aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2010-03-12 16:00:01 +0000
committerPratik Naik <pratiknaik@gmail.com>2010-03-12 16:00:01 +0000
commite68bfaf1fe1a7890a67af6f444281185f507cf9e (patch)
tree5e73caccdcdd65d0ac97f9eb92195928f30925f2
parentef6462c73003b28c8e060a06120abb9cd67b6d52 (diff)
parent16846553b8866eab2aa3b128a2a23a221a25f7e3 (diff)
downloadrails-e68bfaf1fe1a7890a67af6f444281185f507cf9e.tar.gz
rails-e68bfaf1fe1a7890a67af6f444281185f507cf9e.tar.bz2
rails-e68bfaf1fe1a7890a67af6f444281185f507cf9e.zip
Merge remote branch 'mainstream/master'
Conflicts: activerecord/lib/active_record/base.rb railties/lib/rails/configuration.rb railties/lib/rails/log_subscriber.rb
-rw-r--r--.gitignore6
-rw-r--r--Gemfile9
-rw-r--r--RAILS_VERSION1
-rw-r--r--Rakefile48
-rw-r--r--actionmailer/Rakefile11
-rw-r--r--actionmailer/actionmailer.gemspec12
-rw-r--r--actionmailer/lib/action_mailer.rb1
-rw-r--r--actionmailer/lib/action_mailer/base.rb83
-rw-r--r--actionmailer/lib/action_mailer/old_api.rb4
-rw-r--r--actionmailer/lib/action_mailer/quoting.rb2
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb14
-rw-r--r--actionmailer/lib/action_mailer/railties/log_subscriber.rb (renamed from actionmailer/lib/action_mailer/railties/subscriber.rb)2
-rw-r--r--actionmailer/lib/action_mailer/version.rb5
-rw-r--r--actionmailer/test/abstract_unit.rb5
-rw-r--r--actionmailer/test/base_test.rb83
-rw-r--r--actionmailer/test/log_subscriber_test.rb (renamed from actionmailer/test/subscriber_test.rb)14
-rw-r--r--actionmailer/test/old_base/asset_host_test.rb9
-rw-r--r--actionmailer/test/old_base/url_test.rb10
-rw-r--r--actionpack/Rakefile32
-rw-r--r--actionpack/actionpack.gemspec12
-rw-r--r--actionpack/lib/abstract_controller.rb6
-rw-r--r--actionpack/lib/abstract_controller/base.rb18
-rw-r--r--actionpack/lib/abstract_controller/collector.rb2
-rw-r--r--actionpack/lib/abstract_controller/compatibility.rb18
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb8
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb134
-rw-r--r--actionpack/lib/abstract_controller/localized_cache.rb49
-rw-r--r--actionpack/lib/abstract_controller/logger.rb4
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb212
-rw-r--r--actionpack/lib/abstract_controller/view_paths.rb73
-rw-r--r--actionpack/lib/action_controller.rb3
-rw-r--r--actionpack/lib/action_controller/base.rb34
-rw-r--r--actionpack/lib/action_controller/caching.rb37
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb4
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb48
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb4
-rw-r--r--actionpack/lib/action_controller/deprecated.rb2
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb131
-rw-r--r--actionpack/lib/action_controller/metal.rb43
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb98
-rw-r--r--actionpack/lib/action_controller/metal/configuration.rb28
-rw-r--r--actionpack/lib/action_controller/metal/head.rb1
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb4
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb10
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb50
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb21
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb5
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb1
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb68
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb45
-rw-r--r--actionpack/lib/action_controller/metal/session_management.rb36
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb67
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb1
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb167
-rw-r--r--actionpack/lib/action_controller/middleware.rb3
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb3
-rw-r--r--actionpack/lib/action_controller/railtie.rb70
-rw-r--r--actionpack/lib/action_controller/railties/log_subscriber.rb (renamed from actionpack/lib/action_controller/railties/subscriber.rb)12
-rw-r--r--actionpack/lib/action_controller/railties/url_helpers.rb14
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb21
-rw-r--r--actionpack/lib/action_controller/test_case.rb31
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb76
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb21
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb65
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb15
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb39
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb49
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb37
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb51
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb8
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/deprecated_mapper.rb40
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb54
-rw-r--r--actionpack/lib/action_dispatch/routing/route.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb358
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb139
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb23
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb27
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb57
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb7
-rw-r--r--actionpack/lib/action_view.rb15
-rw-r--r--actionpack/lib/action_view/base.rb94
-rw-r--r--actionpack/lib/action_view/helpers.rb1
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb17
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/deprecated_block_helpers.rb52
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb48
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb86
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb28
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb28
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb23
-rw-r--r--actionpack/lib/action_view/lookup_context.rb154
-rw-r--r--actionpack/lib/action_view/paths.rb85
-rw-r--r--actionpack/lib/action_view/railtie.rb8
-rw-r--r--actionpack/lib/action_view/railties/log_subscriber.rb (renamed from actionpack/lib/action_view/railties/subscriber.rb)2
-rw-r--r--actionpack/lib/action_view/render/layouts.rb65
-rw-r--r--actionpack/lib/action_view/render/partials.rb48
-rw-r--r--actionpack/lib/action_view/render/rendering.rb115
-rw-r--r--actionpack/lib/action_view/template.rb32
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb25
-rw-r--r--actionpack/lib/action_view/template/resolver.rb136
-rw-r--r--actionpack/lib/action_view/template/text.rb13
-rw-r--r--actionpack/lib/action_view/test_case.rb9
-rw-r--r--actionpack/test/abstract/abstract_controller_test.rb4
-rw-r--r--actionpack/test/abstract/layouts_test.rb118
-rw-r--r--actionpack/test/abstract/localized_cache_test.rb57
-rw-r--r--actionpack/test/abstract/render_test.rb61
-rw-r--r--actionpack/test/abstract_unit.rb105
-rw-r--r--actionpack/test/activerecord/controller_runtime_test.rb22
-rw-r--r--actionpack/test/activerecord/polymorphic_routes_test.rb8
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb24
-rw-r--r--actionpack/test/controller/assert_select_test.rb13
-rw-r--r--actionpack/test/controller/base_test.rb59
-rw-r--r--actionpack/test/controller/caching_test.rb35
-rw-r--r--actionpack/test/controller/content_type_test.rb12
-rw-r--r--actionpack/test/controller/cookie_test.rb2
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb18
-rw-r--r--actionpack/test/controller/integration_test.rb48
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb (renamed from actionpack/test/controller/subscriber_test.rb)28
-rw-r--r--actionpack/test/controller/mime_responds_test.rb8
-rw-r--r--actionpack/test/controller/new_base/render_rjs_test.rb2
-rw-r--r--actionpack/test/controller/record_identifier_test.rb1
-rw-r--r--actionpack/test/controller/redirect_test.rb18
-rw-r--r--actionpack/test/controller/render_json_test.rb10
-rw-r--r--actionpack/test/controller/render_test.rb24
-rw-r--r--actionpack/test/controller/render_xml_test.rb3
-rw-r--r--actionpack/test/controller/rescue_test.rb4
-rw-r--r--actionpack/test/controller/resources_test.rb27
-rw-r--r--actionpack/test/controller/routing_test.rb38
-rw-r--r--actionpack/test/controller/send_file_test.rb62
-rw-r--r--actionpack/test/controller/test_test.rb16
-rw-r--r--actionpack/test/controller/url_for_test.rb26
-rw-r--r--actionpack/test/controller/url_rewriter_test.rb69
-rw-r--r--actionpack/test/controller/view_paths_test.rb3
-rw-r--r--actionpack/test/controller/webservice_test.rb2
-rw-r--r--actionpack/test/dispatch/mount_test.rb36
-rw-r--r--actionpack/test/dispatch/request_test.rb159
-rw-r--r--actionpack/test/dispatch/response_test.rb24
-rw-r--r--actionpack/test/dispatch/routing_test.rb35
-rw-r--r--actionpack/test/dispatch/url_generation_test.rb38
-rw-r--r--actionpack/test/fixtures/hello.html1
-rw-r--r--actionpack/test/fixtures/test/layout_render_file.erb2
-rw-r--r--actionpack/test/lib/controller/fake_models.rb33
-rw-r--r--actionpack/test/lib/fixture_template.rb17
-rw-r--r--actionpack/test/template/active_model_helper_test.rb4
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb96
-rw-r--r--actionpack/test/template/atom_feed_helper_test.rb8
-rw-r--r--actionpack/test/template/compiled_templates_test.rb5
-rw-r--r--actionpack/test/template/date_helper_test.rb110
-rw-r--r--actionpack/test/template/erb/tag_helper_test.rb82
-rw-r--r--actionpack/test/template/form_helper_test.rb157
-rw-r--r--actionpack/test/template/form_options_helper_test.rb22
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb43
-rw-r--r--actionpack/test/template/javascript_helper_test.rb17
-rw-r--r--actionpack/test/template/log_subscriber_test.rb (renamed from actionpack/test/template/subscriber_test.rb)14
-rw-r--r--actionpack/test/template/lookup_context_test.rb165
-rw-r--r--actionpack/test/template/prototype_helper_test.rb8
-rw-r--r--actionpack/test/template/record_tag_helper_test.rb1
-rw-r--r--actionpack/test/template/render_test.rb23
-rw-r--r--actionpack/test/template/tag_helper_test.rb18
-rw-r--r--actionpack/test/template/test_case_test.rb35
-rw-r--r--actionpack/test/template/translation_helper_test.rb5
-rw-r--r--actionpack/test/template/url_helper_test.rb96
-rwxr-xr-xactivemodel/Rakefile10
-rw-r--r--activemodel/activemodel.gemspec10
-rw-r--r--activemodel/examples/validations.rb2
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb11
-rw-r--r--activemodel/lib/active_model/conversion.rb45
-rw-r--r--activemodel/lib/active_model/lint.rb40
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activemodel/lib/active_model/validations.rb23
-rw-r--r--activemodel/lib/active_model/validations/with.rb9
-rw-r--r--activemodel/lib/active_model/validator.rb18
-rw-r--r--activemodel/lib/active_model/version.rb5
-rw-r--r--activemodel/test/cases/conversion_test.rb25
-rw-r--r--activemodel/test/cases/helper.rb3
-rw-r--r--activemodel/test/cases/lint_test.rb10
-rw-r--r--activemodel/test/cases/validations/presence_validation_test.rb26
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations_test.rb27
-rw-r--r--activemodel/test/models/contact.rb8
-rw-r--r--activerecord/Rakefile15
-rw-r--r--activerecord/activerecord.gemspec12
-rw-r--r--activerecord/lib/active_record.rb13
-rw-r--r--activerecord/lib/active_record/association_preload.rb2
-rwxr-xr-xactiverecord/lib/active_record/associations.rb56
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb16
-rwxr-xr-xactiverecord/lib/active_record/base.rb200
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb11
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/errors.rb165
-rw-r--r--activerecord/lib/active_record/migration.rb4
-rw-r--r--activerecord/lib/active_record/named_scope.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb46
-rw-r--r--activerecord/lib/active_record/railties/databases.rake10
-rw-r--r--activerecord/lib/active_record/railties/log_subscriber.rb (renamed from activerecord/lib/active_record/railties/subscriber.rb)2
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb8
-rw-r--r--activerecord/lib/active_record/version.rb5
-rw-r--r--activerecord/test/cases/ar_schema_test.rb10
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb32
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb20
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb4
-rwxr-xr-xactiverecord/test/cases/base_test.rb28
-rw-r--r--activerecord/test/cases/calculations_test.rb4
-rw-r--r--activerecord/test/cases/helper.rb4
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb (renamed from activerecord/test/cases/subscriber_test.rb)11
-rw-r--r--activerecord/test/cases/method_scoping_test.rb10
-rw-r--r--activerecord/test/cases/pk_test.rb14
-rw-r--r--activerecord/test/models/author.rb9
-rw-r--r--activerecord/test/models/company.rb4
-rw-r--r--activerecord/test/models/developer.rb5
-rw-r--r--activeresource/Rakefile16
-rw-r--r--activeresource/activeresource.gemspec12
-rw-r--r--activeresource/lib/active_resource/railtie.rb4
-rw-r--r--activeresource/lib/active_resource/railties/log_subscriber.rb (renamed from activeresource/lib/active_resource/railties/subscriber.rb)2
-rw-r--r--activeresource/lib/active_resource/version.rb5
-rw-r--r--activeresource/test/abstract_unit.rb4
-rw-r--r--activeresource/test/cases/log_subscriber_test.rb (renamed from activeresource/test/cases/subscriber_test.rb)18
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/Rakefile12
-rw-r--r--activesupport/activesupport.gemspec10
-rw-r--r--activesupport/lib/active_support.rb3
-rw-r--r--activesupport/lib/active_support/all.rb1
-rw-r--r--activesupport/lib/active_support/callbacks.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/file/path.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/metaclass.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/object/singleton_class.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/proc.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/interpolation.rb92
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb8
-rw-r--r--activesupport/lib/active_support/dependencies.rb23
-rw-r--r--activesupport/lib/active_support/dependencies/autoload.rb5
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb18
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb3
-rw-r--r--activesupport/lib/active_support/i18n.rb3
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb2
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb2
-rw-r--r--activesupport/lib/active_support/json/decoding.rb2
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb25
-rw-r--r--activesupport/lib/active_support/memoizable.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb10
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb22
-rw-r--r--activesupport/lib/active_support/ordered_options.rb6
-rw-r--r--activesupport/lib/active_support/railtie.rb12
-rw-r--r--activesupport/lib/active_support/ruby/shim.rb1
-rw-r--r--activesupport/lib/active_support/version.rb5
-rw-r--r--activesupport/lib/active_support/whiny_nil.rb7
-rw-r--r--activesupport/test/abstract_unit.rb3
-rw-r--r--activesupport/test/callback_inheritance_test.rb1
-rw-r--r--activesupport/test/callbacks_test.rb1
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/class/attribute_test.rb35
-rw-r--r--activesupport/test/core_ext/class/delegating_attributes_test.rb2
-rw-r--r--activesupport/test/core_ext/file_test.rb4
-rw-r--r--activesupport/test/core_ext/module_test.rb20
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb11
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb4
-rw-r--r--activesupport/test/inflector_test_cases.rb9
-rw-r--r--activesupport/test/load_paths_test.rb15
-rw-r--r--activesupport/test/notifications_test.rb47
-rw-r--r--activesupport/test/whiny_nil_test.rb2
-rwxr-xr-xci/ci_build.rb2
-rw-r--r--load_paths.rb5
-rwxr-xr-xpushgems.rb14
-rw-r--r--rails.gemspec23
-rw-r--r--rails3b.gemspec5
-rw-r--r--railties/Rakefile14
-rwxr-xr-xrailties/bin/rails37
-rw-r--r--railties/builtin/routes.rb4
-rw-r--r--railties/guides/source/3_0_release_notes.textile2
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/security.textile2
-rw-r--r--railties/lib/generators/erb/scaffold/templates/_form.html.erb2
-rw-r--r--railties/lib/generators/erb/scaffold/templates/layout.html.erb1
-rw-r--r--railties/lib/generators/rails/app/app_generator.rb16
-rw-r--r--railties/lib/generators/rails/app/templates/Gemfile8
-rw-r--r--railties/lib/generators/rails/app/templates/config/application.rb2
-rw-r--r--railties/lib/generators/rails/app/templates/config/boot.rb9
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt2
-rw-r--r--railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt11
-rw-r--r--railties/lib/generators/rails/app/templates/public/index.html34
-rw-r--r--railties/lib/generators/rails/app/templates/script/rails7
-rw-r--r--railties/lib/generators/rails/stylesheets/templates/scaffold.css4
-rw-r--r--railties/lib/generators/test_unit/mailer/templates/functional_test.rb1
-rw-r--r--railties/lib/rails.rb2
-rw-r--r--railties/lib/rails/application.rb11
-rw-r--r--railties/lib/rails/application/bootstrap.rb16
-rw-r--r--railties/lib/rails/application/configuration.rb17
-rw-r--r--railties/lib/rails/application/finisher.rb1
-rw-r--r--railties/lib/rails/application/metal_loader.rb2
-rw-r--r--railties/lib/rails/application/routes_reloader.rb2
-rw-r--r--railties/lib/rails/commands.rb10
-rw-r--r--railties/lib/rails/configuration.rb87
-rw-r--r--railties/lib/rails/console/app.rb7
-rw-r--r--railties/lib/rails/console/helpers.rb4
-rw-r--r--railties/lib/rails/engine.rb23
-rw-r--r--railties/lib/rails/engine/configuration.rb9
-rw-r--r--railties/lib/rails/generators.rb12
-rw-r--r--railties/lib/rails/generators/actions.rb14
-rw-r--r--railties/lib/rails/log_subscriber.rb (renamed from railties/lib/rails/subscriber.rb)63
-rw-r--r--railties/lib/rails/log_subscriber/test_helper.rb (renamed from railties/lib/rails/subscriber/test_helper.rb)31
-rw-r--r--railties/lib/rails/plugin.rb24
-rw-r--r--railties/lib/rails/rack/logger.rb8
-rw-r--r--railties/lib/rails/railtie.rb12
-rw-r--r--railties/lib/rails/railtie/configuration.rb118
-rw-r--r--railties/lib/rails/tasks/documentation.rake72
-rw-r--r--railties/lib/rails/tasks/framework.rake16
-rw-r--r--railties/lib/rails/tasks/routes.rake4
-rw-r--r--railties/lib/rails/test_help.rb12
-rw-r--r--railties/lib/rails/version.rb5
-rw-r--r--railties/railties.gemspec14
-rw-r--r--railties/test/application/configuration_test.rb57
-rw-r--r--railties/test/application/console_test.rb32
-rw-r--r--railties/test/application/initializers/frameworks_test.rb17
-rw-r--r--railties/test/application/initializers/initializers_test.rb26
-rw-r--r--railties/test/application/initializers/notifications_test.rb2
-rw-r--r--railties/test/application/metal_test.rb4
-rw-r--r--railties/test/application/middleware_stack_defaults_test.rb54
-rw-r--r--railties/test/application/middleware_test.rb2
-rw-r--r--railties/test/application/paths_test.rb11
-rw-r--r--railties/test/application/routing_test.rb3
-rw-r--r--railties/test/application/url_generation_test.rb43
-rw-r--r--railties/test/generators/actions_test.rb6
-rw-r--r--railties/test/generators/app_generator_test.rb6
-rw-r--r--railties/test/generators_test.rb15
-rw-r--r--railties/test/isolation/abstract_unit.rb2
-rw-r--r--railties/test/log_subscriber_test.rb123
-rw-r--r--railties/test/rails_info_controller_test.rb7
-rw-r--r--railties/test/railties/plugin_test.rb9
-rw-r--r--railties/test/railties/railtie_test.rb8
-rw-r--r--railties/test/railties/shared_tests.rb20
-rw-r--r--railties/test/subscriber_test.rb119
-rwxr-xr-xrelease.rb25
-rw-r--r--version.rb10
361 files changed, 5293 insertions, 4154 deletions
diff --git a/.gitignore b/.gitignore
index 715d0bc4bc..9d68f4860f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+*.gem
+pkg
+.bundle
debug.log
doc/rdoc
activemodel/doc
@@ -16,6 +19,3 @@ railties/doc/guides/html/images
railties/doc/guides/html/stylesheets
railties/guides/output
railties/tmp
-bin
-.bundle
-pkg
diff --git a/Gemfile b/Gemfile
index 7fbf57ba0b..a51c560232 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,7 @@
path File.dirname(__FILE__)
-source 'http://gemcutter.org'
+source 'http://rubygems.org'
+gem "arel", :git => "git://github.com/rails/arel.git"
gem "rails", "3.0.0.beta1"
gem "rake", ">= 0.8.7"
@@ -14,7 +15,7 @@ end
gem "sqlite3-ruby", ">= 1.2.5", :require => 'sqlite3'
group :test do
- gem "pg", ">= 0.8.0"
+ gem "pg", ">= 0.9.0"
gem "mysql", ">= 2.8.1"
end
@@ -22,6 +23,10 @@ end
gem "rack-test", "0.5.3", :require => 'rack/test'
gem "RedCloth", ">= 4.2.2"
+group :documentation do
+ gem 'rdoc', '2.1'
+end
+
if ENV['CI']
gem "nokogiri", ">= 1.4.0"
diff --git a/RAILS_VERSION b/RAILS_VERSION
new file mode 100644
index 0000000000..efcd59e268
--- /dev/null
+++ b/RAILS_VERSION
@@ -0,0 +1 @@
+3.0.0.beta1
diff --git a/Rakefile b/Rakefile
index d9d24fc1ab..4437b48d9a 100644
--- a/Rakefile
+++ b/Rakefile
@@ -2,23 +2,17 @@ require 'rake'
require 'rake/rdoctask'
require 'rake/gempackagetask'
-env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
-
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
-Dir["#{File.dirname(__FILE__)}/*/lib/*/version.rb"].each do |version_path|
- require version_path
-end
-
desc 'Run all tests by default'
task :default => %w(test test:isolated)
-%w(test test:isolated rdoc pgem package gem gemspec).each do |task_name|
+%w(test test:isolated rdoc package gem).each do |task_name|
desc "Run #{task_name} task for all projects"
task task_name do
errors = []
PROJECTS.each do |project|
- system(%(cd #{project} && #{env} #{$0} #{task_name})) || errors << project
+ system(%(cd #{project} && #{$0} #{task_name})) || errors << project
end
fail("Errors in #{errors.join(', ')}") unless errors.empty?
end
@@ -27,9 +21,9 @@ end
desc "Smoke-test all projects"
task :smoke do
(PROJECTS - %w(activerecord)).each do |project|
- system %(cd #{project} && #{env} #{$0} test:isolated)
+ system %(cd #{project} && #{$0} test:isolated)
end
- system %(cd activerecord && #{env} #{$0} sqlite3:isolated_test)
+ system %(cd activerecord && #{$0} sqlite3:isolated_test)
end
spec = eval(File.read('rails.gemspec'))
@@ -48,12 +42,14 @@ desc "Release all components to gemcutter."
task :release_projects => :package do
errors = []
PROJECTS.each do |project|
- system(%(cd #{project} && #{env} #{$0} release)) || errors << project
+ system(%(cd #{project} && #{$0} release)) || errors << project
end
fail("Errors in #{errors.join(', ')}") unless errors.empty?
end
+desc "Install gems for all projects."
task :install => :gem do
+ require File.expand_path("../actionpack/lib/action_pack/version", __FILE__)
(PROJECTS - ["railties"]).each do |project|
puts "INSTALLING #{project}"
system("gem install #{project}/pkg/#{project}-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc")
@@ -121,6 +117,34 @@ task :pdoc => :rdoc do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc/rdoc").upload
PROJECTS.each do |project|
- system %(cd #{project} && #{env} #{$0} pdoc)
+ system %(cd #{project} && #{$0} pdoc)
+ end
+end
+
+task :update_versions do
+ require File.dirname(__FILE__) + "/version"
+
+ File.open("RAILS_VERSION", "w") do |f|
+ f.write Rails::VERSION::STRING + "\n"
+ end
+
+ constants = {
+ "activesupport" => "ActiveSupport",
+ "activemodel" => "ActiveModel",
+ "actionpack" => "ActionPack",
+ "actionmailer" => "ActionMailer",
+ "activeresource" => "ActiveResource",
+ "activerecord" => "ActiveRecord",
+ "railties" => "Rails"
+ }
+
+ version_file = File.read("version.rb")
+
+ PROJECTS.each do |project|
+ Dir["#{project}/lib/*/version.rb"].each do |file|
+ File.open(file, "w") do |f|
+ f.write version_file.gsub(/Rails/, constants[project])
+ end
+ end
end
end
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index baea591b97..5b72843f5e 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -4,17 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'actionmailer'
-PKG_VERSION = ActionMailer::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "actionmailer"
-RUBY_FORGE_USER = "webster132"
desc "Default Task"
task :default => [ :test ]
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 31d8efc7bf..a7a2599105 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'actionmailer'
- s.version = '3.0.0.beta1'
- s.summary = 'Email composition, delivery, and recieval framework (part of Rails).'
- s.description = 'Email composition, delivery, and recieval framework (part of Rails).'
+ s.version = version
+ s.summary = 'Email composition, delivery, and receiving framework (part of Rails).'
+ s.description = 'Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'David Heinemeier Hansson'
@@ -17,7 +19,7 @@ Gem::Specification.new do |s|
s.has_rdoc = true
- s.add_dependency('actionpack', '= 3.0.0.beta1')
- s.add_dependency('mail', '~> 2.1.2')
+ s.add_dependency('actionpack', version)
+ s.add_dependency('mail', '~> 2.1.3')
s.add_dependency('text-format', '~> 1.0.0')
end
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 7f5bcad922..43d73e71b9 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -34,6 +34,7 @@ require 'active_support/core_ext/array/uniq_by'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections'
+require 'active_support/lazy_load_hooks'
module ActionMailer
extend ::ActiveSupport::Autoload
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 4e89c1ea0c..ef3820ad67 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -181,6 +181,18 @@ module ActionMailer #:nodoc:
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
# with the filename +free_book.pdf+.
#
+ # = Observing and Intercepting Mails
+ #
+ # ActionMailer provides hooks into the Mail observer and interceptor methods. These allow you to
+ # register objects that are called during the mail delivery life cycle.
+ #
+ # An observer object must implement the <tt>:delivered_email(message)</tt> method which will be
+ # called once for every email sent after the email has been sent.
+ #
+ # An interceptor object must implement the <tt>:delivering_email(message)</tt> method which will be
+ # called before the email is sent, allowing you to make modifications to the email before it hits
+ # the delivery agents. Your object should make and needed modifications directly to the passed
+ # in Mail::Message instance.
#
# = Configuration options
#
@@ -255,16 +267,17 @@ module ActionMailer #:nodoc:
include AbstractController::Logger
include AbstractController::Rendering
- include AbstractController::LocalizedCache
include AbstractController::Layouts
include AbstractController::Helpers
include AbstractController::Translation
- include AbstractController::Compatibility
helper ActionMailer::MailHelper
include ActionMailer::OldApi
include ActionMailer::DeprecatedApi
+
+ delegate :register_observer, :to => Mail
+ delegate :register_interceptor, :to => Mail
private_class_method :new #:nodoc:
@@ -276,6 +289,8 @@ module ActionMailer #:nodoc:
:parts_order => [ "text/plain", "text/enriched", "text/html" ]
}.freeze
+ ActionMailer.run_base_hooks(self)
+
class << self
def mailer_name
@@ -452,10 +467,27 @@ module ActionMailer #:nodoc:
# field for the 'envelope from' value.
#
# If you do not pass a block to the +mail+ method, it will find all templates in the
- # template path that match the method name that it is being called from, it will then
- # create parts for each of these templates intelligently, making educated guesses
- # on correct content type and sequence, and return a fully prepared Mail::Message
- # ready to call <tt>:deliver</tt> on to send.
+ # view paths using by default the mailer name and the method name that it is being
+ # called from, it will then create parts for each of these templates intelligently,
+ # making educated guesses on correct content type and sequence, and return a fully
+ # prepared Mail::Message ready to call <tt>:deliver</tt> on to send.
+ #
+ # For example:
+ #
+ # class Notifier < ActionMailer::Base
+ # default :from => 'no-reply@test.lindsaar.net',
+ #
+ # def welcome
+ # mail(:to => 'mikel@test.lindsaar.net')
+ # end
+ # end
+ #
+ # Will look for all templates at "app/views/notifier" with name "welcome". However, those
+ # can be customized:
+ #
+ # 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:
#
@@ -493,7 +525,7 @@ module ActionMailer #:nodoc:
# Merge defaults from class
headers = headers.reverse_merge(self.class.default)
- charset = headers[:charset]
+ charset = headers.delete(:charset)
# Quote fields
headers[:subject] ||= default_i18n_subject
@@ -514,13 +546,11 @@ module ActionMailer #:nodoc:
end
# Set configure delivery behavior
- wrap_delivery_behavior!(headers[:delivery_method])
+ wrap_delivery_behavior!(headers.delete(:delivery_method))
- # Remove headers already treated and assign all others
- headers.except!(:subject, :to, :from, :cc, :bcc, :reply_to)
- headers.except!(:body, :parts_order, :content_type, :charset, :delivery_method)
+ # Remove any missing configuration header and assign all others
+ headers.except!(:parts_order, :content_type)
headers.each { |k, v| m[k] = v }
-
m
end
@@ -548,12 +578,12 @@ module ActionMailer #:nodoc:
# TODO: Move this into Mail
def quote_fields!(headers, charset) #:nodoc:
m = @_message
- m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject]
- m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to]
- m.from ||= quote_address_if_necessary(headers[:from], charset) if headers[:from]
- m.cc ||= quote_address_if_necessary(headers[:cc], charset) if headers[:cc]
- m.bcc ||= quote_address_if_necessary(headers[:bcc], charset) if headers[:bcc]
- m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to]
+ m.subject ||= quote_if_necessary(headers.delete(:subject), charset) if headers[:subject]
+ m.to ||= quote_address_if_necessary(headers.delete(:to), charset) if headers[:to]
+ m.from ||= quote_address_if_necessary(headers.delete(:from), charset) if headers[:from]
+ m.cc ||= quote_address_if_necessary(headers.delete(:cc), charset) if headers[:cc]
+ m.bcc ||= quote_address_if_necessary(headers.delete(:bcc), charset) if headers[:bcc]
+ m.reply_to ||= quote_address_if_necessary(headers.delete(:reply_to), charset) if headers[:reply_to]
end
def collect_responses_and_parts_order(headers) #:nodoc:
@@ -566,13 +596,16 @@ module ActionMailer #:nodoc:
responses = collector.responses
elsif headers[:body]
responses << {
- :body => headers[:body],
+ :body => headers.delete(:body),
:content_type => self.class.default[:content_type] || "text/plain"
}
else
- each_template do |template|
+ templates_path = headers.delete(:template_path) || self.class.mailer_name
+ templates_name = headers.delete(:template_name) || action_name
+
+ each_template(templates_path, templates_name) do |template|
responses << {
- :body => render_to_body(:_template => template),
+ :body => render(:_template => template),
:content_type => template.mime_type.to_s
}
end
@@ -581,10 +614,10 @@ module ActionMailer #:nodoc:
[responses, parts_order]
end
- def each_template(&block) #:nodoc:
- self.class.view_paths.each do |load_paths|
- templates = load_paths.find_all(action_name, {}, self.class.mailer_name)
- templates = templates.uniq_by { |t| t.details[:formats] }
+ def each_template(paths, name, &block) #:nodoc:
+ Array(paths).each do |path|
+ templates = lookup_context.find_all(name, path)
+ templates = templates.uniq_by { |t| t.formats }
unless templates.empty?
templates.each(&block)
diff --git a/actionmailer/lib/action_mailer/old_api.rb b/actionmailer/lib/action_mailer/old_api.rb
index 936ceb0dd6..aeb653c5db 100644
--- a/actionmailer/lib/action_mailer/old_api.rb
+++ b/actionmailer/lib/action_mailer/old_api.rb
@@ -206,8 +206,8 @@ module ActionMailer
if String === @body
@parts.unshift create_inline_part(@body)
elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
- self.class.view_paths.first.find_all(@template, {}, @mailer_name).each do |template|
- @parts << create_inline_part(render_to_body(:_template => template), template.mime_type)
+ lookup_context.find_all(@template, @mailer_name).each do |template|
+ @parts << create_inline_part(render(:_template => template), template.mime_type)
end
if @parts.size > 1
diff --git a/actionmailer/lib/action_mailer/quoting.rb b/actionmailer/lib/action_mailer/quoting.rb
index 70f636bf69..2b55c0f0ab 100644
--- a/actionmailer/lib/action_mailer/quoting.rb
+++ b/actionmailer/lib/action_mailer/quoting.rb
@@ -22,7 +22,7 @@ module ActionMailer
# A quick-and-dirty regexp for determining whether a string contains any
# characters that need escaping.
if !defined?(CHARS_NEEDING_QUOTING)
- CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
+ CHARS_NEEDING_QUOTING = Regexp.new('[\000-\011\013\014\016-\037\177-\377]', nil, 'n')
end
# Quote the given text if it contains any "illegal" characters
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index a3afc23e6a..0182e48425 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -6,19 +6,21 @@ module ActionMailer
railtie_name :action_mailer
initializer "action_mailer.url_for", :before => :load_environment_config do |app|
- ActionMailer::Base.send(:include, ActionController::UrlFor) if defined?(ActionController)
+ ActionMailer.base_hook { include app.routes.url_helpers }
end
- require "action_mailer/railties/subscriber"
- subscriber ActionMailer::Railties::Subscriber.new
+ require "action_mailer/railties/log_subscriber"
+ log_subscriber ActionMailer::Railties::LogSubscriber.new
initializer "action_mailer.logger" do
- ActionMailer::Base.logger ||= Rails.logger
+ ActionMailer.base_hook { self.logger ||= Rails.logger }
end
initializer "action_mailer.set_configs" do |app|
- app.config.action_mailer.each do |k,v|
- ActionMailer::Base.send "#{k}=", v
+ ActionMailer.base_hook do
+ app.config.action_mailer.each do |k,v|
+ send "#{k}=", v
+ end
end
end
end
diff --git a/actionmailer/lib/action_mailer/railties/subscriber.rb b/actionmailer/lib/action_mailer/railties/log_subscriber.rb
index cff852055c..d1b3dd33af 100644
--- a/actionmailer/lib/action_mailer/railties/subscriber.rb
+++ b/actionmailer/lib/action_mailer/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActionMailer
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
def deliver(event)
recipients = Array(event.payload[:to]).join(', ')
info("\nSent mail to #{recipients} (%1.fms)" % event.duration)
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 56af531d01..7328a7dc80 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -2,8 +2,9 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index f6baa4a9e8..16fef3a9a4 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -1,5 +1,8 @@
require File.expand_path('../../../load_paths', __FILE__)
+lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
require 'test/unit'
require 'action_mailer'
require 'action_mailer/test_case'
@@ -14,7 +17,7 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b
FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__))
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
-class MockSMTP
+class MockSMTP
def self.deliveries
@@deliveries
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 222db66aaa..c1cf1f0157 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -14,8 +14,13 @@ class BaseTest < ActiveSupport::TestCase
mail({:subject => "The first email on new API!"}.merge!(hash))
end
- def simple(hash = {})
- mail(hash)
+ def welcome_with_headers(hash = {})
+ headers hash
+ mail
+ end
+
+ def welcome_from_another_path(path)
+ mail(:template_name => "welcome", :template_path => path)
end
def html_only(hash = {})
@@ -25,11 +30,6 @@ class BaseTest < ActiveSupport::TestCase
def plain_text_only(hash = {})
mail(hash)
end
-
- def simple_with_headers(hash = {})
- headers hash
- mail
- end
def attachment_with_content(hash = {})
attachments['invoice.pdf'] = 'This is test File content'
@@ -78,8 +78,12 @@ class BaseTest < ActiveSupport::TestCase
format.html{ render "welcome" } if include_html
end
end
-
- def different_template(template_name='')
+
+ def implicit_different_template(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}" }
@@ -88,13 +92,10 @@ class BaseTest < ActiveSupport::TestCase
def different_layout(layout_name='')
mail do |format|
- format.text {
- render :layout => layout_name
- }
+ format.text { render :layout => layout_name }
format.html { render :layout => layout_name }
end
end
-
end
test "method call to mail does not raise error" do
@@ -154,7 +155,7 @@ class BaseTest < ActiveSupport::TestCase
test "can pass random headers in as a hash to mail" do
hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
'In-Reply-To' => '1234@mikel.me.com' }
- mail = BaseMailer.simple(hash)
+ mail = BaseMailer.welcome(hash)
assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
end
@@ -162,7 +163,7 @@ class BaseTest < ActiveSupport::TestCase
test "can pass random headers in as a hash" do
hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
'In-Reply-To' => '1234@mikel.me.com' }
- mail = BaseMailer.simple_with_headers(hash)
+ mail = BaseMailer.welcome_with_headers(hash)
assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
end
@@ -247,9 +248,9 @@ class BaseTest < ActiveSupport::TestCase
end
test "uses random default headers from class" do
- with_default BaseMailer, "X-SPAM" => "Not spam" do
- email = BaseMailer.simple
- assert_equal("Not spam", email["X-SPAM"].decoded)
+ with_default BaseMailer, "X-Custom" => "Custom" do
+ email = BaseMailer.welcome
+ assert_equal("Custom", email["X-Custom"].decoded)
end
end
@@ -476,18 +477,58 @@ class BaseTest < ActiveSupport::TestCase
end
# Rendering
- test "that you can specify a different template" do
- mail = BaseMailer.different_template('explicit_multipart_templates')
+ test "you can specify a different template for implicit render" do
+ mail = BaseMailer.implicit_different_template('implicit_multipart')
+ assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
+ assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
+ end
+
+ test "you can specify a different template for explicit render" do
+ mail = BaseMailer.explicit_different_template('explicit_multipart_templates')
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
end
- test "that you can specify a different layout" do
+ test "you can specify a different layout" do
mail = BaseMailer.different_layout('different_layout')
assert_equal("HTML -- HTML", mail.html_part.body.decoded)
assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
end
+ test "you can specify the template path for implicit lookup" do
+ mail = BaseMailer.welcome_from_another_path('another.path/base_mailer')
+ assert_equal("Welcome from another path", mail.body.encoded)
+
+ mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer'])
+ assert_equal("Welcome from another path", mail.body.encoded)
+ end
+
+ # Before and After hooks
+
+ class MyObserver
+ def self.delivered_email(mail)
+ end
+ end
+
+ test "you can register an observer to the mail object that gets informed on email delivery" do
+ ActionMailer::Base.register_observer(MyObserver)
+ mail = BaseMailer.welcome
+ MyObserver.expects(:delivered_email).with(mail)
+ mail.deliver
+ end
+
+ class MyInterceptor
+ def self.delivering_email(mail)
+ end
+ end
+
+ test "you can register an interceptor to the mail object that gets passed the mail object before delivery" do
+ ActionMailer::Base.register_interceptor(MyInterceptor)
+ mail = BaseMailer.welcome
+ MyInterceptor.expects(:delivering_email).with(mail)
+ mail.deliver
+ end
+
protected
# Execute the block setting the given values and restoring old values after
diff --git a/actionmailer/test/subscriber_test.rb b/actionmailer/test/log_subscriber_test.rb
index 3d1736d64f..c08c34b4a2 100644
--- a/actionmailer/test/subscriber_test.rb
+++ b/actionmailer/test/log_subscriber_test.rb
@@ -1,10 +1,14 @@
require "abstract_unit"
-require "rails/subscriber/test_helper"
-require "action_mailer/railties/subscriber"
+require "rails/log_subscriber/test_helper"
+require "action_mailer/railties/log_subscriber"
-class AMSubscriberTest < ActionMailer::TestCase
- include Rails::Subscriber::TestHelper
- Rails::Subscriber.add(:action_mailer, ActionMailer::Railties::Subscriber.new)
+class AMLogSubscriberTest < ActionMailer::TestCase
+ include Rails::LogSubscriber::TestHelper
+
+ def setup
+ super
+ Rails::LogSubscriber.add(:action_mailer, ActionMailer::Railties::LogSubscriber.new)
+ end
class TestMailer < ActionMailer::Base
def basic
diff --git a/actionmailer/test/old_base/asset_host_test.rb b/actionmailer/test/old_base/asset_host_test.rb
index aa9cfd88dd..0ec0242f55 100644
--- a/actionmailer/test/old_base/asset_host_test.rb
+++ b/actionmailer/test/old_base/asset_host_test.rb
@@ -14,6 +14,10 @@ class AssetHostTest < Test::Unit::TestCase
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries.clear
+ AssetHostMailer.configure do |c|
+ c.asset_host = "http://www.example.com"
+ c.assets_dir = ''
+ end
end
def teardown
@@ -21,13 +25,12 @@ class AssetHostTest < Test::Unit::TestCase
end
def test_asset_host_as_string
- ActionController::Base.asset_host = "http://www.example.com"
mail = AssetHostMailer.email_with_asset
assert_equal "<img alt=\"Somelogo\" src=\"http://www.example.com/images/somelogo.png\" />", mail.body.to_s.strip
end
def test_asset_host_as_one_arguement_proc
- ActionController::Base.asset_host = Proc.new { |source|
+ AssetHostMailer.config.asset_host = Proc.new { |source|
if source.starts_with?('/images')
"http://images.example.com"
else
@@ -39,7 +42,7 @@ class AssetHostTest < Test::Unit::TestCase
end
def test_asset_host_as_two_arguement_proc
- ActionController::Base.asset_host = Proc.new {|source,request|
+ ActionController::Base.config.asset_host = Proc.new {|source,request|
if request && request.ssl?
"https://www.example.com"
else
diff --git a/actionmailer/test/old_base/url_test.rb b/actionmailer/test/old_base/url_test.rb
index d851431c7a..60740d6b0b 100644
--- a/actionmailer/test/old_base/url_test.rb
+++ b/actionmailer/test/old_base/url_test.rb
@@ -4,13 +4,19 @@ require 'action_controller'
class WelcomeController < ActionController::Base
end
+AppRoutes = ActionDispatch::Routing::RouteSet.new
+
class ActionMailer::Base
- include ActionController::UrlFor
+ include AppRoutes.url_helpers
end
class TestMailer < ActionMailer::Base
default_url_options[:host] = 'www.basecamphq.com'
+ configure do |c|
+ c.assets_dir = '' # To get the tests to pass
+ end
+
def signed_up_with_url(recipient)
@recipients = recipient
@subject = "[Signed up] Welcome #{recipient}"
@@ -61,7 +67,7 @@ class ActionMailerUrlTest < Test::Unit::TestCase
def test_signed_up_with_url
TestMailer.delivery_method = :test
- ActionController::Routing::Routes.draw do |map|
+ AppRoutes.draw do |map|
map.connect ':controller/:action/:id'
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
end
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index c45f88ed04..b9ace8658a 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -4,17 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'actionpack'
-PKG_VERSION = ActionPack::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "actionpack"
-RUBY_FORGE_USER = "webster132"
desc "Default Task"
task :default => :test
@@ -115,26 +104,7 @@ task :update_js => [ :update_scriptaculous ]
# Publishing ------------------------------------------------------
desc "Publish the API documentation"
-task :pgem => [:package] do
- require 'rake/contrib/sshpublisher'
- Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
- `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
-end
-
-desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
-end
-
-desc "Publish the release files to RubyForge."
-task :release => [ :package ] do
- require 'rubyforge'
- require 'rake/contrib/rubyforgepublisher'
-
- packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
-
- rubyforge = RubyForge.new
- rubyforge.login
- rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
-end
+end \ No newline at end of file
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 8a7169bfa1..ed5b7e1e93 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'actionpack'
- s.version = '3.0.0.beta1'
+ s.version = version
s.summary = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).'
- s.description = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).'
+ s.description = 'Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'David Heinemeier Hansson'
@@ -17,10 +19,10 @@ Gem::Specification.new do |s|
s.has_rdoc = true
- s.add_dependency('activesupport', '= 3.0.0.beta1')
- s.add_dependency('activemodel', '= 3.0.0.beta1')
+ s.add_dependency('activesupport', version)
+ s.add_dependency('activemodel', version)
s.add_dependency('rack', '~> 1.1.0')
s.add_dependency('rack-test', '~> 0.5.0')
- s.add_dependency('rack-mount', '~> 0.4.7')
+ s.add_dependency('rack-mount', '~> 0.6.0')
s.add_dependency('erubis', '~> 2.6.5')
end
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 1e15ab090c..2da4dc052c 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -3,8 +3,11 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.inc
require 'active_support/ruby/shim'
require 'active_support/dependencies/autoload'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/anonymous'
+require 'active_support/i18n'
module AbstractController
extend ActiveSupport::Autoload
@@ -12,11 +15,10 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
- autoload :Compatibility
autoload :Helpers
autoload :Layouts
- autoload :LocalizedCache
autoload :Logger
autoload :Rendering
autoload :Translation
+ autoload :ViewPaths
end
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 3119ee498b..c12b584144 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -1,3 +1,5 @@
+require 'active_support/ordered_options'
+
module AbstractController
class Error < StandardError; end
class ActionNotFound < StandardError; end
@@ -5,7 +7,6 @@ module AbstractController
class Base
attr_internal :response_body
attr_internal :action_name
- attr_internal :formats
class << self
attr_reader :abstract
@@ -28,6 +29,14 @@ module AbstractController
@descendants ||= []
end
+ def config
+ @config ||= ActiveSupport::InheritableOptions.new(superclass < Base ? superclass.config : {})
+ end
+
+ def configure
+ yield config
+ end
+
# A list of all internal methods for a controller. This finds the first
# abstract superclass of a controller, and gets a list of all public
# instance methods on that abstract class. Public instance methods of
@@ -84,15 +93,14 @@ module AbstractController
# ==== Returns
# String
def controller_path
- @controller_path ||= name && name.sub(/Controller$/, '').underscore
+ @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
end
end
abstract!
- # Initialize controller with nil formats.
- def initialize #:nodoc:
- @_formats = nil
+ def config
+ @config ||= ActiveSupport::InheritableOptions.new(self.class.config)
end
# Calls the action going through the entire action dispatch stack.
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index d429333661..81fb514770 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -1,3 +1,5 @@
+require "action_dispatch/http/mime_type"
+
module AbstractController
module Collector
def self.generate_method_for_mime(mime)
diff --git a/actionpack/lib/abstract_controller/compatibility.rb b/actionpack/lib/abstract_controller/compatibility.rb
deleted file mode 100644
index 7fb93a0eb5..0000000000
--- a/actionpack/lib/abstract_controller/compatibility.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module AbstractController
- module Compatibility
- extend ActiveSupport::Concern
-
- def _find_layout(name, details)
- details[:prefix] = nil if name =~ /\blayouts/
- super
- end
-
- # Move this into a "don't run in production" module
- def _default_layout(details, require_layout = false)
- super
- rescue ActionView::MissingTemplate
- _find_layout(_layout({}), {})
- nil
- end
- end
-end
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 578b884a4d..f875213afb 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -1,6 +1,4 @@
require 'active_support/dependencies'
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/delegation'
module AbstractController
module Helpers
@@ -27,7 +25,7 @@ module AbstractController
def inherited(klass)
helpers = _helpers
klass._helpers = Module.new { include helpers }
- klass.class_eval { default_helper_module! unless name.blank? }
+ klass.class_eval { default_helper_module! unless anonymous? }
super
end
@@ -99,7 +97,7 @@ module AbstractController
def helper(*args, &block)
self._helper_serial = AbstractController::Helpers.next_serial + 1
- _modules_for_helpers(args).each do |mod|
+ modules_for_helpers(args).each do |mod|
add_template_helper(mod)
end
@@ -134,7 +132,7 @@ module AbstractController
# ==== Returns
# Array[Module]:: A normalized list of modules for the list of
# helpers provided.
- def _modules_for_helpers(args)
+ def modules_for_helpers(args)
args.flatten.map! do |arg|
case arg
when String, Symbol
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 0d214396aa..2f9616124a 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -1,6 +1,3 @@
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/delegation'
-
module AbstractController
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
# repeated setups. The inclusion pattern has pages that look like this:
@@ -173,27 +170,7 @@ module AbstractController
module ClassMethods
def inherited(klass)
super
- klass.class_eval do
- _write_layout_method
- @found_layouts = {}
- end
- end
-
- def clear_template_caches!
- @found_layouts.clear if defined? @found_layouts
- super
- end
-
- def cache_layout(details)
- layout = @found_layouts
- key = Thread.current[:format_locale_key]
-
- # Cache nil
- if layout.key?(key)
- return layout[key]
- else
- layout[key] = yield
- end
+ klass._write_layout_method
end
# This module is mixed in if layout conditions are provided. This means
@@ -262,10 +239,10 @@ module AbstractController
def _write_layout_method
case defined?(@_layout) ? @_layout : nil
when String
- self.class_eval %{def _layout(details) #{@_layout.inspect} end}
+ self.class_eval %{def _layout; #{@_layout.inspect} end}
when Symbol
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def _layout(details)
+ def _layout
#{@_layout}.tap do |layout|
unless layout.is_a?(String) || !layout
raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
@@ -276,21 +253,21 @@ module AbstractController
ruby_eval
when Proc
define_method :_layout_from_proc, &@_layout
- self.class_eval %{def _layout(details) _layout_from_proc(self) end}
+ self.class_eval %{def _layout; _layout_from_proc(self) end}
when false
- self.class_eval %{def _layout(details) end}
+ self.class_eval %{def _layout; end}
when true
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
when nil
if name
+ _prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout(details)
- self.class.cache_layout(details) do
- if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts")
- "#{_implied_layout_name}"
- else
- super
- end
+ def _layout
+ if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
+ "#{_implied_layout_name}"
+ else
+ super
end
end
RUBY
@@ -300,42 +277,20 @@ module AbstractController
end
end
- def render_to_body(options = {})
- # In the case of a partial with a layout, handle the layout
- # here, and make sure the view does not try to handle it
- layout = options.delete(:layout) if options.key?(:partial)
-
- response = super
+ def _normalize_options(options)
+ super
- # This is a little bit messy. We need to explicitly handle partial
- # layouts here since the core lookup logic is in the view, but
- # we need to determine the layout based on the controller
- #
- # TODO: An easier way to handle this would probably be to override
- # render_template
- if layout
- layout = _layout_for_option(layout, options[:_template].details)
- response = layout.render(view_context, options[:locals] || {}) { response }
+ if _include_layout?(options)
+ layout = options.key?(:layout) ? options.delete(:layout) : :default
+ value = _layout_for_option(layout)
+ options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
end
-
- response
end
private
# This will be overwritten by _write_layout_method
- def _layout(details) end
-
- # Determine the layout for a given name and details.
- #
- # ==== Parameters
- # name<String>:: The name of the template
- # details<Hash{Symbol => Object}>:: A list of details to restrict
- # the lookup to. By default, layout lookup is limited to the
- # formats specified for the current request.
- def _layout_for_name(name, details)
- name && _find_layout(name, details)
- end
+ def _layout; end
# Determine the layout for a given name and details, taking into account
# the name type.
@@ -345,11 +300,11 @@ module AbstractController
# details<Hash{Symbol => Object}>:: A list of details to restrict
# the lookup to. By default, layout lookup is limited to the
# formats specified for the current request.
- def _layout_for_option(name, details)
+ def _layout_for_option(name)
case name
- when String then _layout_for_name(name, details)
- when true then _default_layout(details, true)
- when :default then _default_layout(details, false)
+ when String then name
+ when true then _default_layout(true)
+ when :default then _default_layout(false)
when false, nil then nil
else
raise ArgumentError,
@@ -357,29 +312,6 @@ module AbstractController
end
end
- def _determine_template(options)
- super
-
- return unless (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
- layout = options.key?(:layout) ? options[:layout] : :default
- options[:_layout] = _layout_for_option(layout, options[:_template].details)
- end
-
- # Take in the name and details and find a Template.
- #
- # ==== Parameters
- # name<String>:: The name of the template to retrieve
- # details<Hash>:: A list of details to restrict the search by. This
- # might include details like the format or locale of the template.
- #
- # ==== Returns
- # Template:: A template object matching the name and details
- def _find_layout(name, details)
- # TODO: Make prefix actually part of details in ViewPath#find_by_parts
- prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
- find_template(name, details, :_prefix => prefix)
- end
-
# Returns the default layout for this controller and a given set of details.
# Optionally raises an exception if the layout could not be found.
#
@@ -392,18 +324,24 @@ module AbstractController
#
# ==== Returns
# Template:: The template object for the default layout (or nil)
- def _default_layout(details, require_layout = false)
- if require_layout && _action_has_layout? && !_layout(details)
- raise ArgumentError,
- "There was no default layout for #{self.class} in #{view_paths.inspect}"
- end
-
+ def _default_layout(require_layout = false)
begin
- _layout_for_name(_layout(details), details) if _action_has_layout?
+ layout_name = _layout if _action_has_layout?
rescue NameError => e
raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
+
+ if require_layout && _action_has_layout? && !layout_name
+ raise ArgumentError,
+ "There was no default layout for #{self.class} in #{view_paths.inspect}"
+ end
+
+ layout_name
+ end
+
+ def _include_layout?(options)
+ (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
end
def _action_has_layout?
diff --git a/actionpack/lib/abstract_controller/localized_cache.rb b/actionpack/lib/abstract_controller/localized_cache.rb
deleted file mode 100644
index 5e3efa002c..0000000000
--- a/actionpack/lib/abstract_controller/localized_cache.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-module AbstractController
- class HashKey
- @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|sh,sk| sh[sk] = {} } }
-
- def self.get(klass, formats, locale)
- @hash_keys[klass][formats][locale] ||= new(klass, formats, locale)
- end
-
- attr_accessor :hash
- def initialize(klass, formats, locale)
- @formats, @locale = formats, locale
- @hash = [formats, locale].hash
- end
-
- alias_method :eql?, :equal?
-
- def inspect
- "#<HashKey -- formats: #{@formats.inspect} locale: #{@locale.inspect}>"
- end
- end
-
- module LocalizedCache
- extend ActiveSupport::Concern
-
- module ClassMethods
- def clear_template_caches!
- ActionView::Partials::PartialRenderer::TEMPLATES.clear
- template_cache.clear
- super
- end
-
- def template_cache
- @template_cache ||= Hash.new {|h,k| h[k] = {} }
- end
- end
-
- def render(*args)
- Thread.current[:format_locale_key] = HashKey.get(self.class, formats, I18n.locale)
- super
- end
-
- private
-
- def with_template_cache(name)
- self.class.template_cache[Thread.current[:format_locale_key]][name] ||= super
- end
-
- end
-end
diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb
index a23a13e1d6..9318f5e369 100644
--- a/actionpack/lib/abstract_controller/logger.rb
+++ b/actionpack/lib/abstract_controller/logger.rb
@@ -1,5 +1,5 @@
-require 'active_support/core_ext/logger'
-require 'active_support/benchmarkable'
+require "active_support/core_ext/logger"
+require "active_support/benchmarkable"
module AbstractController
module Logger
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 619a49571b..42f4939108 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,7 +1,4 @@
require "abstract_controller/base"
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/delegation'
-require 'active_support/core_ext/array/wrap'
module AbstractController
class DoubleRenderError < Error
@@ -12,32 +9,47 @@ module AbstractController
end
end
+ # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
+ # it will trigger the lookup_context and consequently expire the cache.
+ # TODO Add some deprecation warnings to remove I18n.locale from controllers
+ class I18nProxy < ::I18n::Config #:nodoc:
+ attr_reader :i18n_config, :lookup_context
+
+ def initialize(i18n_config, lookup_context)
+ @i18n_config, @lookup_context = i18n_config, lookup_context
+ end
+
+ def locale
+ @i18n_config.locale
+ end
+
+ def locale=(value)
+ @i18n_config.locale = value
+ @lookup_context.update_details(:locale => @i18n_config.locale)
+ end
+ end
+
module Rendering
extend ActiveSupport::Concern
+ include AbstractController::ViewPaths
- included do
- class_attribute :_view_paths
- delegate :_view_paths, :to => :'self.class'
- self._view_paths = ActionView::PathSet.new
+ # Overwrite process to setup I18n proxy.
+ def process(*) #:nodoc:
+ old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
+ super
+ ensure
+ I18n.config = old_config
end
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
- # View.for_controller[controller] Create a new ActionView instance for a
- # controller
- # View#render_partial[options]
- # - responsible for setting options[:_template]
- # - Returns String with the rendered partial
- # options<Hash>:: see _render_partial in ActionView::Base
- # View#render_template[template, layout, options, partial]
- # - Returns String with the rendered template
- # template<ActionView::Template>:: The template to render
- # layout<ActionView::Template>:: The layout to render around the template
- # options<Hash>:: See _render_template_with_layout in ActionView::Base
- # partial<Boolean>:: Whether or not the template to render is a partial
+ # View.for_controller[controller]
+ # Create a new ActionView instance for a controller
+ # View#render_template[options]
+ # Returns String with the rendered template
#
- # Override this method in a to change the default behavior.
+ # Override this method in a module to change the default behavior.
def view_context
@_view_context ||= ActionView::Base.for_controller(self)
end
@@ -45,57 +57,29 @@ module AbstractController
# Mostly abstracts the fact that calling render twice is a DoubleRenderError.
# Delegates render_to_body and sticks the result in self.response_body.
def render(*args, &block)
- options = _normalize_options(*args, &block)
+ options = _normalize_args(*args, &block)
+ _normalize_options(options)
self.response_body = render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
- #
- # ==== Options
- # _partial_object<Object>:: The object that is being rendered. If this
- # exists, we are in the special case of rendering an object as a partial.
- #
# :api: plugin
def render_to_body(options = {})
- # TODO: Refactor so we can just use the normal template logic for this
- if options.key?(:partial)
- _render_partial(options)
- else
- _determine_template(options)
- _render_template(options)
- end
+ _process_options(options)
+ _render_template(options)
end
# Raw rendering of a template to a string. Just convert the results of
# render_to_body into a String.
- #
# :api: plugin
- def render_to_string(*args)
- options = _normalize_options(*args)
+ def render_to_string(options={})
+ _normalize_options(options)
AbstractController::Rendering.body_to_s(render_to_body(options))
end
- # Renders the template from an object.
- #
- # ==== Options
- # _template<ActionView::Template>:: The template to render
- # _layout<ActionView::Template>:: The layout to wrap the template in (optional)
+ # Find and renders a template based on the options given.
def _render_template(options)
- view_context.render_template(options)
- end
-
- # Renders the given partial.
- #
- # ==== Options
- # partial<String|Object>:: The partial name or the object to be rendered
- def _render_partial(options)
- view_context.render_partial(options)
- end
-
- # The list of view paths for this controller. See ActionView::ViewPathSet for
- # more details about writing custom view paths.
- def view_paths
- _view_paths
+ view_context.render_template(options) { |template| _with_template_hook(template) }
end
# The prefix used in render "foo" shortcuts.
@@ -117,122 +101,42 @@ module AbstractController
private
- # Normalize options, by converting render "foo" to render :template => "prefix/foo"
- # and render "/foo" to render :file => "/foo".
- def _normalize_options(action=nil, options={})
+ # Normalize options by converting render "foo" to render :action => "foo" and
+ # render "foo/bar" to render :file => "foo/bar".
+ def _normalize_args(action=nil, options={})
case action
+ when NilClass
when Hash
options, action = action, nil
when String, Symbol
action = action.to_s
- case action.index("/")
- when NilClass
- options[:_prefix] = _prefix
- options[:_template_name] = action
- when 0
- options[:file] = action
- else
- options[:template] = action
- end
+ key = action.include?(?/) ? :file : :action
+ options[key] = action
+ else
+ options.merge!(:partial => action)
end
options
end
- # Take in a set of options and determine the template to render
- #
- # ==== Options
- # _template<ActionView::Template>:: If this is provided, the search is over
- # _template_name<#to_s>:: The name of the template to look up. Otherwise,
- # use the current action name.
- # _prefix<String>:: The prefix to look inside of. In a file system, this corresponds
- # to a directory.
- # _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial
- def _determine_template(options)
- if options.key?(:text)
- options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text)
- elsif options.key?(:inline)
- handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
- template = ActionView::Template.new(options[:inline], "inline template", handler, {})
- options[:_template] = template
- elsif options.key?(:template)
- options[:_template_name] = options[:template]
- elsif options.key?(:file)
- options[:_template_name] = options[:file]
+ def _normalize_options(options)
+ if options[:partial] == true
+ options[:partial] = action_name
end
- name = (options[:_template_name] || options[:action] || action_name).to_s
- options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty?
-
- details = _normalize_details(options)
- options[:_template] ||= with_template_cache(name) do
- find_template(name, details, options)
+ if (options.keys & [:partial, :file, :template]).empty?
+ options[:prefix] ||= _prefix
end
- end
-
- def _normalize_details(options)
- details = { :formats => formats }
- details[:formats] = Array(options[:format]) if options[:format]
- details[:locale] = Array(options[:locale]) if options[:locale]
- details
- end
- def find_template(name, details, options)
- view_paths.find(name, details, options[:_prefix], options[:_partial])
- end
-
- def template_exists?(name, details, options)
- view_paths.exists?(name, details, options[:_prefix], options[:_partial])
- end
-
- def with_template_cache(name)
- yield
+ options[:template] ||= (options[:action] || action_name).to_s
+ options
end
- def format_for_text
- Mime[:text]
+ def _process_options(options)
end
- module ClassMethods
- def clear_template_caches!
- end
-
- # Append a path to the list of view paths for this controller.
- #
- # ==== Parameters
- # path<String, ViewPath>:: If a String is provided, it gets converted into
- # the default view path. You may also provide a custom view path
- # (see ActionView::ViewPathSet for more information)
- def append_view_path(path)
- self.view_paths = view_paths.dup + Array.wrap(path)
- end
-
- # Prepend a path to the list of view paths for this controller.
- #
- # ==== Parameters
- # path<String, ViewPath>:: If a String is provided, it gets converted into
- # the default view path. You may also provide a custom view path
- # (see ActionView::ViewPathSet for more information)
- def prepend_view_path(path)
- clear_template_caches!
- self.view_paths = Array.wrap(path) + view_paths.dup
- end
-
- # A list of all of the default view paths for this controller.
- def view_paths
- _view_paths
- end
-
- # Set the view paths.
- #
- # ==== Parameters
- # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
- # otherwise, process the parameter into a ViewPathSet.
- def view_paths=(paths)
- clear_template_caches!
- self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
- _view_paths.freeze
- end
+ def _with_template_hook(template)
+ self.formats = template.formats
end
end
end
diff --git a/actionpack/lib/abstract_controller/view_paths.rb b/actionpack/lib/abstract_controller/view_paths.rb
new file mode 100644
index 0000000000..c2a9f6336d
--- /dev/null
+++ b/actionpack/lib/abstract_controller/view_paths.rb
@@ -0,0 +1,73 @@
+module AbstractController
+ module ViewPaths
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :_view_paths
+ self._view_paths = ActionView::PathSet.new
+ end
+
+ delegate :template_exists?, :view_paths, :formats, :formats=,
+ :locale, :locale=, :to => :lookup_context
+
+ # LookupContext is the object responsible to hold all information required to lookup
+ # templates, i.e. view paths and details. Check ActionView::LookupContext for more
+ # information.
+ def lookup_context
+ @lookup_context ||= ActionView::LookupContext.new(self.class._view_paths, details_for_lookup)
+ end
+
+ def details_for_lookup
+ { }
+ end
+
+ def append_view_path(path)
+ lookup_context.view_paths.push(*path)
+ end
+
+ def prepend_view_path(path)
+ lookup_context.view_paths.unshift(*path)
+ end
+
+ def template_exists?(*args)
+ lookup_context.exists?(*args)
+ end
+
+ module ClassMethods
+ # Append a path to the list of view paths for this controller.
+ #
+ # ==== Parameters
+ # path<String, ViewPath>:: If a String is provided, it gets converted into
+ # the default view path. You may also provide a custom view path
+ # (see ActionView::ViewPathSet for more information)
+ def append_view_path(path)
+ self.view_paths = view_paths.dup + Array(path)
+ end
+
+ # Prepend a path to the list of view paths for this controller.
+ #
+ # ==== Parameters
+ # path<String, ViewPath>:: If a String is provided, it gets converted into
+ # the default view path. You may also provide a custom view path
+ # (see ActionView::ViewPathSet for more information)
+ def prepend_view_path(path)
+ self.view_paths = Array(path) + view_paths.dup
+ end
+
+ # A list of all of the default view paths for this controller.
+ def view_paths
+ _view_paths
+ end
+
+ # Set the view paths.
+ #
+ # ==== Parameters
+ # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
+ # otherwise, process the parameter into a ViewPathSet.
+ def view_paths=(paths)
+ self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
+ self._view_paths.freeze
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 759e52b135..536154fc6b 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -13,13 +13,13 @@ module ActionController
autoload_under "metal" do
autoload :Compatibility
autoload :ConditionalGet
- autoload :Configuration
autoload :Cookies
autoload :Flash
autoload :Head
autoload :Helpers
autoload :HideActions
autoload :HttpAuthentication
+ autoload :ImplicitRender
autoload :Instrumentation
autoload :MimeResponds
autoload :RackDelegation
@@ -46,7 +46,6 @@ module ActionController
eager_autoload do
autoload :RecordIdentifier
- autoload :UrlRewriter
# TODO: Don't autoload exceptions, setup explicit
# requires for files that need them
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 10244f8216..fcd3cb9bd3 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -15,7 +15,6 @@ module ActionController
include ActionController::Renderers::All
include ActionController::ConditionalGet
include ActionController::RackDelegation
- include ActionController::Configuration
# Legacy modules
include SessionManagement
@@ -30,35 +29,13 @@ module ActionController
include ActionController::Verification
include ActionController::RequestForgeryProtection
include ActionController::Streaming
+ include ActionController::RecordIdentifier
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Digest::ControllerMethods
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
include ActionController::Instrumentation
-
- # TODO: Extract into its own module
- # This should be moved together with other normalizing behavior
- module ImplicitRender
- def send_action(*)
- ret = super
- default_render unless response_body
- ret
- end
-
- def default_render
- render
- end
-
- def method_for_action(action_name)
- super || begin
- if template_exists?(action_name.to_s, {:formats => formats}, :_prefix => controller_path)
- "default_render"
- end
- end
- end
- end
-
include ImplicitRender
include ActionController::Rescue
@@ -82,12 +59,9 @@ module ActionController
filter
end
- protected
+ ActionController.run_base_hooks(self)
- # Overwrite url rewriter to use request.
- def _url_rewriter
- return ActionController::UrlRewriter unless request
- @_url_rewriter ||= ActionController::UrlRewriter.new(request, params)
- end
end
end
+
+require "action_controller/deprecated/base"
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index d784138ebe..b3fa129929 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -40,28 +40,34 @@ module ActionController #:nodoc:
autoload :Sweeping, 'action_controller/caching/sweeping'
end
- included do
- @@cache_store = nil
- cattr_reader :cache_store
-
- # Defines the storage option for cached fragments
- def self.cache_store=(store_option)
- @@cache_store = ActiveSupport::Cache.lookup_store(store_option)
+ module ConfigMethods
+ def cache_store
+ config.cache_store
end
- include Pages, Actions, Fragments
- include Sweeping if defined?(ActiveRecord)
+ def cache_store=(store)
+ config.cache_store = ActiveSupport::Cache.lookup_store(store)
+ end
- @@perform_caching = true
- cattr_accessor :perform_caching
- end
+ private
- module ClassMethods
def cache_configured?
perform_caching && cache_store
end
end
+ include ConfigMethods
+ include Pages, Actions, Fragments
+ include Sweeping if defined?(ActiveRecord)
+
+ included do
+ extend ConfigMethods
+
+ @@perform_caching = true
+ cattr_accessor :perform_caching
+ end
+
+
def caching_allowed?
request.get? && response.status == 200
end
@@ -75,10 +81,5 @@ module ActionController #:nodoc:
yield
end
end
-
- private
- def cache_configured?
- self.class.cache_configured?
- end
end
end
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 00a7f034d3..bb5ff95a67 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -41,7 +41,9 @@ module ActionController #:nodoc:
else
pos = buffer.length
block.call
- write_fragment(name, buffer[pos..-1], options)
+ content = buffer[pos..-1]
+ content = content.as_str if content.respond_to?(:as_str)
+ write_fragment(name, content, options)
end
else
block.call
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 5797eeebd6..fe95f0e0d7 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -1,5 +1,6 @@
require 'fileutils'
require 'uri'
+require 'active_support/core_ext/class/attribute_accessors'
module ActionController #:nodoc:
module Caching
@@ -34,27 +35,26 @@ module ActionController #:nodoc:
# 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
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- base.class_eval do
- @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
- ##
- # :singleton-method:
- # 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.
- cattr_accessor :page_cache_directory
-
- @@page_cache_extension = '.html'
- ##
- # :singleton-method:
- # 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.
- cattr_accessor :page_cache_extension
- end
+ extend ActiveSupport::Concern
+
+ included do
+ @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
+ ##
+ # :singleton-method:
+ # 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.
+ cattr_accessor :page_cache_directory
+
+ @@page_cache_extension = '.html'
+ ##
+ # :singleton-method:
+ # 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.
+ cattr_accessor :page_cache_extension
end
module ClassMethods
@@ -121,10 +121,10 @@ module ActionController #:nodoc:
if options.is_a?(Hash)
if options[:action].is_a?(Array)
options[:action].dup.each do |action|
- self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => 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, :skip_relative_url_root => true)))
+ self.class.expire_page(url_for(options.merge(:only_path => true)))
end
else
self.class.expire_page(options)
@@ -139,7 +139,7 @@ module ActionController #:nodoc:
path = case options
when Hash
- url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))
+ url_for(options.merge(:only_path => true, :format => params[:format]))
when String
options
else
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 871f41bfdd..cf16417e84 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -30,9 +30,7 @@ module ActionController #:nodoc:
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
# end
module Sweeping
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
module ClassMethods #:nodoc:
def cache_sweeper(*sweepers)
diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb
index a4eef07841..9f2de57033 100644
--- a/actionpack/lib/action_controller/deprecated.rb
+++ b/actionpack/lib/action_controller/deprecated.rb
@@ -1,5 +1,3 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
ActionController::Routing = ActionDispatch::Routing
-ActionController::Routing::Routes = ActionDispatch::Routing::RouteSet.new
-ActionController::UrlWriter = ActionController::UrlFor
diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb
new file mode 100644
index 0000000000..1d05b3fbd6
--- /dev/null
+++ b/actionpack/lib/action_controller/deprecated/base.rb
@@ -0,0 +1,131 @@
+module ActionController
+ class Base
+ class << self
+ def deprecated_config_accessor(option, message = nil)
+ deprecated_config_reader(option, message)
+ deprecated_config_writer(option, message)
+ end
+
+ def deprecated_config_reader(option, message = nil)
+ message ||= "Reading #{option} directly from ActionController::Base is deprecated. " \
+ "Please read it from config.#{option}"
+
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{option}
+ ActiveSupport::Deprecation.warn #{message.inspect}, caller
+ config.#{option}
+ end
+ RUBY
+ end
+
+ def deprecated_config_writer(option, message = nil)
+ message ||= "Setting #{option} directly on ActionController::Base is deprecated. " \
+ "Please set it on config.action_controller.#{option}"
+
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{option}=(val)
+ ActiveSupport::Deprecation.warn #{message.inspect}, caller
+ config.#{option} = val
+ end
+ RUBY
+ end
+
+ def consider_all_requests_local
+ ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
+ "use Rails.application.config.consider_all_requests_local instead", caller
+ Rails.application.config.consider_all_requests_local
+ end
+
+ def consider_all_requests_local=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is deprecated. " <<
+ "Please configure it on your application with config.consider_all_requests_local=", caller
+ Rails.application.config.consider_all_requests_local = value
+ end
+
+ def allow_concurrency
+ ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
+ "use Rails.application.config.allow_concurrency instead", caller
+ Rails.application.config.allow_concurrency
+ end
+
+ def allow_concurrency=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is deprecated. " <<
+ "Please configure it on your application with config.allow_concurrency=", caller
+ Rails.application.config.allow_concurrency = value
+ end
+
+ def ip_spoofing_check=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check= is deprecated. " <<
+ "Please configure it on your application with config.action_dispatch.ip_spoofing_check=", caller
+ Rails.application.config.action_dispatch.ip_spoofing_check = value
+ end
+
+ def ip_spoofing_check
+ ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check is deprecated. " <<
+ "Configuring ip_spoofing_check on the application configures a middleware.", caller
+ Rails.application.config.action_disaptch.ip_spoofing_check
+ end
+
+ def trusted_proxies=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies= is deprecated. " <<
+ "Please configure it on your application with config.action_dispatch.trusted_proxies=", caller
+ Rails.application.config.action_dispatch.ip_spoofing_check = value
+ end
+
+ def trusted_proxies
+ ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies is deprecated. " <<
+ "Configuring trusted_proxies on the application configures a middleware.", caller
+ Rails.application.config.action_dispatch.ip_spoofing_check = value
+ end
+
+ def session(*args)
+ ActiveSupport::Deprecation.warn(
+ "Disabling sessions for a single controller has been deprecated. " +
+ "Sessions are now lazy loaded. So if you don't access them, " +
+ "consider them off. You can still modify the session cookie " +
+ "options with request.session_options.", caller)
+ end
+
+ def session=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.session= is deprecated. " <<
+ "Please configure it on your application with config.session_store :cookie_store, :key => '....'", caller
+ if value.delete(:disabled)
+ Rails.application.config.session_store :disabled
+ else
+ store = Rails.application.config.session_store
+ Rails.application.config.session_store store, value
+ end
+ end
+
+ # Controls the resource action separator
+ def resource_action_separator
+ @resource_action_separator ||= "/"
+ end
+
+ def resource_action_separator=(val)
+ ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \
+ "works with the deprecated router DSL."
+ @resource_action_separator = val
+ end
+
+ def use_accept_header
+ ActiveSupport::Deprecation.warn "ActionController::Base.use_accept_header doesn't do anything anymore. " \
+ "The accept header is always taken into account."
+ end
+
+ def use_accept_header=(val)
+ use_accept_header
+ end
+ end
+
+ deprecated_config_writer :session_store
+ deprecated_config_writer :session_options
+ deprecated_config_accessor :relative_url_root, "relative_url_root is ineffective. Please stop using it"
+ deprecated_config_accessor :assets_dir
+ deprecated_config_accessor :javascripts_dir
+ deprecated_config_accessor :stylesheets_dir
+
+ delegate :consider_all_requests_local, :consider_all_requests_local=,
+ :allow_concurrency, :allow_concurrency=, :to => :"self.class"
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 2b35e111ec..eebd2c943a 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -34,7 +34,8 @@ module ActionController
# and response object available. You might wish to control the
# environment and response manually for performance reasons.
- attr_internal :status, :headers, :content_type, :response
+ attr_internal :status, :headers, :content_type, :response, :request
+ delegate :session, :to => "@_request"
def initialize(*)
@_headers = {}
@@ -49,6 +50,14 @@ module ActionController
headers["Content-Type"] = type.to_s
end
+ def content_type
+ headers["Content-Type"]
+ end
+
+ def location
+ headers["Location"]
+ end
+
def location=(url)
headers["Location"] = url
end
@@ -58,8 +67,9 @@ module ActionController
end
# :api: private
- def dispatch(name, env)
- @_env = env
+ def dispatch(name, request)
+ @_request = request
+ @_env = request.env
@_env['action_controller.instance'] = self
process(name)
to_a
@@ -70,31 +80,12 @@ module ActionController
response ? response.to_a : [status, headers, response_body]
end
- class ActionEndpoint
- @@endpoints = Hash.new {|h,k| h[k] = Hash.new {|sh,sk| sh[sk] = {} } }
-
- def self.for(controller, action, stack)
- @@endpoints[controller][action][stack] ||= begin
- endpoint = new(controller, action)
- stack.build(endpoint)
- end
- end
-
- def initialize(controller, action)
- @controller, @action = controller, action
- @_formats = [Mime::HTML]
- end
-
- def call(env)
- @controller.new.dispatch(@action, env)
- end
- end
-
class_attribute :middleware_stack
self.middleware_stack = ActionDispatch::MiddlewareStack.new
def self.inherited(base)
self.middleware_stack = base.middleware_stack.dup
+ super
end
def self.use(*args)
@@ -118,8 +109,10 @@ module ActionController
#
# ==== Returns
# Proc:: A rack application
- def self.action(name)
- ActionEndpoint.for(self, name, middleware_stack)
+ def self.action(name, klass = ActionDispatch::Request)
+ middleware_stack.build do |env|
+ new.dispatch(name, klass.new(env))
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index a1cfa32d4d..ab8d87b2c4 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -2,26 +2,23 @@ module ActionController
module Compatibility
extend ActiveSupport::Concern
- include AbstractController::Compatibility
-
class ::ActionController::ActionControllerError < StandardError #:nodoc:
end
+ module ClassMethods
+ end
+
# Temporary hax
included do
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError
- cattr_accessor :session_options
- self.session_options = {}
-
- cattr_accessor :relative_url_root
- self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
+ # ROUTES TODO: This should be handled by a middleware and route generation
+ # should be able to handle SCRIPT_NAME
+ self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
class << self
delegate :default_charset=, :to => "ActionDispatch::Response"
- delegate :resources_path_names, :to => "ActionController::Routing::Routes"
- delegate :resources_path_names=, :to => "ActionController::Routing::Routes"
end
# cattr_reader :protected_instance_variables
@@ -32,31 +29,17 @@ module ActionController
@before_filter_chain_aborted @_headers @_params
@_response)
- # Controls the resource action separator
- cattr_accessor :resource_action_separator
- self.resource_action_separator = "/"
-
- cattr_accessor :use_accept_header
- self.use_accept_header = true
+ def rescue_action(env)
+ raise env["action_dispatch.rescue.exception"]
+ end
self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
-
- # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
- # and images to a dedicated asset server away from the main web server. Example:
- # ActionController::Base.asset_host = "http://assets.example.com"
- cattr_accessor :asset_host
-
- cattr_accessor :ip_spoofing_check
- self.ip_spoofing_check = true
-
- cattr_accessor :trusted_proxies
end
# For old tests
def initialize_template_class(*) end
def assign_shortcuts(*) end
- # TODO: Remove this after we flip
def template
@template ||= view_context
end
@@ -66,52 +49,20 @@ module ActionController
super
end
- module ClassMethods
- def consider_all_requests_local
- ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
- "use Rails.application.config.consider_all_requests_local instead"
- Rails.application.config.consider_all_requests_local
- end
-
- def consider_all_requests_local=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is no longer effective. " <<
- "Please configure it on your application with config.consider_all_requests_local="
- Rails.application.config.consider_all_requests_local = value
- end
-
- def allow_concurrency
- ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
- "use Rails.application.config.allow_concurrency instead"
- Rails.application.config.allow_concurrency
- end
-
- def allow_concurrency=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is no longer effective. " <<
- "Please configure it on your application with config.allow_concurrency="
- Rails.application.config.allow_concurrency = value
- end
-
- def rescue_action(env)
- raise env["action_dispatch.rescue.exception"]
- end
-
- # Defines the storage option for cached fragments
- def cache_store=(store_option)
- @@cache_store = ActiveSupport::Cache.lookup_store(store_option)
- end
- end
-
- delegate :consider_all_requests_local, :consider_all_requests_local=,
- :allow_concurrency, :allow_concurrency=, :to => :"self.class"
-
- def render_to_body(options)
- if options.is_a?(Hash) && options.key?(:template)
- options[:template].sub!(/^\//, '')
+ def _normalize_options(options)
+ if options[:action] && options[:action].to_s.include?(?/)
+ ActiveSupport::Deprecation.warn "Giving a path to render :action is deprecated. " <<
+ "Please use render :template instead", caller
+ options[:template] = options.delete(:action)
end
options[:text] = nil if options.delete(:nothing) == true
options[:text] = " " if options.key?(:text) && options[:text].nil?
+ super
+ end
+ def render_to_body(options)
+ options[:template].sub!(/^\//, '') if options.key?(:template)
super || " "
end
@@ -126,18 +77,5 @@ module ActionController
def performed?
response_body
end
-
- # ==== Request only view path switching ====
- def append_view_path(path)
- view_paths.push(*path)
- end
-
- def prepend_view_path(path)
- view_paths.unshift(*path)
- end
-
- def view_paths
- view_context.view_paths
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/configuration.rb b/actionpack/lib/action_controller/metal/configuration.rb
deleted file mode 100644
index 5c829853b7..0000000000
--- a/actionpack/lib/action_controller/metal/configuration.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module ActionController
- module Configuration
- extend ActiveSupport::Concern
-
- def config
- @config ||= self.class.config
- end
-
- def config=(config)
- @config = config
- end
-
- module ClassMethods
- def default_config
- @default_config ||= {}
- end
-
- def config
- self.config ||= default_config
- end
-
- def config=(config)
- @config = ActiveSupport::OrderedHash.new
- @config.merge!(config)
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 37be8b3999..a5c9910d68 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -1,6 +1,7 @@
module ActionController
module Head
extend ActiveSupport::Concern
+
include ActionController::UrlFor
# Return a response that has no content (merely headers). The options
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 1b5a4c3080..8efe01e37b 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -86,7 +86,7 @@ module ActionController
end
private
- # Overwrite _modules_for_helpers to accept :all as argument, which loads
+ # Overwrite modules_for_helpers to accept :all as argument, which loads
# all helpers in helpers_dir.
#
# ==== Parameters
@@ -95,7 +95,7 @@ module ActionController
# ==== Returns
# Array[Module]:: A normalized list of modules for the list of
# helpers provided.
- def _modules_for_helpers(args)
+ def modules_for_helpers(args)
args += all_application_helpers if args.delete(:all)
super(args)
end
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
index e893acffdf..3358d80c35 100644
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ b/actionpack/lib/action_controller/metal/hide_actions.rb
@@ -15,10 +15,8 @@ module ActionController
# Overrides AbstractController::Base#action_method? to return false if the
# action name is in the list of hidden actions.
- def action_method?(action_name)
- self.class.visible_action?(action_name) do
- !self.class.hidden_actions.include?(action_name) && super
- end
+ def method_for_action(action_name)
+ self.class.visible_action?(action_name) && super
end
module ClassMethods
@@ -31,13 +29,13 @@ module ActionController
end
def inherited(klass)
- klass.instance_variable_set("@visible_actions", {})
+ 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] = yield
+ @visible_actions[action_name] = !hidden_actions.include?(action_name)
end
# Overrides AbstractController::Base#action_methods to remove any methods
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 0f35a7c040..6ec788f302 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -124,7 +124,7 @@ module ActionController
end
def authenticate(request, &login_procedure)
- unless authorization(request).blank?
+ unless request.authorization.blank?
login_procedure.call(*user_name_and_password(request))
end
end
@@ -133,15 +133,8 @@ module ActionController
decode_credentials(request).split(/:/, 2)
end
- def authorization(request)
- request.env['HTTP_AUTHORIZATION'] ||
- request.env['X-HTTP_AUTHORIZATION'] ||
- request.env['X_HTTP_AUTHORIZATION'] ||
- request.env['REDIRECT_X_HTTP_AUTHORIZATION']
- end
-
def decode_credentials(request)
- ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '')
+ ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
end
def encode_credentials(user_name, password)
@@ -165,7 +158,7 @@ module ActionController
# Authenticate with HTTP Digest, returns true or false
def authenticate_with_http_digest(realm = "Application", &password_procedure)
- HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
+ HttpAuthentication::Digest.authenticate(config.secret, request, realm, &password_procedure)
end
# Render output including the HTTP Digest authentication header
@@ -175,30 +168,23 @@ module ActionController
end
# Returns false on a valid response, true otherwise
- def authenticate(request, realm, &password_procedure)
- authorization(request) && validate_digest_response(request, realm, &password_procedure)
- end
-
- def authorization(request)
- request.env['HTTP_AUTHORIZATION'] ||
- request.env['X-HTTP_AUTHORIZATION'] ||
- request.env['X_HTTP_AUTHORIZATION'] ||
- request.env['REDIRECT_X_HTTP_AUTHORIZATION']
+ def authenticate(secret_key, request, realm, &password_procedure)
+ request.authorization && validate_digest_response(secret_key, request, realm, &password_procedure)
end
# Returns false unless the request credentials response value matches the expected value.
# First try the password as a ha1 digest password. If this fails, then try it as a plain
# text password.
- def validate_digest_response(request, realm, &password_procedure)
+ def validate_digest_response(secret_key, request, realm, &password_procedure)
credentials = decode_credentials_header(request)
- valid_nonce = validate_nonce(request, credentials[:nonce])
+ valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
- if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
+ if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
password = password_procedure.call(credentials[:username])
return false unless password
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
- uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url
+ uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url
[true, false].any? do |password_is_ha1|
expected = expected_response(method, uri, credentials, password, password_is_ha1)
@@ -226,7 +212,7 @@ module ActionController
end
def decode_credentials_header(request)
- decode_credentials(authorization(request))
+ decode_credentials(request.authorization)
end
def decode_credentials(header)
@@ -238,6 +224,9 @@ module ActionController
end
def authentication_header(controller, realm)
+ secret_key = controller.config.secret
+ nonce = self.nonce(secret_key)
+ opaque = opaque(secret_key)
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
end
@@ -280,7 +269,7 @@ module ActionController
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
# key from the Rails session secret generated upon creation of project. Ensures
# the time cannot be modified by client.
- def nonce(time = Time.now)
+ def nonce(secret_key, time = Time.now)
t = time.to_i
hashed = [t, secret_key]
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
@@ -292,21 +281,16 @@ module ActionController
# Can be much shorter if the Stale directive is implemented. This would
# allow a user to use new nonce without prompting user again for their
# username and password.
- def validate_nonce(request, value, seconds_to_timeout=5*60)
+ def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
t = ActiveSupport::Base64.decode64(value).split(":").first.to_i
- nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
+ nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
end
# Opaque based on random generation - but changing each request?
- def opaque()
+ def opaque(secret_key)
::Digest::MD5.hexdigest(secret_key)
end
- # Set in /initializers/session_store.rb, and loaded even if sessions are not in use.
- def secret_key
- ActionController::Base.session_options[:secret]
- end
-
end
end
end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
new file mode 100644
index 0000000000..282dcf66b3
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -0,0 +1,21 @@
+module ActionController
+ module ImplicitRender
+ def send_action(*)
+ ret = super
+ default_render unless response_body
+ ret
+ end
+
+ def default_render
+ render
+ end
+
+ def method_for_action(action_name)
+ super || begin
+ if template_exists?(action_name.to_s, _prefix)
+ "default_render"
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 85035dc09c..d69de65f28 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -20,7 +20,7 @@ module ActionController
:params => request.filtered_parameters,
:formats => request.formats.map(&:to_sym),
:method => request.method,
- :path => (request.request_uri rescue "unknown")
+ :path => (request.fullpath rescue "unknown")
}
ActiveSupport::Notifications.instrument("action_controller.start_processing", raw_payload.dup)
diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index bb55383631..37106733cb 100644
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -6,14 +6,11 @@ module ActionController
extend ActiveSupport::Concern
included do
- delegate :session, :to => "@_request"
delegate :headers, :status=, :location=, :content_type=,
:status, :location, :content_type, :to => "@_response"
- attr_internal :request
end
- def dispatch(action, env)
- @_request = ActionDispatch::Request.new(env)
+ def dispatch(action, request)
@_response = ActionDispatch::Response.new
@_response.request = request
super
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index faf0589fd2..25e4e18493 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -11,6 +11,7 @@ module ActionController
extend ActiveSupport::Concern
include AbstractController::Logger
+ include ActionController::RackDelegation
include ActionController::UrlFor
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 639b508746..49d3d6b466 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -19,7 +19,7 @@ module ActionController
<<-RUBY_EVAL
if options.key?(:#{name})
_process_options(options)
- return _render_option_#{name}(options[:#{name}], options)
+ return _render_option_#{name}(options.delete(:#{name}), options)
end
RUBY_EVAL
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 0aae9f8579..f892bd9b91 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -2,68 +2,54 @@ module ActionController
module Rendering
extend ActiveSupport::Concern
- included do
- include AbstractController::Rendering
- include AbstractController::LocalizedCache
- end
+ include ActionController::RackDelegation
+ include AbstractController::Rendering
- def process_action(*)
- self.formats = request.formats.map {|x| x.to_sym}
+ def process(*)
+ self.formats = request.formats.map { |x| x.to_sym }
super
end
def render(*args)
- if response_body
- raise ::AbstractController::DoubleRenderError
- end
-
- args << {} unless args.last.is_a?(Hash)
- super(*args)
- self.content_type ||= args.last[:_template].mime_type.to_s
- response_body
- end
-
- def render_to_body(options)
- _process_options(options)
+ raise ::AbstractController::DoubleRenderError if response_body
super
+ response_body
end
private
- def _render_partial(options)
- options[:partial] = action_name if options[:partial] == true
- options[:_details] = {:formats => formats}
- super
+ def _normalize_args(action=nil, options={}, &blk)
+ options = super
+ options[:update] = blk if block_given?
+ options
end
- def format_for_text
- formats.first
+ def _normalize_options(options)
+ if options.key?(:text) && options[:text].respond_to?(:to_text)
+ options[:text] = options[:text].to_text
+ end
+
+ if options[:status]
+ options[:status] = Rack::Utils.status_code(options[:status])
+ end
+
+ super
end
def _process_options(options)
status, content_type, location = options.values_at(:status, :content_type, :location)
+
self.status = status if status
self.content_type = content_type if content_type
self.headers["Location"] = url_for(location) if location
- end
- def _normalize_options(action=nil, options={}, &blk)
- case action
- when NilClass
- when Hash
- options = super(action.delete(:action), action)
- when String, Symbol
- options = super
- else
- options.merge! :partial => action
- end
-
- if options[:status]
- options[:status] = Rack::Utils.status_code(options[:status])
- end
+ super
+ end
- options[:update] = blk if block_given?
- options
+ def _with_template_hook(template)
+ super
+ self.content_type ||= template.mime_type.to_s
end
+
end
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 276c703307..39a809657b 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -12,11 +12,10 @@ module ActionController #:nodoc:
included do
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
# sets it to <tt>:authenticity_token</tt> by default.
- cattr_accessor :request_forgery_protection_token
+ config.request_forgery_protection_token ||= :authenticity_token
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
- class_attribute :allow_forgery_protection
- self.allow_forgery_protection = true
+ config.allow_forgery_protection ||= true
helper_method :form_authenticity_token
helper_method :protect_against_forgery?
@@ -80,9 +79,47 @@ module ActionController #:nodoc:
self.request_forgery_protection_token ||= :authenticity_token
before_filter :verify_authenticity_token, options
end
+
+ def request_forgery_protection_token
+ config.request_forgery_protection_token
+ end
+
+ def request_forgery_protection_token=(val)
+ config.request_forgery_protection_token = val
+ end
+
+ def allow_forgery_protection
+ config.allow_forgery_protection
+ end
+
+ def allow_forgery_protection=(val)
+ config.allow_forgery_protection = val
+ end
end
protected
+
+ def protect_from_forgery(options = {})
+ self.request_forgery_protection_token ||= :authenticity_token
+ before_filter :verify_authenticity_token, options
+ end
+
+ def request_forgery_protection_token
+ config.request_forgery_protection_token
+ end
+
+ def request_forgery_protection_token=(val)
+ config.request_forgery_protection_token = val
+ end
+
+ def allow_forgery_protection
+ config.allow_forgery_protection
+ end
+
+ def allow_forgery_protection=(val)
+ config.allow_forgery_protection = val
+ end
+
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
verified_request? || raise(ActionController::InvalidAuthenticityToken)
@@ -109,7 +146,7 @@ module ActionController #:nodoc:
end
def protect_against_forgery?
- self.class.allow_forgery_protection
+ config.allow_forgery_protection
end
end
end
diff --git a/actionpack/lib/action_controller/metal/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb
index d70f40ce7a..91d89ff9a4 100644
--- a/actionpack/lib/action_controller/metal/session_management.rb
+++ b/actionpack/lib/action_controller/metal/session_management.rb
@@ -2,44 +2,8 @@ module ActionController #:nodoc:
module SessionManagement #:nodoc:
extend ActiveSupport::Concern
- include ActionController::Configuration
-
module ClassMethods
- # Set the session store to be used for keeping the session data between requests.
- # By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
- # but you can also specify one of the other included stores (<tt>:active_record_store</tt>,
- # <tt>:mem_cache_store</tt>, or your own custom class.
- def session_store=(store)
- if store == :active_record_store
- self.session_store = ActiveRecord::SessionStore
- else
- @@session_store = store.is_a?(Symbol) ?
- ActionDispatch::Session.const_get(store.to_s.camelize) :
- store
- end
- end
-
- # Returns the session store class currently used.
- def session_store
- if defined? @@session_store
- @@session_store
- else
- ActionDispatch::Session::CookieStore
- end
- end
-
- def session=(options = {})
- self.session_store = nil if options.delete(:disabled)
- session_options.merge!(options)
- end
- def session(*args)
- ActiveSupport::Deprecation.warn(
- "Disabling sessions for a single controller has been deprecated. " +
- "Sessions are now lazy loaded. So if you don't access them, " +
- "consider them off. You can still modify the session cookie " +
- "options with request.session_options.", caller)
- end
end
end
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 8f03b8bb17..753af3dc58 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -9,18 +9,13 @@ module ActionController #:nodoc:
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
:disposition => 'attachment'.freeze,
- :stream => true,
- :buffer_size => 4096,
- :x_sendfile => false
}.freeze
- X_SENDFILE_HEADER = 'X-Sendfile'.freeze
-
protected
- # Sends the file, by default streaming it 4096 bytes at a time. This way the
- # whole file doesn't need to be read into memory at once. This makes it
- # feasible to send even large files. You can optionally turn off streaming
- # and send the whole file at once.
+ # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
+ # via the Rack::Sendfile middleware. The header to use is set via
+ # config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile".
+ # Your server can also configure this for you by setting the X-Sendfile-Type header.
#
# Be careful to sanitize the path parameter if it is coming from a web
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
@@ -31,24 +26,12 @@ module ActionController #:nodoc:
# Defaults to <tt>File.basename(path)</tt>.
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
- # * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
- # is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
- # * <tt>:stream</tt> - whether to send the file to the user agent as it is read (+true+)
- # or to read the entire file before sending (+false+). Defaults to +true+.
- # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
- # Defaults to 4096.
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
# the URL, which is necessary for i18n filenames on certain browsers
# (setting <tt>:filename</tt> overrides this option).
- # * <tt>:x_sendfile</tt> - uses X-Sendfile to send the file when set to +true+. This is currently
- # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this
- # uses the web server to send the file, this may lower memory consumption on your server and
- # it will not block your application for further requests.
- # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and
- # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+.
#
# The default Content-Type and Content-Disposition headers are
# set to download arbitrary binary files in as many browsers as
@@ -81,29 +64,16 @@ module ActionController #:nodoc:
def send_file(path, options = {}) #:doc:
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
- options[:length] ||= File.size(path)
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
send_file_headers! options
- @performed_render = false
-
if options[:x_sendfile]
- head options[:status], X_SENDFILE_HEADER => path
- else
- if options[:stream]
- # TODO : Make render :text => proc {} work with the new base
- render :status => options[:status], :text => Proc.new { |response, output|
- len = options[:buffer_size] || 4096
- File.open(path, 'rb') do |file|
- while buf = file.read(len)
- output.write(buf)
- end
- end
- }
- else
- File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
- end
+ ActiveSupport::Deprecation.warn(":x_sendfile is no longer needed in send_file", caller)
end
+
+ self.status = options[:status] || 200
+ self.content_type = options[:content_type] if options.key?(:content_type)
+ self.response_body = File.open(path, "rb")
end
# Sends the given binary data to the browser. This method is similar to
@@ -138,32 +108,35 @@ module ActionController #:nodoc:
# data to the browser, then use <tt>render :text => proc { ... }</tt>
# instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
- send_file_headers! options.merge(:length => data.bytesize)
- render :status => options[:status], :text => data
+ send_file_headers! options.dup
+ render options.slice(:status, :content_type).merge(:text => data)
end
private
def send_file_headers!(options)
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
- [:length, :type, :disposition].each do |arg|
+ [:type, :disposition].each do |arg|
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
end
- disposition = options[:disposition].dup || 'attachment'
+ if options.key?(:length)
+ ActiveSupport::Deprecation.warn("You do not need to provide the file's length", caller)
+ end
- disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
+ disposition = options[:disposition]
+ disposition += %(; filename="#{options[:filename]}") if options[:filename]
content_type = options[:type]
if content_type.is_a?(Symbol)
- raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s)
- self.content_type = Mime::Type.lookup_by_extension(content_type.to_s)
+ extension = Mime[content_type]
+ raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
+ self.content_type = extension
else
self.content_type = content_type
end
headers.merge!(
- 'Content-Length' => options[:length].to_s,
'Content-Disposition' => disposition,
'Content-Transfer-Encoding' => 'binary'
)
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index 707ad968f4..4b8c452d50 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -13,7 +13,6 @@ module ActionController
if cookies = @_request.env['action_dispatch.cookies']
cookies.write(@_response)
end
- @_response.body ||= self.response_body
@_response.prepare!
set_test_assigns
ret
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 4f3ad07be5..10c7ca9021 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -1,165 +1,20 @@
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/attribute_accessors'
-
module ActionController
- # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
- # is also possible: an URL can be generated from one of your routing definitions.
- # URL generation functionality is centralized in this module.
- #
- # See ActionController::Routing and ActionController::Resources for general
- # information about routing and routes.rb.
- #
- # <b>Tip:</b> If you need to generate URLs from your models or some other place,
- # then ActionController::UrlFor is what you're looking for. Read on for
- # an introduction.
- #
- # == URL generation from parameters
- #
- # As you may know, some functions - such as ActionController::Base#url_for
- # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
- # of parameters. For example, you've probably had the chance to write code
- # like this in one of your views:
- #
- # <%= link_to('Click here', :controller => 'users',
- # :action => 'new', :message => 'Welcome!') %>
- #
- # #=> Generates a link to: /users/new?message=Welcome%21
- #
- # link_to, and all other functions that require URL generation functionality,
- # actually use ActionController::UrlFor under the hood. And in particular,
- # they use the ActionController::UrlFor#url_for method. One can generate
- # the same path as the above example by using the following code:
- #
- # include UrlFor
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :only_path => true)
- # # => "/users/new?message=Welcome%21"
- #
- # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
- # information about the website hostname that your Rails app is serving. So if you
- # want to include the hostname as well, then you must also pass the <tt>:host</tt>
- # argument:
- #
- # include UrlFor
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
- # # => "http://www.example.com/users/new?message=Welcome%21"
- #
- # By default, all controllers and views have access to a special version of url_for,
- # that already knows what the current hostname is. So if you use url_for in your
- # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
- # argument.
- #
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
- # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
- #
- #
- # == URL generation for named routes
- #
- # UrlFor also allows one to access methods that have been auto-generated from
- # named routes. For example, suppose that you have a 'users' resource in your
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # This generates, among other things, the method <tt>users_path</tt>. By default,
- # this method is accessible from your controllers, views and mailers. If you need
- # to access this auto-generated method from other places (such as a model), then
- # you can do that by including ActionController::UrlFor in your class:
- #
- # class User < ActiveRecord::Base
- # include ActionController::UrlFor
- #
- # def base_uri
- # user_path(self)
- # end
- # end
- #
- # User.find(1).base_uri # => "/users/1"
- #
module UrlFor
extend ActiveSupport::Concern
- included do
- ActionController::Routing::Routes.install_helpers(self)
-
- # Including in a class uses an inheritable hash. Modules get a plain hash.
- if respond_to?(:class_attribute)
- class_attribute :default_url_options
- else
- mattr_accessor :default_url_options
- end
+ include ActionDispatch::Routing::UrlFor
- self.default_url_options = {}
+ def url_options
+ super.reverse_merge(
+ :host => request.host_with_port,
+ :protocol => request.protocol,
+ :_path_segments => request.symbolized_path_parameters
+ ).merge(:script_name => request.script_name)
end
- # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
- # the form of a hash, just like the one you would use for url_for directly. Example:
- #
- # def default_url_options(options)
- # { :project => @project.active? ? @project.url_name : "unknown" }
- # end
- #
- # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
- # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
- # by this method.
- def default_url_options(options = nil)
- self.class.default_url_options
- end
-
- def rewrite_options(options) #:nodoc:
- if options.delete(:use_defaults) != false && (defaults = default_url_options(options))
- defaults.merge(options)
- else
- options
- end
- end
-
- # Generate a url based on the options provided, default_url_options and the
- # routes defined in routes.rb. The following options are supported:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> - Optionally specify the port to connect to.
- # * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in ActionController::Base.relative_url_root.
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
- #
- # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
- # +url_for+ is forwarded to the Routes module.
- #
- # Examples:
- #
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
- # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
- def url_for(options = {})
- options ||= {}
- case options
- when String
- options
- when Hash
- _url_rewriter.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
- end
-
- protected
-
- def _url_rewriter
- ActionController::UrlRewriter
+ def _router
+ raise "In order to use #url_for, you must include the helpers of a particular " \
+ "router. For instance, `include Rails.application.routes.url_helpers"
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/middleware.rb b/actionpack/lib/action_controller/middleware.rb
index 17275793b7..2115b07b3e 100644
--- a/actionpack/lib/action_controller/middleware.rb
+++ b/actionpack/lib/action_controller/middleware.rb
@@ -6,7 +6,8 @@ module ActionController
end
def call(env)
- @controller.build(@app).dispatch(:index, env)
+ request = ActionDispatch::Request.new(env)
+ @controller.build(@app).dispatch(:index, request)
end
end
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index eaed00cfb7..ae363e300c 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -92,8 +92,7 @@ module ActionController
inflection = if options[:action].to_s == "new"
args.pop
:singular
- elsif (record.respond_to?(:new_record?) && record.new_record?) ||
- (record.respond_to?(:destroyed?) && record.destroyed?)
+ elsif (record.respond_to?(:persisted?) && !record.persisted?)
args.pop
:plural
elsif record.is_a?(Class)
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 55a5c22ac0..6a3afbb157 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -1,30 +1,80 @@
-require "action_controller"
require "rails"
+require "action_controller"
require "action_view/railtie"
+require "active_support/core_ext/class/subclasses"
+require "active_support/deprecation/proxy_wrappers"
+require "active_support/deprecation"
module ActionController
class Railtie < Rails::Railtie
railtie_name :action_controller
- require "action_controller/railties/subscriber"
- subscriber ActionController::Railties::Subscriber.new
+ require "action_controller/railties/log_subscriber"
+ require "action_controller/railties/url_helpers"
+
+ ad = config.action_dispatch
+ config.action_controller.singleton_class.send(:define_method, :session) do
+ ActiveSupport::Deprecation.warn "config.action_controller.session has been " \
+ "renamed to config.action_dispatch.session.", caller
+ ad.session
+ end
+
+ config.action_controller.singleton_class.send(:define_method, :session=) do |val|
+ ActiveSupport::Deprecation.warn "config.action_controller.session has been " \
+ "renamed to config.action_dispatch.session.", caller
+ ad.session = val
+ end
+
+ config.action_controller.singleton_class.send(:define_method, :session_store) do
+ ActiveSupport::Deprecation.warn "config.action_controller.session_store has been " \
+ "renamed to config.action_dispatch.session_store.", caller
+ ad.session_store
+ end
+
+ config.action_controller.singleton_class.send(:define_method, :session_store=) do |val|
+ ActiveSupport::Deprecation.warn "config.action_controller.session_store has been " \
+ "renamed to config.action_dispatch.session_store.", caller
+ ad.session_store = val
+ end
+
+ log_subscriber ActionController::Railties::LogSubscriber.new
initializer "action_controller.logger" do
- ActionController::Base.logger ||= Rails.logger
+ ActionController.base_hook { self.logger ||= Rails.logger }
end
initializer "action_controller.set_configs" do |app|
- app.config.action_controller.each do |k,v|
- ActionController::Base.send "#{k}=", v
- end
+ paths = app.config.paths
+ ac = app.config.action_controller
+
+ ac.assets_dir = paths.public.to_a.first
+ ac.javascripts_dir = paths.public.javascripts.to_a.first
+ ac.stylesheets_dir = paths.public.stylesheets.to_a.first
+ ac.secret = app.config.cookie_secret
+
+ ActionController.base_hook { self.config.replace(ac) }
end
initializer "action_controller.initialize_framework_caches" do
- ActionController::Base.cache_store ||= RAILS_CACHE
+ ActionController.base_hook { self.cache_store ||= RAILS_CACHE }
end
initializer "action_controller.set_helpers_path" do |app|
- ActionController::Base.helpers_path = app.config.paths.app.helpers.to_a
+ ActionController.base_hook do
+ self.helpers_path = app.config.paths.app.helpers.to_a
+ end
+ end
+
+ initializer "action_controller.url_helpers" do |app|
+ ActionController.base_hook do
+ extend ::ActionController::Railtie::UrlHelpers.with(app.routes)
+ end
+
+ message = "ActionController::Routing::Routes is deprecated. " \
+ "Instead, use Rails.application.routes"
+
+ proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(app.routes, message)
+ ActionController::Routing::Routes = proxy
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/log_subscriber.rb
index 4499e82292..c2299d0b05 100644
--- a/actionpack/lib/action_controller/railties/subscriber.rb
+++ b/actionpack/lib/action_controller/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActionController
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
INTERNAL_PARAMS = %w(controller action format _method only_path)
def start_processing(event)
@@ -22,15 +22,7 @@ module ActionController
end
def send_file(event)
- message = if event.payload[:x_sendfile]
- header = ActionController::Streaming::X_SENDFILE_HEADER
- "Sent #{header} header %s"
- elsif event.payload[:stream]
- "Streamed file %s"
- else
- "Sent file %s"
- end
-
+ message = "Sent file %s"
message << " (%.1fms)"
info(message % [event.payload[:path], event.duration])
end
diff --git a/actionpack/lib/action_controller/railties/url_helpers.rb b/actionpack/lib/action_controller/railties/url_helpers.rb
new file mode 100644
index 0000000000..ad2a8d4ef3
--- /dev/null
+++ b/actionpack/lib/action_controller/railties/url_helpers.rb
@@ -0,0 +1,14 @@
+module ActionController
+ class Railtie
+ module UrlHelpers
+ def self.with(router)
+ Module.new do
+ define_method(:inherited) do |klass|
+ super(klass)
+ klass.send(:include, router.url_helpers)
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 1165c3b7c5..3f966b1b64 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -60,13 +60,32 @@ module ActionController
#
# dom_id(Post.find(45), :edit) # => "edit_post_45"
def dom_id(record, prefix = nil)
- if record_id = record.id
+ if record_id = record_key_for_dom_id(record)
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
else
dom_class(record, prefix || NEW)
end
end
+ # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
+ # This can be overwritten to customize the default generated string representation if desired.
+ # If you need to read back a key from a dom_id in order to query for the underlying database record,
+ # you should write a helper like 'person_record_from_dom_id' that will extract the key either based
+ # on the default implementation (which just joins all key attributes with '-') or on your own
+ # overwritten version of the method. By default, this implementation passes the key string through a
+ # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
+ # make sure yourself that your dom ids are valid, in case you overwrite this method.
+ def record_key_for_dom_id(record)
+ return record.id unless record.respond_to?(:to_model)
+ key = record.to_model.to_key
+ key ? sanitize_dom_id(key.join('_')) : key
+ end
+
+ # Replaces characters that are invalid in HTML DOM ids with valid ones.
+ def sanitize_dom_id(candidate_id)
+ candidate_id # TODO implement conversion to valid DOM id values
+ end
+
# Returns the plural class name of a record or class. Examples:
#
# plural_class_name(post) # => "posts"
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 14557ca782..cdb5db32aa 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -17,9 +17,9 @@ module ActionController
end
end
- def assign_parameters(controller_path, action, parameters = {})
+ def assign_parameters(router, controller_path, action, parameters = {})
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
- extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
+ extra_keys = router.extra_keys(parameters)
non_path_parameters = get? ? query_parameters : request_parameters
parameters.each do |key, value|
if value.is_a? Fixnum
@@ -220,7 +220,7 @@ module ActionController
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
# Sanity check for required instance variables so we can give an
# understandable error message.
- %w(@controller @request @response).each do |iv_name|
+ %w(@router @controller @request @response).each do |iv_name|
if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
end
@@ -236,7 +236,7 @@ module ActionController
@request.env['REQUEST_METHOD'] = http_method
parameters ||= {}
- @request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
+ @request.assign_parameters(@router, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = @request.flash.update(flash || {})
@@ -322,6 +322,8 @@ module ActionController
@controller ||= klass.new rescue nil
end
+ @request.env.delete('PATH_INFO')
+
if @controller
@controller.request = @request
@controller.params = {}
@@ -335,13 +337,20 @@ module ActionController
private
def build_request_uri(action, parameters)
- unless @request.env['REQUEST_URI']
- options = @controller.__send__(:rewrite_options, parameters)
- options.update(:only_path => true, :action => action)
-
- url = ActionController::UrlRewriter.new(@request, parameters)
- @request.request_uri = url.rewrite(options)
+ unless @request.env["PATH_INFO"]
+ options = @controller.__send__(:url_options).merge(parameters)
+ options.update(
+ :only_path => true,
+ :action => action,
+ :relative_url_root => nil,
+ :_path_segments => @request.symbolized_path_parameters)
+
+ url, query_string = @router.url_for(options).split("?", 2)
+
+ @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
+ @request.env["PATH_INFO"] = url
+ @request.env["QUERY_STRING"] = query_string || ""
end
end
- end
+ end
end
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
deleted file mode 100644
index 933a1fa8f9..0000000000
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-require 'active_support/core_ext/hash/except'
-
-module ActionController
- # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
- class UrlRewriter #:nodoc:
- RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
-
- def initialize(request, parameters)
- @request, @parameters = request, parameters
- end
-
- def rewrite(options = {})
- options[:host] ||= @request.host_with_port
- options[:protocol] ||= @request.protocol
-
- self.class.rewrite(options, @request.symbolized_path_parameters) do |options|
- process_path_options(options)
- end
- end
-
- def to_str
- "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
- end
-
- alias_method :to_s, :to_str
-
- def self.rewrite(options, path_segments=nil)
- rewritten_url = ""
-
- unless options[:only_path]
- rewritten_url << (options[:protocol] || "http")
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
-
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
-
- rewritten_url << options[:host]
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
- end
-
- path_options = options.except(*RESERVED_OPTIONS)
- path_options = yield(path_options) if block_given?
- path = Routing::Routes.generate(path_options, path_segments || {})
-
- rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
-
- rewritten_url
- end
-
- protected
-
- def self.rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
- else
- ""
- end
- end
-
- # Given a Hash of options, generates a route
- def process_path_options(options)
- options = options.symbolize_keys
- options.update(options[:params].symbolize_keys) if options[:params]
-
- if (overwrite = options.delete(:overwrite_params))
- options.update(@parameters.symbolize_keys)
- options.update(overwrite.symbolize_keys)
- end
-
- options
- end
-
- end
-end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 479ea959e6..dfb8919561 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -48,6 +48,7 @@ module ActionDispatch
autoload :Flash
autoload :Head
autoload :ParamsParser
+ autoload :RemoteIp
autoload :Rescue
autoload :ShowExceptions
autoload :Static
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 428e62dc6b..d2404e63c5 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -37,8 +37,21 @@ module ActionDispatch
end
module Response
- def cache_control
- @cache_control ||= {}
+ attr_reader :cache_control
+
+ def initialize(*)
+ status, header, body = super
+
+ @cache_control = {}
+ @etag = self["ETag"]
+
+ if cache_control = self["Cache-Control"]
+ cache_control.split(/,\s*/).each do |segment|
+ first, last = segment.split("=")
+ last ||= true
+ @cache_control[first.to_sym] = last
+ end
+ end
end
def last_modified
@@ -65,7 +78,7 @@ module ActionDispatch
def etag=(etag)
key = ActiveSupport::Cache.expand_cache_key(etag)
- @etag = %("#{Digest::MD5.hexdigest(key)}")
+ @etag = self["ETag"] = %("#{Digest::MD5.hexdigest(key)}")
end
private
@@ -100,6 +113,8 @@ module ActionDispatch
def set_conditional_cache_control!
control = @cache_control
+ return if self["Cache-Control"].present?
+
if control.empty?
headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
elsif @cache_control[:no_cache]
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 1958e1668d..451b79b190 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -25,9 +25,16 @@ module ActionDispatch
module FilterParameters
extend ActiveSupport::Concern
+ mattr_reader :compiled_parameter_filter_for
+ @@compiled_parameter_filter_for = {}
+
# Return a hash of parameters with all sensitive data replaced.
def filtered_parameters
- @filtered_parameters ||= process_parameter_filter(parameters)
+ @filtered_parameters ||= if filtering_parameters?
+ process_parameter_filter(parameters)
+ else
+ parameters.dup
+ end
end
alias :fitered_params :filtered_parameters
@@ -46,10 +53,18 @@ module ActionDispatch
protected
- def compile_parameter_filter #:nodoc:
+ def filtering_parameters? #:nodoc:
+ @env["action_dispatch.parameter_filter"].present?
+ end
+
+ def process_parameter_filter(params) #:nodoc:
+ compiled_parameter_filter_for(@env["action_dispatch.parameter_filter"]).call(params)
+ end
+
+ def compile_parameter_filter(filters) #:nodoc:
strings, regexps, blocks = [], [], []
- Array(@env["action_dispatch.parameter_filter"]).each do |item|
+ filters.each do |item|
case item
when NilClass
when Proc
@@ -65,34 +80,34 @@ module ActionDispatch
[regexps, blocks]
end
- def filtering_parameters? #:nodoc:
- @env["action_dispatch.parameter_filter"].present?
- end
+ def compiled_parameter_filter_for(filters) #:nodoc:
+ @@compiled_parameter_filter_for[filters] ||= begin
+ regexps, blocks = compile_parameter_filter(filters)
- def process_parameter_filter(original_params) #:nodoc:
- return original_params.dup unless filtering_parameters?
+ lambda do |original_params|
+ filtered_params = {}
- filtered_params = {}
- regexps, blocks = compile_parameter_filter
+ original_params.each do |key, value|
+ if regexps.find { |r| key =~ r }
+ value = '[FILTERED]'
+ elsif value.is_a?(Hash)
+ value = process_parameter_filter(value)
+ elsif value.is_a?(Array)
+ value = value.map { |v| v.is_a?(Hash) ? process_parameter_filter(v) : v }
+ elsif blocks.present?
+ key = key.dup
+ value = value.dup if value.duplicable?
+ blocks.each { |b| b.call(key, value) }
+ end
- original_params.each do |key, value|
- if regexps.find { |r| key =~ r }
- value = '[FILTERED]'
- elsif value.is_a?(Hash)
- value = process_parameter_filter(value)
- elsif value.is_a?(Array)
- value = value.map { |i| process_parameter_filter(i) }
- elsif blocks.present?
- key = key.dup
- value = value.dup if value.duplicable?
- blocks.each { |b| b.call(key, value) }
- end
+ filtered_params[key] = value
+ end
- filtered_params[key] = value
+ filtered_params
+ end
end
-
- filtered_params
end
+
end
end
end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 40617e239a..fec250e928 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -67,21 +67,6 @@ module ActionDispatch
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
end
- # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
- # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
- # otherwise.
- def template_format
- parameter_format = parameters[:format]
-
- if parameter_format
- parameter_format
- elsif xhr?
- :js
- else
- :html
- end
- end
-
# Receives an array of mimes and return the first user sent mime that
# matches the order array.
#
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 7a17023ed2..ea9f0f99c2 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -30,6 +30,14 @@ module ActionDispatch
METHOD
end
+ def self.new(env)
+ if request = env["action_dispatch.request"] && request.instance_of?(self)
+ return request
+ end
+
+ super
+ end
+
def key?(key)
@env.key?(key)
end
@@ -119,36 +127,7 @@ module ActionDispatch
# delimited list in the case of multiple chained proxies; the last
# address which is not trusted is the originating IP.
def remote_ip
- remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].scan(/[^,\s]+/)
-
- unless remote_addr_list.blank?
- not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES || addr =~ ActionController::Base.trusted_proxies}
- return not_trusted_addrs.first unless not_trusted_addrs.empty?
- end
- remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',')
-
- if @env.include? 'HTTP_CLIENT_IP'
- if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
- # We don't know which came from the proxy, and which from the user
- raise ActionController::ActionControllerError.new <<EOM
-IP spoofing attack?!
-HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
-HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
-EOM
- end
-
- return @env['HTTP_CLIENT_IP']
- end
-
- if remote_ips
- while remote_ips.size > 1 && (TRUSTED_PROXIES =~ remote_ips.last.strip || ActionController::Base.trusted_proxies =~ remote_ips.last.strip)
- remote_ips.pop
- end
-
- return remote_ips.last.strip
- end
-
- @env['REMOTE_ADDR']
+ (@env["action_dispatch.remote_ip"] || ip).to_s
end
# Returns the lowercase name of the HTTP server software.
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index f299306ff4..9cfe5a5ea9 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -32,31 +32,38 @@ module ActionDispatch # :nodoc:
# end
# end
class Response < Rack::Response
- include ActionDispatch::Http::Cache::Response
-
attr_accessor :request, :blank
attr_writer :header, :sending_file
alias_method :headers=, :header=
- def initialize
- @status = 200
- @header = {}
- @cache_control = {}
+ module Setup
+ def initialize(status = 200, header = {}, body = [])
+ @writer = lambda { |x| @body << x }
+ @block = nil
+ @length = 0
- @writer = lambda { |x| @body << x }
- @block = nil
- @length = 0
+ @status, @header = status, header
+ self.body = body
- @body, @cookie = [], []
- @sending_file = false
+ @cookie = []
+ @sending_file = false
- @blank = false
- @etag = nil
+ @blank = false
+
+ if content_type = self["Content-Type"]
+ type, charset = content_type.split(/;\s*charset=/)
+ @content_type = Mime::Type.lookup(type)
+ @charset = charset || "UTF-8"
+ end
- yield self if block_given?
+ yield self if block_given?
+ end
end
+ include Setup
+ include ActionDispatch::Http::Cache::Response
+
def status=(status)
@status = Rack::Utils.status_code(status)
end
@@ -76,6 +83,18 @@ module ActionDispatch # :nodoc:
end
alias_method :status_message, :message
+ def respond_to?(method)
+ if method.to_sym == :to_path
+ @body.respond_to?(:to_path)
+ else
+ super
+ end
+ end
+
+ def to_path
+ @body.to_path
+ end
+
def body
str = ''
each { |part| str << part.to_s }
@@ -120,7 +139,7 @@ module ActionDispatch # :nodoc:
assign_default_content_type_and_charset!
handle_conditional_get!
self["Set-Cookie"] = @cookie.join("\n") unless @cookie.blank?
- self["ETag"] = @etag if @etag
+ self["ETag"] = @_etag if @_etag
super
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 40ceb5a9b6..b64a83c62e 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -3,7 +3,7 @@ module ActionDispatch
module URL
# Returns the complete URL used for this request.
def url
- protocol + host_with_port + request_uri
+ protocol + host_with_port + fullpath
end
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
@@ -81,42 +81,15 @@ module ActionDispatch
parts[0..-(tld_length+2)]
end
- # Returns the query string, accounting for server idiosyncrasies.
- def query_string
- @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
+ def subdomain(tld_length = 1)
+ subdomains(tld_length).join('.')
end
# Returns the request URI, accounting for server idiosyncrasies.
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
def request_uri
- if uri = @env['REQUEST_URI']
- # Remove domain, which webrick puts into the request_uri.
- (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
- else
- # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
- uri = @env['PATH_INFO'].to_s
-
- if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
- uri = uri.sub(/#{script_filename}\//, '')
- end
-
- env_qs = @env['QUERY_STRING'].to_s
- uri += "?#{env_qs}" unless env_qs.empty?
-
- if uri.blank?
- @env.delete('REQUEST_URI')
- else
- @env['REQUEST_URI'] = uri
- end
- end
- end
-
- # Returns the interpreted \path to requested resource after all the installation
- # directory of this application was taken into account.
- def path
- path = request_uri.to_s[/\A[^\?]*/]
- path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
- path
+ ActiveSupport::Deprecation.warn "Using #request_uri is deprecated. Use fullpath instead.", caller
+ fullpath
end
private
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 7cf75ffe63..d07841218a 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -37,7 +37,7 @@ module ActionDispatch
def initialize(app, prepare_each_request = false)
@app, @prepare_each_request = app, prepare_each_request
- run_callbacks(:prepare) unless @prepare_each_request
+ run_callbacks(:prepare)
end
def call(env)
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 522982e202..f4c4324fb0 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -1,4 +1,3 @@
-require 'active_support/json'
require 'action_dispatch/http/request'
module ActionDispatch
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
new file mode 100644
index 0000000000..c7d710b98e
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -0,0 +1,51 @@
+module ActionDispatch
+ class RemoteIp
+ class IpSpoofAttackError < StandardError ; end
+
+ class RemoteIpGetter
+ def initialize(env, check_ip_spoofing, trusted_proxies)
+ @env = env
+ @check_ip_spoofing = check_ip_spoofing
+ @trusted_proxies = trusted_proxies
+ end
+
+ def remote_addrs
+ @remote_addrs ||= begin
+ list = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
+ list.reject { |addr| addr =~ @trusted_proxies }
+ end
+ end
+
+ def to_s
+ return remote_addrs.first if remote_addrs.any?
+
+ forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
+
+ if client_ip = @env['HTTP_CLIENT_IP']
+ if @check_ip_spoofing && !forwarded_ips.include?(client_ip)
+ # We don't know which came from the proxy, and which from the user
+ raise IpSpoofAttackError, "IP spoofing attack?!" \
+ "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
+ "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
+ end
+ return client_ip
+ end
+
+ return forwarded_ips.reject { |ip| ip =~ @trusted_proxies }.last || @env["REMOTE_ADDR"]
+ end
+ end
+
+ def initialize(app, check_ip_spoofing = true, trusted_proxies = nil)
+ @app = app
+ @check_ip_spoofing = check_ip_spoofing
+ regex = '(^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.)'
+ regex << "|(#{trusted_proxies})" if trusted_proxies
+ @trusted_proxies = Regexp.new(regex, "i")
+ end
+
+ def call(env)
+ env["action_dispatch.remote_ip"] = RemoteIpGetter.new(env, @check_ip_spoofing, @trusted_proxies)
+ @app.call(env)
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 04a101dbb2..22da82479e 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/hash/keys'
-require 'rack/request'
module ActionDispatch
module Session
@@ -177,9 +176,8 @@ module ActionDispatch
if key.blank?
raise ArgumentError, 'A key is required to write a ' +
'cookie containing the session data. Use ' +
- 'config.action_controller.session = { :key => ' +
- '"_myapp_session", :secret => "some secret phrase" } in ' +
- 'config/application.rb'
+ 'config.action_controller.session_store :cookie_store, { :key => ' +
+ '"_myapp_session" } in config/application.rb'
end
end
@@ -193,10 +191,9 @@ module ActionDispatch
if secret.blank?
raise ArgumentError, "A secret is required to generate an " +
"integrity hash for cookie session data. Use " +
- "config.action_controller.session = { :key => " +
- "\"_myapp_session\", :secret => \"some secret phrase of at " +
- "least #{SECRET_MIN_LENGTH} characters\" } " +
- "in config/environment.rb"
+ "config.cookie_secret = \"some secret phrase of at " +
+ "least #{SECRET_MIN_LENGTH} characters\"" +
+ "in config/application.rb"
end
if secret.length < SECRET_MIN_LENGTH
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index 18a2922fa7..5c5362ce4a 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -58,7 +58,7 @@ module ActionDispatch
if lazy_compare?(@klass) && lazy_compare?(middleware)
normalize(@klass) == normalize(middleware)
else
- klass == ActiveSupport::Inflector.constantize(middleware.to_s)
+ klass.name == middleware.to_s
end
end
end
@@ -122,7 +122,11 @@ module ActionDispatch
find_all { |middleware| middleware.active? }
end
- def build(app)
+ def build(app = nil, &blk)
+ app ||= blk
+
+ raise "MiddlewareStack#build requires an app" unless app
+
active.reverse.inject(app) { |a, e| e.build(a) }
end
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 335daafc01..e486bd4079 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -5,6 +5,9 @@ module ActionDispatch
class Railtie < Rails::Railtie
railtie_name :action_dispatch
+ config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ config.action_dispatch.ip_spoofing_check = true
+
# Prepare dispatcher callbacks and run 'prepare' callbacks
initializer "action_dispatch.prepare_dispatcher" do |app|
# TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 335c9edb98..5bc3205c51 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -205,6 +205,7 @@ module ActionDispatch
autoload :Mapper, 'action_dispatch/routing/mapper'
autoload :Route, 'action_dispatch/routing/route'
autoload :RouteSet, 'action_dispatch/routing/route_set'
+ autoload :UrlFor, 'action_dispatch/routing/url_for'
SEPARATORS = %w( / . ? )
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
index 8ce6b2f6d5..dd650e83d9 100644
--- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
@@ -1,5 +1,30 @@
module ActionDispatch
module Routing
+ class RouteSet
+ attr_accessor :controller_namespaces
+
+ CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/
+
+ def controller_constraints
+ @controller_constraints ||= begin
+ namespaces = controller_namespaces + in_memory_controller_namespaces
+ source = namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" }
+ source << CONTROLLER_REGEXP.source
+ Regexp.compile(source.sort.reverse.join('|'))
+ end
+ end
+
+ def in_memory_controller_namespaces
+ namespaces = Set.new
+ ActionController::Base.subclasses.each do |klass|
+ controller_name = klass.underscore
+ namespaces << controller_name.split('/')[0...-1].join('/')
+ end
+ namespaces.delete('')
+ namespaces
+ end
+ end
+
# Mapper instances are used to build routes. The object passed to the draw
# block in config/routes.rb is a Mapper instance.
#
@@ -244,14 +269,15 @@ module ActionDispatch
attr_reader :collection_methods, :member_methods, :new_methods
attr_reader :path_prefix, :name_prefix, :path_segment
attr_reader :plural, :singular
- attr_reader :options
+ attr_reader :options, :defaults
- def initialize(entities, options)
+ def initialize(entities, options, defaults)
@plural ||= entities
@singular ||= options[:singular] || plural.to_s.singularize
@path_segment = options.delete(:as) || @plural
@options = options
+ @defaults = defaults
arrange_actions
add_default_actions
@@ -280,7 +306,7 @@ module ActionDispatch
def new_path
new_action = self.options[:path_names][:new] if self.options[:path_names]
- new_action ||= ActionController::Base.resources_path_names[:new]
+ new_action ||= self.defaults[:path_names][:new]
@new_path ||= "#{path}/#{new_action}"
end
@@ -370,7 +396,7 @@ module ActionDispatch
end
class SingletonResource < Resource #:nodoc:
- def initialize(entity, options)
+ def initialize(entity, options, defaults)
@singular = @plural = entity
options[:controller] ||= @singular.to_s.pluralize
super
@@ -717,7 +743,7 @@ module ActionDispatch
private
def map_resource(entities, options = {}, &block)
- resource = Resource.new(entities, options)
+ resource = Resource.new(entities, options, :path_names => @set.resources_path_names)
with_options :controller => resource.controller do |map|
map_associations(resource, options)
@@ -734,7 +760,7 @@ module ActionDispatch
end
def map_singleton_resource(entities, options = {}, &block)
- resource = SingletonResource.new(entities, options)
+ resource = SingletonResource.new(entities, options, :path_names => @set.resources_path_names)
with_options :controller => resource.controller do |map|
map_associations(resource, options)
@@ -826,7 +852,7 @@ module ActionDispatch
actions.each do |action|
[method].flatten.each do |m|
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
- action_path ||= ActionController::Base.resources_path_names[action] || action
+ action_path ||= @set.resources_path_names[action] || action
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 8de68b3174..0b7b09ee7a 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/except'
+
module ActionDispatch
module Routing
class Mapper
@@ -36,7 +38,7 @@ module ActionDispatch
end
def to_route
- [ app, conditions, requirements, defaults, @options[:as] ]
+ [ app, conditions, requirements, defaults, @options[:as], @options[:anchor] ]
end
private
@@ -64,7 +66,7 @@ module ActionDispatch
# match "account/overview"
def using_match_shorthand?(args, options)
- args.present? && options.except(:via).empty? && !args.first.include?(':')
+ args.present? && options.except(:via, :anchor).empty? && !args.first.include?(':')
end
def normalize_path(path)
@@ -85,10 +87,9 @@ module ActionDispatch
end
def requirements
- @requirements ||= returning(@options[:constraints] || {}) do |requirements|
+ @requirements ||= (@options[:constraints] || {}).tap do |requirements|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
- requirements[:controller] ||= @set.controller_constraints
end
end
@@ -175,9 +176,15 @@ module ActionDispatch
end
def match(*args)
- @set.add_route(*Mapping.new(@set, @scope, args).to_route)
+ mapping = Mapping.new(@set, @scope, args).to_route
+ @set.add_route(*mapping)
self
end
+
+ def default_url_options=(options)
+ @set.default_url_options = options
+ end
+ alias_method :default_url_options, :default_url_options=
end
module HttpHelpers
@@ -283,7 +290,7 @@ module ActionDispatch
end
def namespace(path)
- scope(path.to_s, :name_prefix => path.to_s, :namespace => path.to_s) { yield }
+ scope(path.to_s, :name_prefix => path.to_s, :controller_namespace => path.to_s) { yield }
end
def constraints(constraints = {})
@@ -294,6 +301,7 @@ module ActionDispatch
options = args.extract_options!
options = (@scope[:options] || {}).merge(options)
+ options[:anchor] = true unless options.key?(:anchor)
if @scope[:name_prefix] && !options[:as].blank?
options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}"
@@ -318,12 +326,12 @@ module ActionDispatch
parent ? "#{parent}_#{child}" : child
end
- def merge_namespace_scope(parent, child)
+ def merge_controller_namespace_scope(parent, child)
parent ? "#{parent}/#{child}" : child
end
def merge_controller_scope(parent, child)
- @scope[:namespace] ? "#{@scope[:namespace]}/#{child}" : child
+ @scope[:controller_namespace] ? "#{@scope[:controller_namespace]}/#{child}" : child
end
def merge_resources_path_names_scope(parent, child)
@@ -367,9 +375,9 @@ module ActionDispatch
def actions
if only = options[:only]
- only.map(&:to_sym)
+ Array(only).map(&:to_sym)
elsif except = options[:except]
- default_actions - except.map(&:to_sym)
+ default_actions - Array(except).map(&:to_sym)
else
default_actions
end
@@ -443,7 +451,7 @@ module ActionDispatch
def resource(*resources, &block)
options = resources.extract_options!
- if verify_common_behavior_for(:resource, resources, options, &block)
+ if apply_common_behavior_for(:resource, resources, options, &block)
return self
end
@@ -451,7 +459,10 @@ module ActionDispatch
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resource, resource) do
- yield if block_given?
+
+ scope(:name_prefix => resource.name.to_s) do
+ yield if block_given?
+ end
get :show if resource.actions.include?(:show)
post :create if resource.actions.include?(:create)
@@ -468,7 +479,7 @@ module ActionDispatch
def resources(*resources, &block)
options = resources.extract_options!
- if verify_common_behavior_for(:resources, resources, options, &block)
+ if apply_common_behavior_for(:resources, resources, options, &block)
return self
end
@@ -534,6 +545,21 @@ module ActionDispatch
end
end
+ def mount(app, options = nil)
+ if options
+ path = options.delete(:at)
+ else
+ options = app
+ app, path = options.find { |k, v| k.respond_to?(:call) }
+ options.delete(app) if app
+ end
+
+ raise "A rack application must be specified" unless path
+
+ match(path, options.merge(:to => app, :anchor => false))
+ self
+ end
+
def match(*args)
options = args.extract_options!
@@ -591,7 +617,7 @@ module ActionDispatch
path_names[name.to_sym] || name.to_s
end
- def verify_common_behavior_for(method, resources, options, &block)
+ def apply_common_behavior_for(method, resources, options, &block)
if resources.length > 1
resources.each { |r| send(method, r, options, &block) }
return true
diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb
index e6e44d3546..6f37eadd6b 100644
--- a/actionpack/lib/action_dispatch/routing/route.rb
+++ b/actionpack/lib/action_dispatch/routing/route.rb
@@ -4,7 +4,7 @@ module ActionDispatch
attr_reader :app, :conditions, :defaults, :name
attr_reader :path, :requirements
- def initialize(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
+ def initialize(app, conditions, requirements, defaults, name, anchor)
@app = app
@defaults = defaults
@name = name
@@ -17,7 +17,7 @@ module ActionDispatch
if path = conditions[:path_info]
@path = path
- conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS)
+ conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor)
end
@conditions = conditions.inject({}) { |h, (k, v)|
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index dcf98b729b..722be432c7 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,5 +1,6 @@
require 'rack/mount'
require 'forwardable'
+require 'action_dispatch/routing/deprecated_mapper'
module ActionDispatch
module Routing
@@ -11,8 +12,8 @@ module ActionDispatch
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
class Dispatcher
- def initialize(options = {})
- defaults = options[:defaults]
+ def initialize(options={})
+ @defaults = options[:defaults]
@glob_param = options.delete(:glob)
end
@@ -20,7 +21,8 @@ module ActionDispatch
params = env[PARAMETERS_KEY]
prepare_params!(params)
- unless controller = controller(params)
+ # Just raise undefined constant errors if a controller was specified as default.
+ unless controller = controller(params, @defaults.key?(:controller))
return [404, {'X-Cascade' => 'pass'}, []]
end
@@ -39,14 +41,13 @@ module ActionDispatch
end
end
- def controller(params)
+ def controller(params, raise_error=true)
if params && params.has_key?(:controller)
controller = "#{params[:controller].camelize}Controller"
ActiveSupport::Inflector.constantize(controller)
end
rescue NameError => e
- raise unless e.message.include?(controller)
- nil
+ raise ActionController::RoutingError, e.message, e.backtrace if raise_error
end
private
@@ -59,7 +60,6 @@ module ActionDispatch
end
end
-
# A NamedRouteCollection instance is a collection of named routes, and also
# maintains an anonymous module that can be used to install helpers for the
# named routes.
@@ -168,63 +168,25 @@ module ActionDispatch
selector = url_helper_name(name, kind)
hash_access_method = hash_access_name(name, kind)
- # We use module_eval to avoid leaks.
- #
- # def users_url(*args)
- # if args.empty? || Hash === args.first
- # options = hash_for_users_url(args.first || {})
- # else
- # options = hash_for_users_url(args.extract_options!)
- # default = default_url_options(options) if self.respond_to?(:default_url_options, true)
- # options = (default ||= {}).merge(options)
- #
- # keys = []
- # keys -= options.keys if args.size < keys.size - 1
- #
- # args = args.zip(keys).inject({}) do |h, (v, k)|
- # h[k] = v
- # h
- # end
- #
- # # Tell url_for to skip default_url_options
- # options[:use_defaults] = false
- # options.merge!(args)
- # end
- #
- # url_for(options)
- # end
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
def #{selector}(*args)
- if args.empty? || Hash === args.first
- options = #{hash_access_method}(args.first || {})
- else
- options = #{hash_access_method}(args.extract_options!)
- default = default_url_options(options) if self.respond_to?(:default_url_options, true)
- options = (default ||= {}).merge(options)
-
- keys = #{route.segment_keys.inspect}
- keys -= options.keys if args.size < keys.size - 1 # take format into account
-
- args = args.zip(keys).inject({}) do |h, (v, k)|
- h[k] = v
- h
- end
-
- # Tell url_for to skip default_url_options
- options[:use_defaults] = false
- options.merge!(args)
+ options = #{hash_access_method}(args.extract_options!)
+
+ if args.any?
+ options[:_positional_args] = args
+ options[:_positional_keys] = #{route.segment_keys.inspect}
end
url_for(options)
end
- protected :#{selector}
END_EVAL
helpers << selector
end
end
- attr_accessor :routes, :named_routes, :controller_namespaces
+ attr_accessor :routes, :named_routes
attr_accessor :disable_clear_and_finalize, :resources_path_names
+ attr_accessor :default_url_options
def self.default_resources_path_names
{ :new => 'new', :edit => 'edit' }
@@ -235,8 +197,10 @@ module ActionDispatch
self.named_routes = NamedRouteCollection.new
self.resources_path_names = self.class.default_resources_path_names.dup
self.controller_namespaces = Set.new
+ self.default_url_options = {}
@disable_clear_and_finalize = false
+ clear!
end
def draw(&block)
@@ -273,61 +237,155 @@ module ActionDispatch
named_routes.install(destinations, regenerate_code)
end
- def empty?
- routes.empty?
- end
+ def url_helpers
+ @url_helpers ||= begin
+ router = self
- CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/
+ Module.new do
+ extend ActiveSupport::Concern
+ include UrlFor
- def controller_constraints
- @controller_constraints ||= begin
- namespaces = controller_namespaces + in_memory_controller_namespaces
- source = namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" }
- source << CONTROLLER_REGEXP.source
- Regexp.compile(source.sort.reverse.join('|'))
+ # ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
+ # we can include?
+ # Yes plz - JP
+ included do
+ router.install_helpers(self)
+ end
+
+ define_method(:_router) { router }
+ end
end
end
- def in_memory_controller_namespaces
- namespaces = Set.new
- ActionController::Base.subclasses.each do |klass|
- controller_name = klass.underscore
- namespaces << controller_name.split('/')[0...-1].join('/')
- end
- namespaces.delete('')
- namespaces
+ def empty?
+ routes.empty?
end
- def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
- route = Route.new(app, conditions, requirements, defaults, name)
+ def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
+ route = Route.new(app, conditions, requirements, defaults, name, anchor)
@set.add_route(*route)
named_routes[name] = route if name
routes << route
route
end
- def options_as_params(options)
- # If an explicit :controller was given, always make :action explicit
- # too, so that action expiry works as expected for things like
- #
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
- #
- # (the above is from the unit tests). In the above case, because the
- # controller was explicitly given, but no action, the action is implied to
- # be "index", not the recalled action of "show".
- #
- # great fun, eh?
-
- options_as_params = options.clone
- options_as_params[:action] ||= 'index' if options[:controller]
- options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
- options_as_params
- end
+ class Generator
+ attr_reader :options, :recall, :set, :script_name, :named_route
+
+ def initialize(options, recall, set, extras = false)
+ @script_name = options.delete(:script_name)
+ @named_route = options.delete(:use_route)
+ @options = options.dup
+ @recall = recall.dup
+ @set = set
+ @extras = extras
+
+ normalize_options!
+ normalize_controller_action_id!
+ use_relative_controller!
+ controller.sub!(%r{^/}, '') if controller
+ handle_nil_action!
+ end
+
+ def controller
+ @controller ||= @options[:controller]
+ end
+
+ def current_controller
+ @recall[:controller]
+ end
+
+ def use_recall_for(key)
+ if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
+ @options[key] = @recall.delete(key)
+ end
+ end
+
+ def normalize_options!
+ # If an explicit :controller was given, always make :action explicit
+ # too, so that action expiry works as expected for things like
+ #
+ # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
+ #
+ # (the above is from the unit tests). In the above case, because the
+ # controller was explicitly given, but no action, the action is implied to
+ # be "index", not the recalled action of "show".
+
+ if options[:controller]
+ options[:action] ||= 'index'
+ options[:controller] = options[:controller].to_s
+ end
+
+ if options[:action]
+ options[:action] = options[:action].to_s
+ end
+ end
+
+ # This pulls :controller, :action, and :id out of the recall.
+ # The recall key is only used if there is no key in the options
+ # or if the key in the options is identical. If any of
+ # :controller, :action or :id is not found, don't pull any
+ # more keys from the recall.
+ def normalize_controller_action_id!
+ @recall[:action] ||= 'index' if current_controller
+
+ use_recall_for(:controller) or return
+ use_recall_for(:action) or return
+ use_recall_for(:id)
+ end
+
+ # if the current controller is "foo/bar/baz" and :controller => "baz/bat"
+ # is specified, the controller becomes "foo/baz/bat"
+ def use_relative_controller!
+ if !named_route && different_controller?
+ old_parts = current_controller.split('/')
+ size = controller.count("/") + 1
+ parts = old_parts[0...-size] << controller
+ @controller = @options[:controller] = parts.join("/")
+ end
+ end
+
+ # This handles the case of :action => nil being explicitly passed.
+ # It is identical to :action => "index"
+ def handle_nil_action!
+ if options.has_key?(:action) && options[:action].nil?
+ options[:action] = 'index'
+ end
+ recall[:action] = options.delete(:action) if options[:action] == 'index'
+ end
- def build_expiry(options, recall)
- recall.inject({}) do |expiry, (key, recalled_value)|
- expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param)
- expiry
+ def generate
+ error = ActionController::RoutingError.new("No route matches #{options.inspect}")
+ path, params = @set.generate(:path_info, named_route, options, recall, opts)
+
+ raise error unless path
+
+ params.reject! {|k,v| !v }
+
+ return [path, params.keys] if @extras
+
+ path << "?#{params.to_query}" if params.any?
+ "#{script_name}#{path}"
+ rescue Rack::Mount::RoutingError
+ raise error
+ end
+
+ def opts
+ parameterize = lambda do |name, value|
+ if name == :controller
+ value
+ elsif value.is_a?(Array)
+ value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
+ else
+ Rack::Mount::Utils.escape_uri(value.to_param)
+ end
+ end
+ {:parameterize => parameterize}
+ end
+
+ def different_controller?
+ return false unless current_controller
+ controller.to_param != current_controller.to_param
end
end
@@ -338,82 +396,44 @@ module ActionDispatch
end
def generate_extras(options, recall={})
- generate(options, recall, :generate_extras)
+ generate(options, recall, true)
end
- def generate(options, recall = {}, method = :generate)
- options, recall = options.dup, recall.dup
- named_route = options.delete(:use_route)
+ def generate(options, recall = {}, extras = false)
+ Generator.new(options, recall, @set, extras).generate
+ end
- options = options_as_params(options)
- expire_on = build_expiry(options, recall)
+ RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash]
- recall[:action] ||= 'index' if options[:controller] || recall[:controller]
+ def url_for(options)
+ options = default_url_options.merge(options || {})
- if recall[:controller] && (!options.has_key?(:controller) || options[:controller] == recall[:controller])
- options[:controller] = recall.delete(:controller)
+ handle_positional_args(options)
- if recall[:action] && (!options.has_key?(:action) || options[:action] == recall[:action])
- options[:action] = recall.delete(:action)
+ rewritten_url = ""
- if recall[:id] && (!options.has_key?(:id) || options[:id] == recall[:id])
- options[:id] = recall.delete(:id)
- end
- end
- end
-
- options[:controller] = options[:controller].to_s if options[:controller]
+ path_segments = options.delete(:_path_segments)
- if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
- old_parts = recall[:controller].split('/')
- new_parts = options[:controller].split('/')
- parts = old_parts[0..-(new_parts.length + 1)] + new_parts
- options[:controller] = parts.join('/')
- end
+ unless options[:only_path]
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << "://" unless rewritten_url.match("://")
+ rewritten_url << rewrite_authentication(options)
- options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
+ raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
- merged = options.merge(recall)
- if options.has_key?(:action) && options[:action].nil?
- options.delete(:action)
- recall[:action] = 'index'
+ rewritten_url << options[:host]
+ rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
end
- recall[:action] = options.delete(:action) if options[:action] == 'index'
-
- opts = {}
- opts[:parameterize] = lambda { |name, value|
- if name == :controller
- value
- elsif value.is_a?(Array)
- value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
- else
- Rack::Mount::Utils.escape_uri(value.to_param)
- end
- }
- unless result = @set.generate(:path_info, named_route, options, recall, opts)
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
- end
+ path_options = options.except(*RESERVED_OPTIONS)
+ path_options = yield(path_options) if block_given?
+ path = generate(path_options, path_segments || {})
- path, params = result
- params.each do |k, v|
- if v
- params[k] = v
- else
- params.delete(k)
- end
- end
+ # ROUTES TODO: This can be called directly, so script_name should probably be set in the router
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
- if path && method == :generate_extras
- [path, params.keys]
- elsif path
- path << "?#{params.to_query}" if params.any?
- path
- else
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
- end
- rescue Rack::Mount::RoutingError
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
+ rewritten_url
end
def call(env)
@@ -431,9 +451,9 @@ module ActionDispatch
end
req = Rack::Request.new(env)
- @set.recognize(req) do |route, params|
+ @set.recognize(req) do |route, matches, params|
dispatcher = route.app
- if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params)
+ if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
dispatcher.prepare_params!(params)
return params
end
@@ -441,6 +461,30 @@ module ActionDispatch
raise ActionController::RoutingError, "No route matches #{path.inspect}"
end
+
+ private
+ def handle_positional_args(options)
+ return unless args = options.delete(:_positional_args)
+
+ keys = options.delete(:_positional_keys)
+ keys -= options.keys if args.size < keys.size - 1 # take format into account
+
+ args = args.zip(keys).inject({}) do |h, (v, k)|
+ h[k] = v
+ h
+ end
+
+ # Tell url_for to skip default_url_options
+ options.merge!(args)
+ end
+
+ def rewrite_authentication(options)
+ if options[:user] && options[:password]
+ "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
+ else
+ ""
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
new file mode 100644
index 0000000000..ec78f53fa6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -0,0 +1,139 @@
+module ActionDispatch
+ module Routing
+ # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
+ # is also possible: an URL can be generated from one of your routing definitions.
+ # URL generation functionality is centralized in this module.
+ #
+ # See ActionDispatch::Routing and ActionController::Resources for general
+ # information about routing and routes.rb.
+ #
+ # <b>Tip:</b> If you need to generate URLs from your models or some other place,
+ # then ActionController::UrlFor is what you're looking for. Read on for
+ # an introduction.
+ #
+ # == URL generation from parameters
+ #
+ # As you may know, some functions - such as ActionController::Base#url_for
+ # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
+ # of parameters. For example, you've probably had the chance to write code
+ # like this in one of your views:
+ #
+ # <%= link_to('Click here', :controller => 'users',
+ # :action => 'new', :message => 'Welcome!') %>
+ #
+ # #=> Generates a link to: /users/new?message=Welcome%21
+ #
+ # link_to, and all other functions that require URL generation functionality,
+ # actually use ActionController::UrlFor under the hood. And in particular,
+ # they use the ActionController::UrlFor#url_for method. One can generate
+ # the same path as the above example by using the following code:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :only_path => true)
+ # # => "/users/new?message=Welcome%21"
+ #
+ # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
+ # information about the website hostname that your Rails app is serving. So if you
+ # want to include the hostname as well, then you must also pass the <tt>:host</tt>
+ # argument:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :host => 'www.example.com') # Changed this.
+ # # => "http://www.example.com/users/new?message=Welcome%21"
+ #
+ # By default, all controllers and views have access to a special version of url_for,
+ # that already knows what the current hostname is. So if you use url_for in your
+ # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
+ # argument.
+ #
+ # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
+ # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
+ # in full. However, mailers don't have hostname information, and what's why you'll still
+ # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
+ #
+ #
+ # == URL generation for named routes
+ #
+ # UrlFor also allows one to access methods that have been auto-generated from
+ # named routes. For example, suppose that you have a 'users' resource in your
+ # <b>routes.rb</b>:
+ #
+ # map.resources :users
+ #
+ # This generates, among other things, the method <tt>users_path</tt>. By default,
+ # this method is accessible from your controllers, views and mailers. If you need
+ # to access this auto-generated method from other places (such as a model), then
+ # you can do that by including ActionController::UrlFor in your class:
+ #
+ # class User < ActiveRecord::Base
+ # include ActionController::UrlFor
+ #
+ # def base_uri
+ # user_path(self)
+ # end
+ # end
+ #
+ # User.find(1).base_uri # => "/users/1"
+ #
+ module UrlFor
+ extend ActiveSupport::Concern
+
+ included do
+ # TODO: with_routing extends @controller with url_helpers, trickling down to including this module which overrides its default_url_options
+ unless method_defined?(:default_url_options)
+ # Including in a class uses an inheritable hash. Modules get a plain hash.
+ if respond_to?(:class_attribute)
+ class_attribute :default_url_options
+ else
+ mattr_accessor :default_url_options
+ remove_method :default_url_options
+ end
+
+ self.default_url_options = {}
+ end
+ end
+
+ def url_options
+ default_url_options
+ end
+
+ # Generate a url based on the options provided, default_url_options and the
+ # routes defined in routes.rb. The following options are supported:
+ #
+ # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+ # * <tt>:host</tt> - Specifies the host the link should be targeted at.
+ # If <tt>:only_path</tt> is false, this option must be
+ # provided either explicitly, or via +default_url_options+.
+ # * <tt>:port</tt> - Optionally specify the port to connect to.
+ # * <tt>:anchor</tt> - An anchor name to be appended to the path.
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
+ #
+ # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
+ # +url_for+ is forwarded to the Routes module.
+ #
+ # Examples:
+ #
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
+ # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
+ def url_for(options = nil)
+ case options
+ when String
+ options
+ when nil, Hash
+ _router.url_for(url_options.merge(options || {}))
+ else
+ polymorphic_url(options)
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index c2486d3730..937c9f48d2 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -132,16 +132,21 @@ module ActionDispatch
end
def normalize_argument_to_redirection(fragment)
- after_routing = @controller.url_for(fragment)
- if after_routing =~ %r{^\w+://.*}
- after_routing
- else
- # FIXME - this should probably get removed.
- if after_routing.first != '/'
- after_routing = '/' + after_routing
+ case fragment
+ when %r{^\w[\w\d+.-]*:.*}
+ fragment
+ when String
+ if fragment =~ %r{^\w[\w\d+.-]*:.*}
+ fragment
+ else
+ @request.protocol + @request.host_with_port + fragment
end
- @request.protocol + @request.host_with_port + after_routing
- end
+ when :back
+ raise RedirectBackError unless refer = @request.headers["Referer"]
+ refer
+ else
+ @controller.url_for(fragment)
+ end.gsub(/[\r\n]/, '')
end
def validate_request!
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 0c33539b4a..1d7e8090e4 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -80,7 +80,7 @@ module ActionDispatch
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
# Load routes.rb if it hasn't been loaded.
- generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
+ generated_path, extra_keys = @router.generate_extras(options, defaults)
found_extras = options.reject {|k, v| ! extra_keys.include? k}
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
@@ -125,7 +125,7 @@ module ActionDispatch
end
# A helper to make it easier to test different route configurations.
- # This method temporarily replaces ActionController::Routing::Routes
+ # This method temporarily replaces @router
# with a new RouteSet instance.
#
# The new instance is yielded to the passed block. Typically the block
@@ -142,22 +142,19 @@ module ActionDispatch
# end
#
def with_routing
- real_routes = ActionController::Routing::Routes
- ActionController::Routing.module_eval { remove_const :Routes }
-
- temporary_routes = ActionController::Routing::RouteSet.new
- ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
-
- yield temporary_routes
+ old_routes, @router = @router, ActionDispatch::Routing::RouteSet.new
+ old_controller, @controller = @controller, @controller.clone if @controller
+ _router = @router
+ @controller.singleton_class.send(:send, :include, @router.url_helpers) if @controller
+ yield @router
ensure
- if ActionController::Routing.const_defined? :Routes
- ActionController::Routing.module_eval { remove_const :Routes }
- end
- ActionController::Routing.const_set(:Routes, real_routes) if real_routes
+ @router = old_routes
+ @controller = old_controller if @controller
end
+ # ROUTES TODO: These assertions should really work in an integration context
def method_missing(selector, *args, &block)
- if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+ if @controller && @router.named_routes.helpers.include?(selector)
@controller.send(selector, *args, &block)
else
super
@@ -174,7 +171,7 @@ module ActionDispatch
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
request.path = path
- params = ActionController::Routing::Routes.recognize_path(path, { :method => request.method })
+ params = @router.recognize_path(path, { :method => request.method })
request.path_parameters = params.with_indifferent_access
request
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 2093bb3a0e..0aff4250c1 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,6 +1,6 @@
require 'stringio'
require 'uri'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'rack/test'
module ActionDispatch
@@ -162,12 +162,31 @@ module ActionDispatch
# A running counter of the number of requests processed.
attr_accessor :request_count
+ include ActionDispatch::Routing::UrlFor
+
# Create and initialize a new Session instance.
def initialize(app)
@app = app
+
+ # If the app is a Rails app, make url_helpers available on the session
+ # This makes app.url_for and app.foo_path available in the console
+ if app.respond_to?(:routes) && app.routes.respond_to?(:url_helpers)
+ singleton_class.class_eval { include app.routes.url_helpers }
+ end
+
reset!
end
+ def url_options
+ opts = super.reverse_merge(
+ :host => host,
+ :protocol => https? ? "https" : "http"
+ )
+
+ opts.merge!(:port => 443) if !opts.key?(:port) && https?
+ opts
+ end
+
# Resets the instance. This can be used to reset the state information
# in an existing session instance, so it can be used from a clean-slate
# condition.
@@ -187,12 +206,10 @@ module ActionDispatch
unless defined? @named_routes_configured
# install the named routes in this session instance.
- klass = metaclass
- ActionController::Routing::Routes.install_helpers(klass)
+ klass = singleton_class
# the helpers are made protected by default--we make them public for
# easier access during testing and troubleshooting.
- klass.module_eval { public *ActionController::Routing::Routes.named_routes.helpers }
@named_routes_configured = true
end
end
@@ -221,14 +238,6 @@ module ActionDispatch
@host = name
end
- # Returns the URL for the given options, according to the rules specified
- # in the application's routes.
- def url_for(options)
- controller ?
- controller.url_for(options) :
- generic_url_rewriter.rewrite(options)
- end
-
private
# Performs the actual request.
@@ -273,26 +282,14 @@ module ActionDispatch
@request_count += 1
@request = ActionDispatch::Request.new(session.last_request.env)
- @response = ActionDispatch::TestResponse.from_response(@mock_session.last_response)
+ response = @mock_session.last_response
+ @response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
@html_document = nil
@controller = session.last_request.env['action_controller.instance']
return response.status
end
-
- # Get a temporary URL writer object
- def generic_url_rewriter
- env = {
- 'REQUEST_METHOD' => "GET",
- 'QUERY_STRING' => "",
- "REQUEST_URI" => "/",
- "HTTP_HOST" => host,
- "SERVER_PORT" => https? ? "443" : "80",
- "HTTPS" => https? ? "on" : "off"
- }
- ActionController::UrlRewriter.new(ActionDispatch::Request.new(env), {})
- end
end
module Runner
@@ -364,6 +361,14 @@ module ActionDispatch
end
end
+ extend ActiveSupport::Concern
+ include ActionDispatch::Routing::UrlFor
+
+ def url_options
+ reset! unless @integration_session
+ @integration_session.url_options
+ end
+
# Delegate unhandled messages to the current session instance.
def method_missing(sym, *args, &block)
reset! unless @integration_session
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index eae703e1b6..d4eecac2de 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -1,3 +1,5 @@
+require 'action_dispatch/middleware/flash'
+
module ActionDispatch
module TestProcess
def assigns(key = nil)
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 67f89e1627..72a1cd30ad 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -1,9 +1,10 @@
-module ActionPack #:nodoc:
+module ActionPack
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index f5035fe45a..afe6386105 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -37,15 +37,18 @@ module ActionView
autoload :Helpers
autoload_under "render" do
+ autoload :Layouts
autoload :Partials
autoload :Rendering
end
- autoload :MissingTemplate, 'action_view/base'
- autoload :Resolver, 'action_view/template/resolver'
- autoload :PathResolver, 'action_view/template/resolver'
- autoload :PathSet, 'action_view/paths'
- autoload :FileSystemResolverWithFallback, 'action_view/template/resolver'
+ autoload :Base
+ autoload :LookupContext
+ autoload :MissingTemplate, 'action_view/base'
+ autoload :Resolver, 'action_view/template/resolver'
+ autoload :PathResolver, 'action_view/template/resolver'
+ autoload :FileSystemResolver, 'action_view/template/resolver'
+ autoload :PathSet, 'action_view/paths'
autoload :TemplateError, 'action_view/template/error'
autoload :TemplateHandler, 'action_view/template'
@@ -55,7 +58,7 @@ module ActionView
autoload :TestCase, 'action_view/test_case'
end
+require 'active_support/i18n'
require 'active_support/core_ext/string/output_safety'
-require 'action_view/base'
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 4096c296c3..ffe3060404 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -11,7 +11,7 @@ module ActionView #:nodoc:
def initialize(paths, path, details, partial)
@path = path
- display_paths = paths.compact.join(":")
+ display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
template_type = if partial
"partial"
elsif path =~ /layouts/i
@@ -20,7 +20,7 @@ module ActionView #:nodoc:
'template'
end
- super("Missing #{template_type} #{path} with #{details.inspect} in view path #{display_paths}")
+ super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
end
end
@@ -173,81 +173,43 @@ module ActionView #:nodoc:
module Subclasses
end
- include Helpers, Rendering, Partials, ::ERB::Util
+ include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
+ extend ActiveSupport::Memoizable
- def config
- self.config = DEFAULT_CONFIG unless @config
- @config
- end
-
- def config=(config)
- @config = ActiveSupport::OrderedOptions.new.merge(config)
- end
-
- extend ActiveSupport::Memoizable
-
- attr_accessor :base_path, :assigns, :template_extension, :formats
- attr_internal :captures
+ ActionView.run_base_hooks(self)
- def reset_formats(formats)
- @formats = formats
-
- if defined?(AbstractController::HashKey)
- # This is expensive, but we need to reset this when the format is updated,
- # which currently only happens
- Thread.current[:format_locale_key] =
- AbstractController::HashKey.get(self.class, formats, I18n.locale)
- end
- end
-
- class << self
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
- delegate :logger, :to => 'ActionController::Base', :allow_nil => true
- end
-
- @@debug_rjs = false
- ##
- # :singleton-method:
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
+ @@debug_rjs = false
- # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
- # Automatically reloading templates are not thread safe and should only be used in development mode.
- @@cache_template_loading = nil
- cattr_accessor :cache_template_loading
-
- # :nodoc:
- def self.xss_safe?
- true
- end
+ class_attribute :helpers
+ attr_reader :helpers
- def self.cache_template_loading?
- ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
+ class << self
+ delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
+ delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
- attr_internal :request, :layout
+ attr_accessor :base_path, :assigns, :template_extension, :lookup_context
+ attr_internal :captures, :request, :layout, :controller, :template, :config
- def controller_path
- @controller_path ||= controller && controller.controller_path
- end
+ delegate :find, :exists?, :formats, :formats=, :locale, :locale=,
+ :view_paths, :view_paths=, :with_fallbacks, :update_details, :to => :lookup_context
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
- delegate :find, :to => :view_paths
-
- include Context
+ def self.xss_safe? #:nodoc:
+ true
+ end
def self.process_view_paths(value)
ActionView::PathSet.new(Array(value))
end
- class_attribute :helpers
- attr_reader :helpers
-
def self.for_controller(controller)
@views ||= {}
@@ -275,26 +237,26 @@ module ActionView #:nodoc:
klass = self
end
- klass.new(controller.class.view_paths, {}, controller)
+ klass.new(controller.lookup_context, {}, controller)
end
- def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
+ def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
@config = nil
- @formats = formats
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@helpers = self.class.helpers || Module.new
@_controller = controller
- @_content_for = Hash.new {|h,k| h[k] = ActiveSupport::SafeBuffer.new }
+ @_config = ActiveSupport::InheritableOptions.new(controller.config) if controller
+ @_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
- self.view_paths = view_paths
- end
- attr_internal :controller, :template
- attr_reader :view_paths
+ @lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
+ lookup_context : ActionView::LookupContext.new(lookup_context)
+ @lookup_context.formats = formats if formats
+ end
- def view_paths=(paths)
- @view_paths = self.class.process_view_paths(paths)
+ def controller_path
+ @controller_path ||= controller && controller.controller_path
end
def punctuate_body!(part)
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index b4f649385a..e359b0bdac 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -10,6 +10,7 @@ module ActionView #:nodoc:
autoload :CsrfHelper, 'action_view/helpers/csrf_helper'
autoload :DateHelper, 'action_view/helpers/date_helper'
autoload :DebugHelper, 'action_view/helpers/debug_helper'
+ autoload :DeprecatedBlockHelpers, 'action_view/helpers/deprecated_block_helpers'
autoload :FormHelper, 'action_view/helpers/form_helper'
autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper'
autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index e106bb0897..4e12cdab54 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -5,9 +5,11 @@ require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting'
module ActionView
- class Base
- @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
- cattr_accessor :field_error_proc
+ ActionView.base_hook do
+ class ActionView::Base
+ @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
+ cattr_accessor :field_error_proc
+ end
end
module Helpers
@@ -80,13 +82,13 @@ module ActionView
record = convert_to_model(record)
options = options.symbolize_keys
- options[:action] ||= record.new_record? ? "create" : "update"
+ options[:action] ||= record.persisted? ? "update" : "create"
action = url_for(:action => options[:action], :id => record)
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
- contents.safe_concat hidden_field(record_name, :id) unless record.new_record?
+ contents.safe_concat hidden_field(record_name, :id) if record.persisted?
contents.safe_concat all_input_tags(record, record_name, options)
yield contents if block_given?
contents.safe_concat submit_tag(submit_value)
@@ -127,7 +129,7 @@ module ActionView
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors[method])
content_tag("div",
- "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.first)}#{options[:append_text]}",
+ (options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]),
:class => options[:css_class]
)
else
@@ -226,16 +228,16 @@ module ActionView
error_messages = objects.sum do |object|
object.errors.full_messages.map do |msg|
- content_tag(:li, ERB::Util.html_escape(msg))
+ content_tag(:li, msg)
end
- end.join
+ end.join.html_safe
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
contents << content_tag(:p, message) unless message.blank?
contents << content_tag(:ul, error_messages)
- content_tag(:div, contents, html)
+ content_tag(:div, contents.html_safe, html)
end
else
''
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 96976ce45f..0c488b6793 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -11,7 +11,7 @@ module ActionView
# the assets exist before linking to them:
#
# image_tag("rails.png")
- # # => <img alt="Rails src="/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="/images/rails.png?1230601161" />
# stylesheet_link_tag("application")
# # => <link href="/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
#
@@ -133,13 +133,6 @@ module ActionView
# change. You can use something like Live HTTP Headers for Firefox to verify
# that the cache is indeed working.
module AssetTagHelper
- assets_dir = defined?(Rails.public_path) ? Rails.public_path : "public"
- ActionView::DEFAULT_CONFIG = {
- :assets_dir => assets_dir,
- :javascripts_dir => "#{assets_dir}/javascripts",
- :stylesheets_dir => "#{assets_dir}/stylesheets",
- }
-
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
# Returns a link tag that browsers and news readers can use to auto-detect
@@ -530,7 +523,7 @@ module ActionView
options.symbolize_keys!
src = options[:src] = path_to_image(source)
- options[:alt] ||= File.basename(src, '.*').split('.').first.to_s.capitalize
+ options[:alt] ||= File.basename(src, '.*').capitalize
if size = options.delete(:size)
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
@@ -648,8 +641,8 @@ module ActionView
source = rewrite_asset_path(source)
if has_request && include_host
- unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
- source = "#{ActionController::Base.relative_url_root}#{source}"
+ unless source =~ %r{^#{controller.config.relative_url_root}/}
+ source = "#{controller.config.relative_url_root}#{source}"
end
end
end
@@ -677,7 +670,7 @@ module ActionView
# or the value returned from invoking the proc if it's a proc or the value from
# invoking call if it's an object responding to call.
def compute_asset_host(source)
- if host = ActionController::Base.asset_host
+ if host = config.asset_host
if host.is_a?(Proc) || host.respond_to?(:call)
case host.is_a?(Proc) ? host.arity : host.method(:call).arity
when 2
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index 9951e11a37..58c3a8752e 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -8,7 +8,7 @@ module ActionView
# Full usage example:
#
# config/routes.rb:
- # ActionController::Routing::Routes.draw do |map|
+ # Basecamp::Application.routes.draw do |map|
# map.resources :posts
# map.root :controller => "posts"
# end
@@ -114,7 +114,7 @@ module ActionView
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
xml.feed(feed_opts) do
- xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
+ xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 8c48300ed3..75fc2fddeb 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -30,14 +30,10 @@ module ActionView
# <b><%= @greeting %></b>
# </body></html>
#
- def capture(*args, &block)
- # Return captured buffer in erb.
- if block_called_from_erb?(block)
- with_output_buffer { block.call(*args) }
- else
- # Return block result otherwise, but protect buffer also.
- with_output_buffer { return block.call(*args) }
- end
+ def capture(*args)
+ value = nil
+ buffer = with_output_buffer { value = yield *args }
+ buffer.presence || value
end
# Calling content_for stores a block of markup in an identifier for later use.
@@ -143,7 +139,7 @@ module ActionView
# Defaults to a new empty string.
def with_output_buffer(buf = nil) #:nodoc:
unless buf
- buf = ActiveSupport::SafeBuffer.new
+ buf = ActionView::OutputBuffer.new
buf.force_encoding(output_buffer.encoding) if buf.respond_to?(:force_encoding)
end
self.output_buffer, old_buffer = buf, output_buffer
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 8be2f76bd6..42018ee261 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -1,5 +1,6 @@
require "date"
require 'action_view/helpers/tag_helper'
+require 'active_support/core_ext/hash/slice'
module ActionView
module Helpers
@@ -815,7 +816,7 @@ module ActionView
tag_options[:selected] = "selected" if selected == i
select_options << content_tag(:option, value, tag_options)
end
- select_options.join("\n") + "\n"
+ (select_options.join("\n") + "\n").html_safe
end
# Builds select tag from date type and html select options
@@ -833,9 +834,9 @@ module ActionView
select_html = "\n"
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
- select_html << select_options_as_html.to_s
+ select_html << select_options_as_html
- (content_tag(:select, select_html, select_options) + "\n").html_safe
+ (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
end
# Builds a prompt option tag with supplied options or from default options
@@ -865,7 +866,7 @@ module ActionView
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
- }) + "\n").html_safe
+ }.merge(@html_options.slice(:disabled))) + "\n").html_safe
end
# Returns the name attribute for the input tag
@@ -907,7 +908,7 @@ module ActionView
when :hour
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
when :minute
- @options[:time_separator]
+ @options[:discard_minute] ? "" : @options[:time_separator]
when :second
@options[:include_seconds] ? @options[:time_separator] : ""
end
diff --git a/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
new file mode 100644
index 0000000000..3d0657e873
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
@@ -0,0 +1,52 @@
+module ActionView
+ module Helpers
+ module DeprecatedBlockHelpers
+ extend ActiveSupport::Concern
+
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::FormHelper
+
+ def content_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def javascript_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def form_for(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def form_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def fields_for(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def field_set_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
+
+ if RUBY_VERSION < '1.9.0'
+ # Check whether we're called from an erb template.
+ # We'd return a string in any other case, but erb <%= ... %>
+ # can't take an <% end %> later on, so we have to use <% ... %>
+ # and implicitly concat.
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block)
+ end
+ else
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 305d6b3128..7293145964 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -92,6 +92,10 @@ module ActionView
# link:classes/ActionView/Helpers/DateHelper.html, and
# link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
+ extend ActiveSupport::Concern
+
+ include FormTagHelper
+
# Creates a form and a scope around a specific model object that is used
# as a base for questioning about values for the fields.
#
@@ -309,21 +313,20 @@ module ActionView
options[:html][:remote] = true if options.delete(:remote)
- safe_concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
- fields_for(object_name, *(args << options), &proc)
- safe_concat('</form>')
+ output = form_tag(options.delete(:url) || {}, options.delete(:html) || {})
+ output << fields_for(object_name, *(args << options), &proc)
+ output.safe_concat('</form>')
end
def apply_form_for_options!(object_or_array, options) #:nodoc:
object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array
-
object = convert_to_model(object)
html_options =
- if object.respond_to?(:new_record?) && object.new_record?
- { :class => dom_class(object, :new), :id => dom_id(object), :method => :post }
- else
+ if object.respond_to?(:persisted?) && object.persisted?
{ :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put }
+ else
+ { :class => dom_class(object, :new), :id => dom_id(object), :method => :post }
end
options[:html] ||= {}
@@ -529,7 +532,10 @@ module ActionView
end
builder = options[:builder] || ActionView::Base.default_form_builder
- yield builder.new(object_name, object, self, options, block)
+
+ with_output_buffer do
+ yield builder.new(object_name, object, self, options, block)
+ end
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
@@ -1150,7 +1156,7 @@ module ActionView
def submit_default_value
object = @object.respond_to?(:to_model) ? @object.to_model : @object
- key = object ? (object.new_record? ? :create : :update) : :submit
+ key = object ? (object.persisted? ? :update : :create) : :submit
model = if object.class.respond_to?(:model_name)
object.class.model_name.human
@@ -1176,7 +1182,7 @@ module ActionView
association = args.shift
association = association.to_model if association.respond_to?(:to_model)
- if association.respond_to?(:new_record?)
+ if association.respond_to?(:persisted?)
association = [association] if @object.send(association_name).is_a?(Array)
elsif !association.is_a?(Array)
association = @object.send(association_name)
@@ -1184,9 +1190,11 @@ module ActionView
if association.is_a?(Array)
explicit_child_index = options[:child_index]
- association.map do |child|
- fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
- end.join
+ output = ActiveSupport::SafeBuffer.new
+ association.each do |child|
+ output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
+ end
+ output
elsif association
fields_for_nested_model(name, association, options, block)
end
@@ -1195,13 +1203,13 @@ module ActionView
def fields_for_nested_model(name, object, options, block)
object = object.to_model if object.respond_to?(:to_model)
- if object.new_record?
- @template.fields_for(name, object, options, &block)
- else
+ if object.persisted?
@template.fields_for(name, object, options) do |builder|
block.call(builder)
@template.concat builder.hidden_field(:id) unless builder.emitted_hidden_id?
end
+ else
+ @template.fields_for(name, object, options, &block)
end
end
@@ -1212,8 +1220,10 @@ module ActionView
end
end
- class Base
- cattr_accessor :default_form_builder
- @@default_form_builder = ::ActionView::Helpers::FormBuilder
+ ActionView.base_hook do
+ class ActionView::Base
+ cattr_accessor :default_form_builder
+ @@default_form_builder = ::ActionView::Helpers::FormBuilder
+ end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 21acfbbee8..4c523d4b20 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -97,7 +97,9 @@ module ActionView
# </select>
#
module FormOptionsHelper
+ # ERB::Util can mask some helpers like textilize. Make sure to include them.
include ERB::Util
+ include TextHelper
# Create a select tag and a series of contained option tags for the provided object and method.
# The option currently held by the object will be selected, provided that the object is available.
@@ -572,10 +574,9 @@ module ActionView
end
if value.blank? && options[:prompt]
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
- "<option value=\"\">#{prompt}</option>\n" + option_tags
- else
- option_tags
+ option_tags = "<option value=\"\">#{prompt}</option>\n" + option_tags
end
+ option_tags.html_safe
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 9d76870646..07694f5ebb 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -10,6 +10,11 @@ module ActionView
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
# <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>.
module FormTagHelper
+ extend ActiveSupport::Concern
+
+ include UrlHelper
+ include TextHelper
+
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# ActionController::Base#url_for. The method for the form defaults to POST.
#
@@ -90,9 +95,9 @@ module ActionView
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
if blank = options.delete(:include_blank)
if blank.kind_of?(String)
- option_tags = "<option value=\"\">#{blank}</option>" + option_tags
+ option_tags = "<option value=\"\">#{blank}</option>".html_safe + option_tags
else
- option_tags = "<option value=\"\"></option>" + option_tags
+ option_tags = "<option value=\"\"></option>".html_safe + option_tags
end
end
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
@@ -279,7 +284,7 @@ module ActionView
escape = options.key?("escape") ? options.delete("escape") : true
content = html_escape(content) if escape
- content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
+ content_tag :textarea, content.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
end
# Creates a check box form input tag.
@@ -441,10 +446,10 @@ module ActionView
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
content = capture(&block)
- safe_concat(tag(:fieldset, options, true))
- safe_concat(content_tag(:legend, legend)) unless legend.blank?
- concat(content)
- safe_concat("</fieldset>")
+ output = tag(:fieldset, options, true)
+ output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
+ output.concat(content)
+ output.safe_concat("</fieldset>")
end
private
@@ -477,9 +482,10 @@ module ActionView
def form_tag_in_block(html_options, &block)
content = capture(&block)
- safe_concat(form_tag_html(html_options))
- concat(content)
- safe_concat("</form>")
+ output = ActiveSupport::SafeBuffer.new
+ output.safe_concat(form_tag_html(html_options))
+ output << content
+ output.safe_concat("</form>")
end
def token_tag
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 0a901b2d2b..07eee3b399 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -83,17 +83,11 @@ module ActionView
content_or_options_with_block
end
- tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
-
- if block_called_from_erb?(block)
- safe_concat(tag)
- else
- tag
- end
+ content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
end
def javascript_cdata_section(content) #:nodoc:
- "\n//#{cdata_section("\n#{content}\n//")}\n"
+ "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
end
end
end
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 3d3502a08b..46e41bc406 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -28,27 +28,25 @@ module ActionView
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
# => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
- number = number.to_s.strip unless number.nil?
+ return nil if number.nil?
+
+ number = number.to_s.strip
options = options.symbolize_keys
area_code = options[:area_code] || nil
delimiter = options[:delimiter] || "-"
extension = options[:extension].to_s.strip || nil
country_code = options[:country_code] || nil
- begin
- str = ""
- str << "+#{country_code}#{delimiter}" unless country_code.blank?
- str << if area_code
- number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
- else
- number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
- number.starts_with?('-') ? number.slice!(1..-1) : number
- end
- str << " x #{extension}" unless extension.blank?
- str
- rescue
- number
+ str = ""
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
+ str << if area_code
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
+ else
+ number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+ number.starts_with?('-') ? number.slice!(1..-1) : number
end
+ str << " x #{extension}" unless extension.blank?
+ str
end
# Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
@@ -87,13 +85,14 @@ module ActionView
format = options[:format] || defaults[:format]
separator = '' if precision == 0
- begin
- format.gsub(/%n/, number_with_precision(number,
- :precision => precision,
- :delimiter => delimiter,
- :separator => separator)
- ).gsub(/%u/, unit).html_safe
- rescue
+ value = number_with_precision(number,
+ :precision => precision,
+ :delimiter => delimiter,
+ :separator => separator)
+
+ if value
+ format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
+ else
number
end
end
@@ -122,14 +121,11 @@ module ActionView
separator = options[:separator] || defaults[:separator]
delimiter = options[:delimiter] || defaults[:delimiter]
- begin
- number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter) + "%"
- rescue
- number
- end
+ value = number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter)
+ value ? value + "%" : number
end
# Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
@@ -168,11 +164,11 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
separator ||= (options[:separator] || defaults[:separator])
- begin
- parts = number.to_s.split('.')
+ parts = number.to_s.split('.')
+ if parts[0]
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
parts.join(separator)
- rescue
+ else
number
end
end
@@ -216,11 +212,17 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
begin
+ value = Float(number)
+ rescue ArgumentError, TypeError
+ value = nil
+ end
+
+ if value
rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision
number_with_delimiter("%01.#{precision}f" % rounded_number,
:separator => separator,
:delimiter => delimiter)
- rescue
+ else
number
end
end
@@ -293,17 +295,13 @@ module ActionView
unit_key = STORAGE_UNITS[exponent]
unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
- begin
- escaped_separator = Regexp.escape(separator)
- formatted_number = number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter
- ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
- rescue
- number
- end
+ escaped_separator = Regexp.escape(separator)
+ formatted_number = number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter
+ ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 51510449bd..4d6ea7dfb2 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -182,9 +182,11 @@ module ActionView
def initialize(context, &block) #:nodoc:
context._evaluate_assigns_and_ivars
@context, @lines = context, []
- include_helpers_from_context
- @context.with_output_buffer(@lines) do
- @context.instance_exec(self, &block)
+ @context.update_details(:formats => [:js, :html]) do
+ include_helpers_from_context
+ @context.with_output_buffer(@lines) do
+ @context.instance_exec(self, &block)
+ end
end
end
@@ -569,15 +571,19 @@ module ActionView
end
end
- def render(*options_for_render)
- old_formats = @context && @context.formats
+ def render(*options)
+ with_formats(:html) do
+ case option = options.first
+ when Hash
+ @context.render(*options)
+ else
+ option.to_s
+ end
+ end
+ end
- @context.reset_formats([:html]) if @context
- Hash === options_for_render.first ?
- @context.render(*options_for_render) :
- options_for_render.first.to_s
- ensure
- @context.reset_formats(old_formats) if @context
+ def with_formats(*args)
+ @context ? @context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index ee10f8f3bb..8ae2e5f28f 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -7,6 +7,9 @@ module ActionView
module TagHelper
include ERB::Util
+ extend ActiveSupport::Concern
+ include CaptureHelper
+
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
autoplay controls loop selected hidden scoped async
defer reversed ismap seemless muted required
@@ -69,13 +72,7 @@ module ActionView
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
- content_tag = content_tag_string(name, capture(&block), options, escape)
-
- if block_called_from_erb?(block)
- safe_concat(content_tag)
- else
- content_tag
- end
+ content_tag_string(name, capture(&block), options, escape)
else
content_tag_string(name, content_or_options_with_block, options, escape)
end
@@ -109,25 +106,10 @@ module ActionView
end
private
- BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
-
- if RUBY_VERSION < '1.9.0'
- # Check whether we're called from an erb template.
- # We'd return a string in any other case, but erb <%= ... %>
- # can't take an <% end %> later on, so we have to use <% ... %>
- # and implicitly concat.
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block)
- end
- else
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
- end
- end
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
- "<#{name}#{tag_options}>#{content}</#{name}>".html_safe
+ ("<#{name}#{tag_options}>".html_safe << content.to_s).safe_concat("</#{name}>")
end
def tag_options(options, escape = true)
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index c348ea7a0d..8a89ee58a0 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -12,9 +12,10 @@ module ActionView
# prepend the key with a period, nothing is converted.
def translate(key, options = {})
options[:raise] = true
- I18n.translate(scope_key_by_partial(key), options).html_safe
+ translation = I18n.translate(scope_key_by_partial(key), options)
+ translation.is_a?(Array) ? translation.map { |entry| entry.html_safe } : translation.html_safe
rescue I18n::MissingTranslationData => e
- keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
+ keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
end
alias :t :translate
@@ -28,7 +29,7 @@ module ActionView
private
def scope_key_by_partial(key)
- if key.to_s.first == "."
+ if (key.respond_to?(:join) ? key.join : key.to_s).first == "."
if @_virtual_path
@_virtual_path.gsub(%r{/_?}, ".") + key.to_s
else
@@ -40,4 +41,4 @@ module ActionView
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index fde4bfa4ce..ae1385f3b7 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -5,7 +5,7 @@ require 'active_support/core_ext/hash/keys'
module ActionView
module Helpers #:nodoc:
# Provides a set of methods for making links and getting URLs that
- # depend on the routing subsystem (see ActionController::Routing).
+ # depend on the routing subsystem (see ActionDispatch::Routing).
# This allows you to use the same format for links in views
# and controllers.
module UrlHelper
@@ -63,7 +63,7 @@ module ActionView
# # => /testing/jump/#tax&ship
#
# <%= url_for(Workshop.new) %>
- # # relies on Workshop answering a new_record? call (and in this case returning true)
+ # # relies on Workshop answering a persisted? call (and in this case returning false)
# # => /workshops
#
# <%= url_for(@workshop) %>
@@ -202,8 +202,6 @@ module ActionView
#
# link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
-
- #
def link_to(*args, &block)
if block_given?
options = args.first || {}
@@ -226,7 +224,7 @@ module ActionView
end
href_attr = "href=\"#{url}\"" unless href
- "<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe
+ ("<a #{href_attr}#{tag_options}>".html_safe << (name || url)).safe_concat("</a>")
end
end
@@ -469,14 +467,12 @@ module ActionView
extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}&" unless subject.nil?
extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
- email_address = email_address.to_s
-
- email_address_obfuscated = email_address.dup
+ email_address_obfuscated = html_escape(email_address)
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
if encode == "javascript"
- "document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
+ "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
string << sprintf("%%%x", c)
end
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
@@ -493,9 +489,9 @@ module ActionView
char = c.chr
string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
end
- content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" })
+ content_tag "a", name || email_address_encoded.html_safe, html_options.merge({ "href" => "#{string}#{extras}" })
else
- content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
+ content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
end
end
@@ -548,10 +544,11 @@ module ActionView
# submitted url doesn't have any either. This lets the function
# work with things like ?order=asc
if url_string.index("?")
- request_uri = request.request_uri
+ request_uri = request.fullpath
else
- request_uri = request.request_uri.split('?').first
+ request_uri = request.path
end
+
if url_string =~ /^\w+:\/\//
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
else
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
new file mode 100644
index 0000000000..27ee8b23c9
--- /dev/null
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -0,0 +1,154 @@
+require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/object/blank'
+
+module ActionView
+ # LookupContext is the object responsible to hold all information required to lookup
+ # templates, i.e. view paths and details. The LookupContext is also responsible to
+ # generate a key, given to view paths, used in the resolver cache lookup. Since
+ # this key is generated just once during the request, it speeds up all cache accesses.
+ class LookupContext #:nodoc:
+ mattr_accessor :fallbacks
+ @@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
+
+ mattr_accessor :registered_details
+ self.registered_details = []
+
+ def self.register_detail(name, options = {}, &block)
+ self.registered_details << name
+
+ Setters.send :define_method, :"_#{name}_defaults", &block
+ Setters.module_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{name}=(value)
+ value = Array(value.presence || _#{name}_defaults)
+ #{"value << nil unless value.include?(nil)" unless options[:allow_nil] == false}
+
+ unless value == @details[:#{name}]
+ @details_key, @details = nil, @details.merge(:#{name} => value)
+ @details.freeze
+ end
+ end
+ METHOD
+ end
+
+ # Holds raw setters for the registered details.
+ module Setters #:nodoc:
+ end
+
+ register_detail(:formats) { Mime::SET.symbols }
+ register_detail(:locale) { [I18n.locale] }
+
+ class DetailsKey #:nodoc:
+ attr_reader :details
+ alias :eql? :equal?
+
+ @details_keys = Hash.new
+
+ def self.get(details)
+ @details_keys[details] ||= new(details)
+ end
+
+ def initialize(details)
+ @details, @hash = details, details.hash
+ end
+ end
+
+ def initialize(view_paths, details = {})
+ @details, @details_key = {}, nil
+ self.view_paths = view_paths
+ self.details = details
+ end
+
+ module ViewPaths
+ attr_reader :view_paths
+
+ # Whenever setting view paths, makes a copy so we can manipulate then in
+ # instance objects as we wish.
+ def view_paths=(paths)
+ @view_paths = ActionView::Base.process_view_paths(paths)
+ end
+
+ def find(name, prefix = nil, partial = false)
+ @view_paths.find(name, prefix, partial, details, details_key)
+ end
+
+ def find_all(name, prefix = nil, partial = false)
+ @view_paths.find_all(name, prefix, partial, details, details_key)
+ end
+
+ def exists?(name, prefix = nil, partial = false)
+ @view_paths.exists?(name, prefix, partial, details, details_key)
+ end
+
+ # Add fallbacks to the view paths. Useful in cases you are rendering a :file.
+ def with_fallbacks
+ added_resolvers = 0
+ self.class.fallbacks.each do |resolver|
+ next if view_paths.include?(resolver)
+ view_paths.push(resolver)
+ added_resolvers += 1
+ end
+ yield
+ ensure
+ added_resolvers.times { view_paths.pop }
+ end
+ end
+
+ module Details
+ attr_reader :details
+
+ def details=(given_details)
+ registered_details.each { |key| send(:"#{key}=", given_details[key]) }
+ end
+
+ def details_key
+ @details_key ||= DetailsKey.get(@details)
+ end
+
+ # Shortcut to read formats from details.
+ def formats
+ @details[:formats].compact
+ end
+
+ # Overload formats= to reject [:"*/*"] values.
+ def formats=(value, freeze=true)
+ value = nil if value == [:"*/*"]
+ super(value)
+ end
+
+ # Shortcut to read locale.
+ def locale
+ I18n.locale
+ end
+
+ # Overload locale= to also set the I18n.locale. If the current I18n.config object responds
+ # to i18n_config, it means that it's has a copy of the original I18n configuration and it's
+ # acting as proxy, which we need to skip.
+ def locale=(value)
+ value = value.first if value.is_a?(Array)
+ config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
+ config.locale = value if value
+ super(I18n.locale)
+ end
+
+ # Update the details keys by merging the given hash into the current
+ # details hash. If a block is given, the details are modified just during
+ # the execution of the block and reverted to the previous value after.
+ def update_details(new_details)
+ old_details = @details
+ self.details = old_details.merge(new_details)
+
+ if block_given?
+ begin
+ yield
+ ensure
+ @details = old_details
+ end
+ end
+ end
+ end
+
+ include Setters
+ include Details
+ include ViewPaths
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 6e55d69d00..35927d09d1 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -1,80 +1,39 @@
module ActionView #:nodoc:
class PathSet < Array #:nodoc:
- def self.type_cast(obj, cache = nil)
- # TODO: Clean this up
- if obj.is_a?(String)
- if cache.nil?
- cache = !defined?(Rails.application) || Rails.application.config.cache_classes
+ %w(initialize << concat insert push unshift).each do |method|
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{method}(*args)
+ super
+ typecast!
end
- FileSystemResolverWithFallback.new(obj, :cache => cache)
- else
- obj
- end
- end
-
- def initialize(*args)
- super(*args).map! { |obj| self.class.type_cast(obj) }
- end
-
- def <<(obj)
- super(self.class.type_cast(obj))
- end
-
- def concat(array)
- super(array.map! { |obj| self.class.type_cast(obj) })
- end
-
- def insert(index, obj)
- super(index, self.class.type_cast(obj))
- end
-
- def push(*objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
+ METHOD
end
- def unshift(*objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
+ def find(path, prefix = nil, partial = false, details = {}, key = nil)
+ template = find_all(path, prefix, partial, details, key).first
+ raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
+ template
end
- def find(path, details = {}, prefix = nil, partial = false)
- template_path = path
-
- each do |load_path|
- if template = load_path.find(template_path, details, prefix, partial)
- return template
- end
+ def find_all(*args)
+ each do |resolver|
+ templates = resolver.find_all(*args)
+ return templates unless templates.empty?
end
-
- raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}", details, partial)
+ []
end
-
- def exists?(path, extension = nil, prefix = nil, partial = false)
- template_path = path.sub(/^\//, '')
- each do |load_path|
- return true if template = load_path.find(template_path, extension, prefix, partial)
- end
- false
+ def exists?(*args)
+ find_all(*args).any?
end
- def find_template(original_template_path, format = nil, html_fallback = true)
- return original_template_path if original_template_path.respond_to?(:render)
- template_path = original_template_path.sub(/^\//, '')
+ protected
- each do |load_path|
- if template = load_path.find(template_path, format)
- return template
- # Try to find html version if the format is javascript
- elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"]
- return template
- elsif format == :js && html_fallback && template = load_path["#{template_path}.html"]
- return template
- end
+ def typecast!
+ each_with_index do |path, i|
+ next unless path.is_a?(String)
+ self[i] = FileSystemResolver.new(path)
end
-
- return Template.new(original_template_path, original_template_path.to_s =~ /\A\// ? "" : ".") if File.file?(original_template_path)
-
- raise MissingTemplate.new(self, original_template_path, format)
end
end
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index d9e2557d89..2e5d115630 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -5,12 +5,14 @@ module ActionView
class Railtie < Rails::Railtie
railtie_name :action_view
- require "action_view/railties/subscriber"
- subscriber ActionView::Railties::Subscriber.new
+ require "action_view/railties/log_subscriber"
+ log_subscriber ActionView::Railties::LogSubscriber.new
initializer "action_view.cache_asset_timestamps" do |app|
unless app.config.cache_classes
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ ActionView.base_hook do
+ ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ end
end
end
end
diff --git a/actionpack/lib/action_view/railties/subscriber.rb b/actionpack/lib/action_view/railties/log_subscriber.rb
index 803f19379c..9487a10706 100644
--- a/actionpack/lib/action_view/railties/subscriber.rb
+++ b/actionpack/lib/action_view/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActionView
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
def render_template(event)
message = "Rendered #{from_rails_root(event.payload[:identifier])}"
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
new file mode 100644
index 0000000000..8688de3d18
--- /dev/null
+++ b/actionpack/lib/action_view/render/layouts.rb
@@ -0,0 +1,65 @@
+require 'active_support/core_ext/object/try'
+
+module ActionView
+ module Layouts
+
+ # You can think of a layout as a method that is called with a block. _layout_for
+ # returns the contents that are yielded to the layout. If the user calls yield
+ # :some_name, the block, by default, returns content_for(:some_name). If the user
+ # calls yield, the default block returns content_for(:layout).
+ #
+ # The user can override this default by passing a block to the layout.
+ #
+ # ==== Example
+ #
+ # # The template
+ # <% render :layout => "my_layout" do %>Content<% end %>
+ #
+ # # The layout
+ # <html><% yield %></html>
+ #
+ # In this case, instead of the default block, which would return content_for(:layout),
+ # this method returns the block that was passed in to render layout, and the response
+ # would be <html>Content</html>.
+ #
+ # Finally, the block can take block arguments, which can be passed in by yield.
+ #
+ # ==== Example
+ #
+ # # The template
+ # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
+ #
+ # # The layout
+ # <html><% yield Struct.new(:name).new("David") %></html>
+ #
+ # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
+ # and the Struct specified in the layout would be passed into the block. The result
+ # would be <html>Hello David</html>.
+ def _layout_for(name = nil, &block) #:nodoc:
+ if !block || name
+ @_content_for[name || :layout]
+ else
+ capture(&block)
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checkes if at least a layout with
+ # the given name exists across all details before raising the error.
+ def _find_layout(layout) #:nodoc:
+ begin
+ layout =~ /^\// ?
+ with_fallbacks { find(layout) } : find(layout)
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless exists?(layout)
+ end
+ end
+ end
+
+ # Contains the logic that actually renders the layout.
+ def _render_layout(layout, locals, &block) #:nodoc:
+ layout.render(self, locals){ |*name| _layout_for(*name, &block) }
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 62d31662db..d34ab0354c 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -174,17 +174,11 @@ module ActionView
class PartialRenderer
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
- TEMPLATES = Hash.new {|h,k| h[k] = {} }
-
- attr_reader :template
def initialize(view_context, options, block)
@view = view_context
@partial_names = PARTIAL_NAMES[@view.controller.class]
- key = Thread.current[:format_locale_key]
- @templates = TEMPLATES[key] if key
-
setup(options, block)
end
@@ -228,6 +222,7 @@ module ActionView
if !@block && (layout = @options[:layout])
content = @view._render_layout(find_template(layout), @locals){ content }
end
+
content
end
end
@@ -244,9 +239,9 @@ module ActionView
end
def collection_with_template(template = @template)
- segments, locals, as = [], @locals, @options[:as] || template.variable_name
+ segments, locals, as, template = [], @locals, @options[:as] || @template.variable_name, @template
- counter_name = template.counter_name
+ counter_name = template.counter_name
locals[counter_name] = -1
@collection.each do |object|
@@ -256,7 +251,6 @@ module ActionView
segments << template.render(@view, locals)
end
- @template = template
segments
end
@@ -277,7 +271,7 @@ module ActionView
end
def render_partial(object = @object)
- locals, view = @locals, @view
+ locals, view, template = @locals, @view, @template
object ||= locals[template.variable_name]
locals[@options[:as] || template.variable_name] = object
@@ -288,6 +282,7 @@ module ActionView
end
private
+
def collection
if @object.respond_to?(:to_ary)
@object
@@ -296,20 +291,10 @@ module ActionView
end
end
- def find_template(path = @path)
- unless @templates
- path && _find_template(path)
- else
- path && @templates[path] ||= _find_template(path)
- end
- end
-
- def _find_template(path)
- if controller = @view.controller
- prefix = controller.controller_path unless path.include?(?/)
- end
-
- @view.find(path, {:formats => @view.formats}, prefix, true)
+ def find_template(path=@path)
+ return path unless path.is_a?(String)
+ prefix = @view.controller_path unless path.include?(?/)
+ @view.find(path, prefix, true)
end
def partial_path(object = @object)
@@ -324,21 +309,8 @@ module ActionView
end
end
- def render_partial(options)
- _evaluate_assigns_and_ivars
-
- details = options[:_details]
-
- # Is this needed
- self.formats = details[:formats] if details
- renderer = PartialRenderer.new(self, options, nil)
- text = renderer.render
- options[:_template] = renderer.template
- text
- end
-
def _render_partial(options, &block) #:nodoc:
- if defined? @renderer
+ if defined?(@renderer)
@renderer.setup(options, block)
else
@renderer = PartialRenderer.new(self, options, block)
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index abc7c09991..47ea70f5ad 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -15,27 +15,11 @@ module ActionView
def render(options = {}, locals = {}, &block) #:nodoc:
case options
when Hash
- layout = options[:layout]
- options[:locals] ||= {}
-
if block_given?
- return safe_concat(_render_partial(options.merge(:partial => layout), &block))
- elsif options.key?(:partial)
- return _render_partial(options)
- end
-
- template = if options[:file]
- find(options[:file], {:formats => formats})
- elsif options[:inline]
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- Template.new(options[:inline], "inline template", handler, {})
- elsif options[:text]
- Template::Text.new(options[:text])
- end
-
- if template
- layout = find(layout, {:formats => formats}) if layout
- _render_template(template, layout, :locals => options[:locals])
+ content = _render_partial(options.merge(:partial => options[:layout]), &block)
+ safe_concat(content)
+ else
+ _render(options)
end
when :update
update_page(&block)
@@ -44,61 +28,57 @@ module ActionView
end
end
- # You can think of a layout as a method that is called with a block. _layout_for
- # returns the contents that are yielded to the layout. If the user calls yield
- # :some_name, the block, by default, returns content_for(:some_name). If the user
- # calls yield, the default block returns content_for(:layout).
- #
- # The user can override this default by passing a block to the layout.
- #
- # ==== Example
- #
- # # The template
- # <% render :layout => "my_layout" do %>Content<% end %>
- #
- # # The layout
- # <html><% yield %></html>
- #
- # In this case, instead of the default block, which would return content_for(:layout),
- # this method returns the block that was passed in to render layout, and the response
- # would be <html>Content</html>.
- #
- # Finally, the block can take block arguments, which can be passed in by yield.
- #
- # ==== Example
- #
- # # The template
- # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
- #
- # # The layout
- # <html><% yield Struct.new(:name).new("David") %></html>
- #
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
- # and the Struct specified in the layout would be passed into the block. The result
- # would be <html>Hello David</html>.
- def _layout_for(name = nil, &block)
- return @_content_for[name || :layout] if !block_given? || name
- capture(&block)
- end
-
# This is the API to render a ViewContext's template from a controller.
- #
- # Internal Options:
- # _template:: The Template object to render
- # _layout:: The layout, if any, to wrap the Template in
- def render_template(options)
+ def render_template(options, &block)
_evaluate_assigns_and_ivars
- template, layout = options.values_at(:_template, :_layout)
- _render_template(template, layout, options)
+
+ # TODO Layout for partials should be handled here, because inside the
+ # partial renderer it looks for the layout as a partial.
+ if options.key?(:partial) && options[:layout]
+ options[:layout] = _find_layout(options[:layout])
+ end
+
+ _render(options, &block)
+ end
+
+ # This method holds the common render logic for both controllers and
+ # views rendering stacks.
+ def _render(options) #:nodoc:
+ if options.key?(:partial)
+ _render_partial(options)
+ else
+ template = _determine_template(options)
+ yield template if block_given?
+ _render_template(template, options[:layout], options)
+ end
end
- def _render_template(template, layout = nil, options = {})
+ # Determine the template to be rendered using the given options.
+ def _determine_template(options) #:nodoc:
+ if options.key?(:inline)
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ Template.new(options[:inline], "inline template", handler, {})
+ elsif options.key?(:text)
+ Template::Text.new(options[:text], self.formats.try(:first))
+ elsif options.key?(:_template)
+ options[:_template]
+ elsif options.key?(:file)
+ with_fallbacks { find(options[:file], options[:prefix]) }
+ elsif options.key?(:template)
+ find(options[:template], options[:prefix])
+ end
+ end
+
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def _render_template(template, layout = nil, options = {}) #:nodoc:
locals = options[:locals] || {}
+ layout = _find_layout(layout) if layout
ActiveSupport::Notifications.instrument("action_view.render_template",
:identifier => template.identifier, :layout => layout.try(:identifier)) do
- content = template.render(self, locals)
+ content = template.render(self, locals) { |*name| _layout_for(*name) }
@_content_for[:layout] = content
if layout
@@ -110,8 +90,5 @@ module ActionView
end
end
- def _render_layout(layout, locals, &block)
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
- end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index cd6b1930a1..b4fdb49d3b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -16,23 +16,22 @@ module ActionView
end
extend Template::Handlers
- attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
+
+ attr_reader :source, :identifier, :handler, :virtual_path, :formats
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
- @details = details
+
+ @partial = details[:partial]
+ @virtual_path = details[:virtual_path]
@method_names = {}
- format = details.delete(:format) || begin
- # TODO: Clean this up
- handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
- end
- @mime_type = Mime::Type.lookup_by_extension(format.to_s)
- @formats = [format.to_sym]
- @formats << :html if format == :js
- @details[:formats] = Array.wrap(format.to_sym)
+ format = details[:format]
+ format ||= handler.default_format.to_sym if handler.respond_to?(:default_format)
+ format ||= :html
+ @formats = [format.to_sym]
end
def render(view, locals, &block)
@@ -47,19 +46,20 @@ module ActionView
end
end
- # TODO: Figure out how to abstract this
+ def mime_type
+ @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
+ end
+
def variable_name
- @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
- # TODO: Figure out how to abstract this
def counter_name
@counter_name ||= "#{variable_name}_counter".to_sym
end
- # TODO: kill hax
def partial?
- @details[:partial]
+ @partial
end
def inspect
@@ -87,7 +87,7 @@ module ActionView
source = <<-end_src
def #{method_name}(local_assigns)
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@details[:virtual_path].inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
+ _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
ensure
@_virtual_path, self.output_buffer = _old_virtual_path, _old_output_buffer
end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 4573a440d1..ac5902cc0e 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -3,10 +3,17 @@ require 'active_support/core_ext/string/output_safety'
require 'erubis'
module ActionView
+ class OutputBuffer < ActiveSupport::SafeBuffer
+ def <<(value)
+ super(value.to_s)
+ end
+ alias :append= :<<
+ end
+
module Template::Handlers
class Erubis < ::Erubis::Eruby
def add_preamble(src)
- src << "@output_buffer = ActiveSupport::SafeBuffer.new;"
+ src << "@output_buffer = ActionView::OutputBuffer.new;"
end
def add_text(src, text)
@@ -15,15 +22,15 @@ module ActionView
end
def add_expr_literal(src, code)
- if code =~ /\s*raw\s+(.*)/
- src << "@output_buffer.safe_concat((" << $1 << ").to_s);"
+ if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
+ src << '@output_buffer.append= ' << code
else
- src << '@output_buffer << ((' << code << ').to_s);'
+ src << '@output_buffer.append= (' << code << ');'
end
end
def add_expr_escaped(src, code)
- src << '@output_buffer << ' << escaped_expr(code) << ';'
+ src << '@output_buffer.append= ' << escaped_expr(code) << ';'
end
def add_postamble(src)
@@ -42,14 +49,14 @@ module ActionView
self.erb_trim_mode = '-'
self.default_format = Mime::HTML
-
- cattr_accessor :erubis_implementation
- self.erubis_implementation = Erubis
+
+ cattr_accessor :erb_implementation
+ self.erb_implementation = Erubis
def compile(template)
source = template.source.gsub(/\A(<%(#.*coding[:=]\s*(\S+)\s*)-?%>)\s*\n?/, '')
erb = "<% __in_erb_template=true %>#{source}"
- result = self.class.erubis_implementation.new(erb, :trim=>(self.class.erb_trim_mode == "-")).src
+ result = self.class.erb_implementation.new(erb, :trim=>(self.class.erb_trim_mode == "-")).src
result = "#{$2}\n#{result}" if $2
result
end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 8acfe6cad0..a43597e728 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -4,64 +4,47 @@ require "active_support/core_ext/array/wrap"
require "action_view/template"
module ActionView
- # Abstract superclass
class Resolver
-
- class_inheritable_accessor(:registered_details)
- self.registered_details = {}
-
- def self.register_detail(name, options = {})
- registered_details[name] = lambda do |val|
- val = Array.wrap(val || yield)
- val |= [nil] unless options[:allow_nil] == false
- val
- end
- end
-
- register_detail(:locale) { [I18n.locale] }
- register_detail(:formats) { Mime::SET.symbols }
- register_detail(:handlers, :allow_nil => false) do
- Template::Handlers.extensions
+ def initialize
+ @cached = Hash.new { |h1,k1| h1[k1] =
+ Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
end
- def initialize(options = {})
- @cache = options[:cache]
- @cached = {}
+ def clear_cache
+ @cached.clear
end
- # Normalizes the arguments and passes it on to find_template
def find(*args)
find_all(*args).first
end
- def find_all(name, details = {}, prefix = nil, partial = nil)
- details = normalize_details(details)
+ # Normalizes the arguments and passes it on to find_template.
+ def find_all(name, prefix=nil, partial=false, details={}, key=nil)
name, prefix = normalize_name(name, prefix)
+ details = details.merge(:handlers => default_handlers)
- cached([name, details, prefix, partial]) do
- find_templates(name, details, prefix, partial)
+ cached(key, prefix, name, partial) do
+ find_templates(name, prefix, partial, details)
end
end
private
+ def caching?
+ @caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
+ end
+
+ def default_handlers
+ Template::Handlers.extensions + [nil]
+ end
+
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
# normalized.
- def find_templates(name, details, prefix, partial)
+ def find_templates(name, prefix, partial, details)
raise NotImplementedError
end
- def normalize_details(details)
- details = details.dup
- # TODO: Refactor this concern out of the resolver
- details.delete(:formats) if details[:formats] == [:"*/*"]
- registered_details.each do |k, v|
- details[k] = v.call(details[k])
- end
- details
- end
-
# Support legacy foo.erb names even though we now ignore .erb
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
@@ -73,92 +56,73 @@ module ActionView
return parts.pop, [prefix, *parts].compact.join("/")
end
- def cached(key)
- return yield unless @cache
- return @cached[key] if @cached.key?(key)
- @cached[key] = yield
+ def cached(key, prefix, name, partial)
+ return yield unless key && caching?
+ scope = @cached[key][prefix][name]
+ if scope.key?(partial)
+ scope[partial]
+ else
+ scope[partial] = yield
+ end
end
end
class PathResolver < Resolver
-
EXTENSION_ORDER = [:locale, :formats, :handlers]
def to_s
@path.to_s
end
- alias to_path to_s
-
- def find_templates(name, details, prefix, partial)
- path = build_path(name, details, prefix, partial)
- query(path, EXTENSION_ORDER.map { |ext| details[ext] })
- end
+ alias :to_path :to_s
private
- def build_path(name, details, prefix, partial)
+ def find_templates(name, prefix, partial, details)
+ path = build_path(name, prefix, partial, details)
+ query(partial, path, EXTENSION_ORDER.map { |ext| details[ext] })
+ end
+
+ def build_path(name, prefix, partial, details)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
path
end
- def query(path, exts)
+ def query(partial, path, exts)
query = File.join(@path, path)
+
exts.each do |ext|
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
end
Dir[query].reject { |p| File.directory?(p) }.map do |p|
- Template.new(File.read(p), File.expand_path(p), *path_to_details(p))
+ handler, format = extract_handler_and_format(p)
+ Template.new(File.read(p), File.expand_path(p), handler,
+ :partial => partial, :virtual_path => path, :format => format)
end
end
- # # TODO: fix me
- # # :api: plugin
- def path_to_details(path)
- # [:erb, :format => :html, :locale => :en, :partial => true/false]
- if m = path.match(%r'((^|.*/)(_)?[\w-]+)((?:\.[\w-]+)*)\.(\w+)$')
- partial = m[3] == '_'
- details = (m[4]||"").split('.').reject { |e| e.empty? }
- handler = Template.handler_class_for_extension(m[5])
+ def extract_handler_and_format(path)
+ pieces = File.basename(path).split(".")
+ pieces.shift
- format = Mime[details.last] && details.pop.to_sym
- locale = details.last && details.pop.to_sym
-
- virtual_path = (m[1].gsub("#{@path}/", "") << details.join("."))
-
- return handler, :format => format, :locale => locale, :partial => partial,
- :virtual_path => virtual_path
- end
+ handler = Template.handler_class_for_extension(pieces.pop)
+ format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
+ [handler, format]
end
end
class FileSystemResolver < PathResolver
- def initialize(path, options = {})
+ def initialize(path)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
- super(options)
+ super()
@path = Pathname.new(path).expand_path
end
- end
-
- # TODO: remove hack
- class FileSystemResolverWithFallback < Resolver
- def initialize(path, options = {})
- super(options)
- @paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)]
- end
- def find_templates(*args)
- @paths.each do |p|
- template = p.find_templates(*args)
- return template unless template.empty?
- end
- []
- end
-
- def to_s
- @paths.first.to_s
+ def eql?(resolver)
+ self.class.equal?(resolver.class) && to_path == resolver.to_path
end
+ alias :== :eql?
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 2abb352d4e..df394b0fb0 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -1,15 +1,10 @@
module ActionView #:nodoc:
class Template
class Text < String #:nodoc:
- HTML = Mime[:html]
-
- def initialize(string, content_type = HTML)
+ def initialize(string, content_type = nil)
super(string.to_s)
- @content_type = Mime[content_type] || content_type
- end
-
- def details
- {:formats => [@content_type.to_sym]}
+ @content_type = Mime[content_type] || content_type if content_type
+ @content_type ||= Mime::TEXT
end
def identifier
@@ -29,7 +24,7 @@ module ActionView #:nodoc:
end
def formats
- [mime_type]
+ [@content_type.to_sym]
end
def partial?
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index fc29acea6d..1578ac9479 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,4 +1,5 @@
require 'action_controller/test_case'
+require 'action_view'
module ActionView
class Base
@@ -34,6 +35,8 @@ module ActionView
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
+ @request.env.delete('PATH_INFO')
+
@params = {}
end
end
@@ -60,6 +63,10 @@ module ActionView
make_test_case_available_to_view!
end
+ def config
+ @controller.config
+ end
+
def render(options = {}, local_assigns = {}, &block)
@rendered << output = _view.render(options, local_assigns, &block)
output
@@ -152,7 +159,7 @@ module ActionView
end
def method_missing(selector, *args)
- if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+ if @controller._router.named_routes.helpers.include?(selector)
@controller.__send__(selector, *args)
else
super
diff --git a/actionpack/test/abstract/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb
index 4ad87d9762..f70d497481 100644
--- a/actionpack/test/abstract/abstract_controller_test.rb
+++ b/actionpack/test/abstract/abstract_controller_test.rb
@@ -59,11 +59,11 @@ module AbstractController
end
def rendering_to_body
- self.response_body = render_to_body :_template_name => "naked_render.erb"
+ self.response_body = render_to_body :template => "naked_render.erb"
end
def rendering_to_string
- self.response_body = render_to_string :_template_name => "naked_render.erb"
+ self.response_body = render_to_string :template => "naked_render.erb"
end
end
diff --git a/actionpack/test/abstract/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb
index b6d89ea489..65a50807fd 100644
--- a/actionpack/test/abstract/layouts_test.rb
+++ b/actionpack/test/abstract/layouts_test.rb
@@ -8,41 +8,52 @@ module AbstractControllerTests
include AbstractController::Rendering
include AbstractController::Layouts
+ def _prefix
+ "template"
+ end
+
self.view_paths = [ActionView::FixtureResolver.new(
- "layouts/hello.erb" => "With String <%= yield %>",
- "layouts/hello_override.erb" => "With Override <%= yield %>",
- "layouts/abstract_controller_tests/layouts/with_string_implied_child.erb" =>
- "With Implied <%= yield %>",
- "layouts/overwrite.erb" => "Overwrite <%= yield %>",
- "layouts/with_false_layout.erb" => "False Layout <%= yield %>"
+ "abstract_controller_tests/layouts/with_string_implied_child.erb" =>
+ "With Implied <%= yield %>",
+ "layouts/hello.erb" => "With String <%= yield %>",
+ "layouts/hello_override.erb" => "With Override <%= yield %>",
+ "layouts/overwrite.erb" => "Overwrite <%= yield %>",
+ "layouts/with_false_layout.erb" => "False Layout <%= yield %>"
)]
end
class Blank < Base
- self.view_paths = []
-
+ self.view_paths = ActionView::FixtureResolver.new("template/index.erb" => "Hello blank!")
+
def index
- render :_template => ActionView::Template::Text.new("Hello blank!")
+ render
end
end
class WithString < Base
layout "hello"
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello string!",
+ "template/overwrite_default.erb" => "Hello string!",
+ "template/overwrite_false.erb" => "Hello string!",
+ "template/overwrite_string.erb" => "Hello string!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello string!")
+ render
end
def overwrite_default
- render :_template => ActionView::Template::Text.new("Hello string!"), :layout => :default
+ render :layout => :default
end
def overwrite_false
- render :_template => ActionView::Template::Text.new("Hello string!"), :layout => false
+ render :layout => false
end
def overwrite_string
- render :_template => ActionView::Template::Text.new("Hello string!"), :layout => "overwrite"
+ render :layout => "overwrite"
end
def overwrite_skip
@@ -70,18 +81,28 @@ module AbstractControllerTests
class WithProc < Base
layout proc { |c| "overwrite" }
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello proc!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello proc!")
+ render
end
end
class WithSymbol < Base
layout :hello
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello symbol!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello symbol!")
+ render
end
- private
+
+ private
+
def hello
"overwrite"
end
@@ -89,11 +110,17 @@ module AbstractControllerTests
class WithSymbolReturningString < Base
layout :no_hello
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello missing symbol!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello missing symbol!")
+ render
end
- private
+
+ private
+
def no_hello
nil
end
@@ -101,19 +128,28 @@ module AbstractControllerTests
class WithSymbolReturningNil < Base
layout :nilz
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello nilz!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello nilz!")
+ render
end
- def nilz() end
+ def nilz
+ end
end
class WithSymbolReturningObj < Base
layout :objekt
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello object!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello nilz!")
+ render
end
def objekt
@@ -123,33 +159,49 @@ module AbstractControllerTests
class WithSymbolAndNoMethod < Base
layout :no_method
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello boom!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello boom!")
+ render
end
end
class WithMissingLayout < Base
layout "missing"
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello missing!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello missing!")
+ render
end
end
class WithFalseLayout < Base
layout false
-
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello false!"
+ )
+
def index
- render :_template => ActionView::Template::Text.new("Hello false!")
+ render
end
end
class WithNilLayout < Base
layout nil
+
+ append_view_path ActionView::FixtureResolver.new(
+ "template/index.erb" => "Hello nil!"
+ )
def index
- render :_template => ActionView::Template::Text.new("Hello nil!")
+ render
end
end
diff --git a/actionpack/test/abstract/localized_cache_test.rb b/actionpack/test/abstract/localized_cache_test.rb
deleted file mode 100644
index 8b0b0fff03..0000000000
--- a/actionpack/test/abstract/localized_cache_test.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require 'abstract_unit'
-
-module AbstractController
- module Testing
-
- class CachedController < AbstractController::Base
- include AbstractController::Rendering
- include AbstractController::LocalizedCache
-
- self.view_paths = [ActionView::FixtureResolver.new(
- "default.erb" => "With Default",
- "template.erb" => "With Template",
- "some/file.erb" => "With File",
- "template_name.erb" => "With Template Name"
- )]
- end
-
- class TestLocalizedCache < ActiveSupport::TestCase
-
- def setup
- @controller = CachedController.new
- CachedController.clear_template_caches!
- end
-
- def test_templates_are_cached
- @controller.render :template => "default.erb"
- assert_equal "With Default", @controller.response_body
-
- cached = @controller.class.template_cache
- assert_equal 1, cached.size
- assert_kind_of ActionView::Template, cached.values.first["default.erb"]
- end
-
- def test_cache_is_used
- CachedController.new.render :template => "default.erb"
-
- @controller.expects(:find_template).never
- @controller.render :template => "default.erb"
-
- assert_equal 1, @controller.class.template_cache.size
- end
-
- def test_cache_changes_with_locale
- CachedController.new.render :template => "default.erb"
-
- I18n.locale = :es
- @controller.render :template => "default.erb"
-
- assert_equal 2, @controller.class.template_cache.size
- ensure
- I18n.locale = :en
- end
-
- end
-
- end
-end
diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb
index db924633ca..25dc8bd804 100644
--- a/actionpack/test/abstract/render_test.rb
+++ b/actionpack/test/abstract/render_test.rb
@@ -15,13 +15,8 @@ module AbstractController
"renderer/default.erb" => "With Default",
"renderer/string.erb" => "With String",
"renderer/symbol.erb" => "With Symbol",
- "renderer/template_name.erb" => "With Template Name",
"string/with_path.erb" => "With String With Path",
- "some/file.erb" => "With File",
- "with_format.html.erb" => "With html format",
- "with_format.xml.erb" => "With xml format",
- "with_locale.en.erb" => "With en locale",
- "with_locale.pl.erb" => "With pl locale"
+ "some/file.erb" => "With File"
)]
def template
@@ -55,30 +50,6 @@ module AbstractController
def symbol
render :symbol
end
-
- def template_name
- render :_template_name => :template_name
- end
-
- def object
- render :_template => ActionView::Template::Text.new("With Object")
- end
-
- def with_html_format
- render :template => "with_format", :format => :html
- end
-
- def with_xml_format
- render :template => "with_format", :format => :xml
- end
-
- def with_en_locale
- render :template => "with_locale"
- end
-
- def with_pl_locale
- render :template => "with_locale", :locale => :pl
- end
end
class TestRenderer < ActiveSupport::TestCase
@@ -126,36 +97,6 @@ module AbstractController
@controller.process(:string_with_path)
assert_equal "With String With Path", @controller.response_body
end
-
- def test_render_template_name
- @controller.process(:template_name)
- assert_equal "With Template Name", @controller.response_body
- end
-
- def test_render_object
- @controller.process(:object)
- assert_equal "With Object", @controller.response_body
- end
-
- def test_render_with_html_format
- @controller.process(:with_html_format)
- assert_equal "With html format", @controller.response_body
- end
-
- def test_render_with_xml_format
- @controller.process(:with_xml_format)
- assert_equal "With xml format", @controller.response_body
- end
-
- def test_render_with_en_locale
- @controller.process(:with_en_locale)
- assert_equal "With en locale", @controller.response_body
- end
-
- def test_render_with_pl_locale
- @controller.process(:with_pl_locale)
- assert_equal "With pl locale", @controller.response_body
- end
end
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 867e50d5b9..67aa412d3d 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -1,5 +1,11 @@
require File.expand_path('../../../load_paths', __FILE__)
+lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
+activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
+$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
+
$:.unshift(File.dirname(__FILE__) + '/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
@@ -10,7 +16,6 @@ require 'test/unit'
require 'abstract_controller'
require 'action_controller'
require 'action_view'
-require 'action_view/base'
require 'action_dispatch'
require 'fixture_template'
require 'active_support/dependencies'
@@ -64,29 +69,66 @@ module SetupOnce
end
end
-class ActiveSupport::TestCase
- include SetupOnce
+SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
+
+module ActiveSupport
+ class TestCase
+ include SetupOnce
+ # Hold off drawing routes until all the possible controller classes
+ # have been loaded.
+ setup_once do
+ SharedTestRoutes.draw do |map|
+ # FIXME: match ':controller(/:action(/:id))'
+ map.connect ':controller/:action/:id'
+ end
+
+ ActionController::IntegrationTest.app.router.draw do |map|
+ # FIXME: match ':controller(/:action(/:id))'
+ map.connect ':controller/:action/:id'
+ end
+ end
+ end
+end
+
+class RoutedRackApp
+ attr_reader :router
+ alias routes router
+
+ def initialize(router, &blk)
+ @router = router
+ @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@router)
+ end
+
+ def call(env)
+ @stack.call(env)
+ end
+end
- # Hold off drawing routes until all the possible controller classes
- # have been loaded.
- setup_once do
- ActionController::Routing::Routes.draw do |map|
- match ':controller(/:action(/:id))'
+class BasicController
+ attr_accessor :request
+
+ def config
+ @config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
+ # VIEW TODO: View tests should not require a controller
+ public_dir = File.expand_path("../fixtures/public", __FILE__)
+ config.assets_dir = public_dir
+ config.javascripts_dir = "#{public_dir}/javascripts"
+ config.stylesheets_dir = "#{public_dir}/stylesheets"
+ config
end
end
end
class ActionController::IntegrationTest < ActiveSupport::TestCase
def self.build_app(routes = nil)
- ActionDispatch::Flash
- ActionDispatch::MiddlewareStack.new { |middleware|
+ RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
middleware.use "ActionDispatch::ShowExceptions"
middleware.use "ActionDispatch::Callbacks"
middleware.use "ActionDispatch::ParamsParser"
middleware.use "ActionDispatch::Cookies"
middleware.use "ActionDispatch::Flash"
middleware.use "ActionDispatch::Head"
- }.build(routes || ActionController::Routing::Routes)
+ end
end
self.app = build_app
@@ -112,28 +154,22 @@ class ActionController::IntegrationTest < ActiveSupport::TestCase
end
def with_routing(&block)
- real_routes = ActionController::Routing::Routes
- ActionController::Routing.module_eval { remove_const :Routes }
-
- temporary_routes = ActionController::Routing::RouteSet.new
- self.class.app = self.class.build_app(temporary_routes)
- ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
+ temporary_routes = ActionDispatch::Routing::RouteSet.new
+ old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
+ old_routes = SharedTestRoutes
+ silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
yield temporary_routes
ensure
- if ActionController::Routing.const_defined? :Routes
- ActionController::Routing.module_eval { remove_const :Routes }
- end
- ActionController::Routing.const_set(:Routes, real_routes) if real_routes
- self.class.app = self.class.build_app
+ self.class.app = old_app
+ silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
end
end
# Temporary base class
class Rack::TestCase < ActionController::IntegrationTest
setup do
- ActionController::Base.session_options[:key] = "abc"
- ActionController::Base.session_options[:secret] = ("*" * 30)
+ ActionController::Base.config.secret = "abc" * 30
end
def self.testing(klass = nil)
@@ -180,6 +216,16 @@ end
class ::ApplicationController < ActionController::Base
end
+module ActionView
+ class TestCase
+ # Must repeat the setup because AV::TestCase is a duplication
+ # of AC::TestCase
+ setup do
+ @router = SharedTestRoutes
+ end
+ end
+end
+
module ActionController
class Base
include ActionController::Testing
@@ -190,6 +236,10 @@ module ActionController
class TestCase
include ActionDispatch::TestProcess
+ setup do
+ @router = SharedTestRoutes
+ end
+
def assert_template(options = {}, message = nil)
validate_request!
@@ -232,3 +282,10 @@ module ActionController
end
end
end
+
+# This stub emulates the Railtie including the URL helpers from a Rails application
+module ActionController
+ class Base
+ include SharedTestRoutes.url_helpers
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb
index ed8e324938..331f861d8f 100644
--- a/actionpack/test/activerecord/controller_runtime_test.rb
+++ b/actionpack/test/activerecord/controller_runtime_test.rb
@@ -1,37 +1,37 @@
require 'active_record_unit'
require 'active_record/railties/controller_runtime'
require 'fixtures/project'
-require 'rails/subscriber/test_helper'
-require 'action_controller/railties/subscriber'
+require 'rails/log_subscriber/test_helper'
+require 'action_controller/railties/log_subscriber'
ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime
-class ControllerRuntimeSubscriberTest < ActionController::TestCase
- class SubscriberController < ActionController::Base
+class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
+ class LogSubscriberController < ActionController::Base
def show
render :inline => "<%= Project.all %>"
end
end
-
- include Rails::Subscriber::TestHelper
- tests SubscriberController
+
+ include Rails::LogSubscriber::TestHelper
+ tests LogSubscriberController
def setup
- @old_logger = ActionController::Base.logger
- Rails::Subscriber.add(:action_controller, ActionController::Railties::Subscriber.new)
super
+ @old_logger = ActionController::Base.logger
+ Rails::LogSubscriber.add(:action_controller, ActionController::Railties::LogSubscriber.new)
end
def teardown
super
- Rails::Subscriber.subscribers.clear
+ Rails::LogSubscriber.log_subscribers.clear
ActionController::Base.logger = @old_logger
end
def set_logger(logger)
ActionController::Base.logger = logger
end
-
+
def test_log_with_active_record
get :show
wait
diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb
index ea82758cf5..5643ad5ad6 100644
--- a/actionpack/test/activerecord/polymorphic_routes_test.rb
+++ b/actionpack/test/activerecord/polymorphic_routes_test.rb
@@ -26,7 +26,7 @@ class Series < ActiveRecord::Base
end
class PolymorphicRoutesTest < ActionController::TestCase
- include ActionController::UrlFor
+ include SharedTestRoutes.url_helpers
self.default_url_options[:host] = 'example.com'
def setup
@@ -400,7 +400,7 @@ class PolymorphicRoutesTest < ActionController::TestCase
map.resources :series
end
- ActionController::Routing::Routes.install_helpers(self.class)
+ self.class.send(:include, @router.url_helpers)
yield
end
end
@@ -422,7 +422,7 @@ class PolymorphicRoutesTest < ActionController::TestCase
end
end
- ActionController::Routing::Routes.install_helpers(self.class)
+ self.class.send(:include, @router.url_helpers)
yield
end
end
@@ -441,7 +441,7 @@ class PolymorphicRoutesTest < ActionController::TestCase
end
end
- ActionController::Routing::Routes.install_helpers(self.class)
+ self.class.send(:include, @router.url_helpers)
yield
end
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index d54be9bdc0..26e0d6d844 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -253,12 +253,14 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
def test_assert_redirect_to_nested_named_route
+ @controller = Admin::InnerModuleController.new
+
with_routing do |set|
set.draw do |map|
match 'admin/inner_module', :to => 'admin/inner_module#index', :as => :admin_inner_module
- match ':controller/:action'
+ # match ':controller/:action'
+ map.connect ':controller/:action/:id'
end
- @controller = Admin::InnerModuleController.new
process :redirect_to_index
# redirection is <{"action"=>"index", "controller"=>"admin/admin/inner_module"}>
assert_redirected_to admin_inner_module_path
@@ -266,12 +268,14 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
def test_assert_redirected_to_top_level_named_route_from_nested_controller
+ @controller = Admin::InnerModuleController.new
+
with_routing do |set|
set.draw do |map|
match '/action_pack_assertions/:id', :to => 'action_pack_assertions#index', :as => :top_level
- match ':controller/:action'
+ # match ':controller/:action'
+ map.connect ':controller/:action/:id'
end
- @controller = Admin::InnerModuleController.new
process :redirect_to_top_level_named_route
# assert_redirected_to "http://test.host/action_pack_assertions/foo" would pass because of exact match early return
assert_redirected_to "/action_pack_assertions/foo"
@@ -279,13 +283,15 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
def test_assert_redirected_to_top_level_named_route_with_same_controller_name_in_both_namespaces
+ @controller = Admin::InnerModuleController.new
+
with_routing do |set|
set.draw do |map|
# this controller exists in the admin namespace as well which is the only difference from previous test
match '/user/:id', :to => 'user#index', :as => :top_level
- match ':controller/:action'
+ # match ':controller/:action'
+ map.connect ':controller/:action/:id'
end
- @controller = Admin::InnerModuleController.new
process :redirect_to_top_level_named_route
# assert_redirected_to top_level_url('foo') would pass because of exact match early return
assert_redirected_to top_level_path('foo')
@@ -463,9 +469,11 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
assert_redirected_to '/some/path'
end
- def test_redirected_to_url_no_leadling_slash
+ def test_redirected_to_url_no_leading_slash_fails
process :redirect_to_path
- assert_redirected_to 'some/path'
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_redirected_to 'some/path'
+ end
end
def test_redirected_to_url_full_url
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 612827dd41..cb3e848dfa 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -6,18 +6,7 @@
require 'abstract_unit'
require 'controller/fake_controllers'
-
-unless defined?(ActionMailer)
- begin
- $:.unshift("#{File.dirname(__FILE__)}/../../../actionmailer/lib")
- require 'action_mailer'
- rescue LoadError => e
- raise unless e.message =~ /action_mailer/
- require 'rubygems'
- gem 'actionmailer'
- end
-end
-
+require 'action_mailer'
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
class AssertSelectTest < ActionController::TestCase
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 4fcfbacf4e..f047e7da30 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -66,6 +66,19 @@ class DefaultUrlOptionsController < ActionController::Base
end
end
+class UrlOptionsController < ActionController::Base
+ def from_view
+ render :inline => "<%= #{params[:route]} %>"
+ end
+
+ def url_options
+ super.merge(:host => 'www.override.com', :action => 'new', :locale => 'en')
+ end
+end
+
+class RecordIdentifierController < ActionController::Base
+end
+
class ControllerClassTests < ActiveSupport::TestCase
def test_controller_path
assert_equal 'empty', EmptyController.controller_path
@@ -92,6 +105,11 @@ class ControllerClassTests < ActiveSupport::TestCase
assert_equal [:password], parameters
end
+
+ def test_record_identifier
+ assert_respond_to RecordIdentifierController.new, :dom_id
+ assert_respond_to RecordIdentifierController.new, :dom_class
+ end
end
class ControllerInstanceTests < Test::Unit::TestCase
@@ -113,6 +131,15 @@ class ControllerInstanceTests < Test::Unit::TestCase
assert_equal Set.new(%w(public_action)), c.class.__send__(:action_methods), "#{c.controller_path} should not be empty!"
end
end
+
+ def test_temporary_anonymous_controllers
+ name = 'ExamplesController'
+ klass = Class.new(ActionController::Base)
+ Object.const_set(name, klass)
+
+ controller = klass.new
+ assert_equal "examples", controller.controller_path
+ end
end
class PerformActionTest < ActionController::TestCase
@@ -153,6 +180,31 @@ class PerformActionTest < ActionController::TestCase
end
end
+class UrlOptionsTest < ActionController::TestCase
+ tests UrlOptionsController
+
+ def setup
+ super
+ @request.host = 'www.example.com'
+ rescue_action_in_public!
+ end
+
+ def test_url_options_override
+ with_routing do |set|
+ set.draw do |map|
+ match 'from_view', :to => 'url_options#from_view', :as => :from_view
+ match ':controller/:action'
+ end
+
+ get :from_view, :route => "from_view_url"
+
+ assert_equal 'http://www.override.com/from_view?locale=en', @response.body
+ assert_equal 'http://www.override.com/from_view?locale=en', @controller.send(:from_view_url)
+ assert_equal 'http://www.override.com/default_url_options/new?locale=en', @controller.url_for(:controller => 'default_url_options')
+ end
+ end
+end
+
class DefaultUrlOptionsTest < ActionController::TestCase
tests DefaultUrlOptionsController
@@ -162,7 +214,7 @@ class DefaultUrlOptionsTest < ActionController::TestCase
rescue_action_in_public!
end
- def test_default_url_options_are_used_if_set
+ def test_default_url_options_override
with_routing do |set|
set.draw do |map|
match 'from_view', :to => 'default_url_options#from_view', :as => :from_view
@@ -219,12 +271,15 @@ class EmptyUrlOptionsTest < ActionController::TestCase
end
def test_named_routes_with_path_without_doing_a_request_first
+ @controller = EmptyController.new
+ @controller.request = @request
+
with_routing do |set|
set.draw do |map|
resources :things
end
- assert_equal '/things', EmptyController.new.send(:things_path)
+ assert_equal '/things', @controller.send(:things_path)
end
end
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index de92fc56fd..a3c8fdbb6e 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -6,12 +6,18 @@ 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)
ActionController::Base.page_cache_directory = FILE_STORE_PATH
-ActionController::Base.cache_store = :file_store, FILE_STORE_PATH
-class PageCachingTestController < ActionController::Base
+class CachingController < ActionController::Base
+ abstract!
+
+ self.cache_store = :file_store, FILE_STORE_PATH
+end
+
+class PageCachingTestController < CachingController
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
caches_page :found, :not_found
+
def ok
head :ok
end
@@ -51,12 +57,13 @@ class PageCachingTest < ActionController::TestCase
@request = ActionController::TestRequest.new
@request.host = 'hostname.com'
+ @request.env.delete('PATH_INFO')
@response = ActionController::TestResponse.new
@controller = PageCachingTestController.new
+ @controller.cache_store = :file_store, FILE_STORE_PATH
- @params = {:controller => 'posts', :action => 'index', :only_path => true, :skip_relative_url_root => true}
- @rewriter = ActionController::UrlRewriter.new(@request, @params)
+ @params = {:controller => 'posts', :action => 'index', :only_path => true}
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
FileUtils.mkdir_p(FILE_STORE_PATH)
@@ -74,9 +81,9 @@ class PageCachingTest < ActionController::TestCase
match '/', :to => 'posts#index', :as => :main
end
@params[:format] = 'rss'
- assert_equal '/posts.rss', @rewriter.rewrite(@params)
+ assert_equal '/posts.rss', @router.url_for(@params)
@params[:format] = nil
- assert_equal '/', @rewriter.rewrite(@params)
+ assert_equal '/', @router.url_for(@params)
end
end
@@ -110,7 +117,7 @@ class PageCachingTest < ActionController::TestCase
end
def test_should_cache_ok_at_custom_path
- @request.request_uri = "/index.html"
+ @request.env['PATH_INFO'] = '/index.html'
get :ok
assert_response :ok
assert File.exist?("#{FILE_STORE_PATH}/index.html")
@@ -147,7 +154,7 @@ class PageCachingTest < ActionController::TestCase
end
end
-class ActionCachingTestController < ActionController::Base
+class ActionCachingTestController < CachingController
rescue_from(Exception) { head 500 }
if defined? ActiveRecord
rescue_from(ActiveRecord::RecordNotFound) { head :not_found }
@@ -305,12 +312,9 @@ class ActionCacheTest < ActionController::TestCase
end
def test_action_cache_conditional_options
- old_use_accept_header = ActionController::Base.use_accept_header
- ActionController::Base.use_accept_header = true
@request.env['HTTP_ACCEPT'] = 'application/json'
get :index
assert !fragment_exist?('hostname.com/action_caching_test')
- ActionController::Base.use_accept_header = old_use_accept_header
end
def test_action_cache_with_store_options
@@ -511,6 +515,7 @@ class ActionCacheTest < ActionController::TestCase
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@controller = ActionCachingTestController.new
+ @controller.singleton_class.send(:include, @router.url_helpers)
@request.host = 'hostname.com'
end
@@ -523,7 +528,7 @@ class ActionCacheTest < ActionController::TestCase
end
end
-class FragmentCachingTestController < ActionController::Base
+class FragmentCachingTestController < CachingController
def some_action; end;
end
@@ -532,8 +537,8 @@ class FragmentCachingTest < ActionController::TestCase
super
ActionController::Base.perform_caching = true
@store = ActiveSupport::Cache::MemoryStore.new
- ActionController::Base.cache_store = @store
@controller = FragmentCachingTestController.new
+ @controller.cache_store = @store
@params = {:controller => 'posts', :action => 'index'}
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@@ -631,7 +636,7 @@ class FragmentCachingTest < ActionController::TestCase
end
-class FunctionalCachingController < ActionController::Base
+class FunctionalCachingController < CachingController
def fragment_cached
end
@@ -665,8 +670,8 @@ class FunctionalFragmentCachingTest < ActionController::TestCase
super
ActionController::Base.perform_caching = true
@store = ActiveSupport::Cache::MemoryStore.new
- ActionController::Base.cache_store = @store
@controller = FunctionalCachingController.new
+ @controller.cache_store = @store
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index e5ffe20ecc..967107853b 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -145,18 +145,6 @@ end
class AcceptBasedContentTypeTest < ActionController::TestCase
tests OldContentTypeController
- def setup
- super
- @_old_accept_header = ActionController::Base.use_accept_header
- ActionController::Base.use_accept_header = true
- end
-
- def teardown
- super
- ActionController::Base.use_accept_header = @_old_accept_header
- end
-
-
def test_render_default_content_types_for_respond_to
@request.accept = Mime::HTML.to_s
get :render_default_content_types_for_respond_to
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index f5ccef8aaf..908967a110 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -157,7 +157,7 @@ class CookieTest < ActionController::TestCase
def assert_cookie_header(expected)
header = @response.headers["Set-Cookie"]
if header.respond_to?(:to_str)
- assert_equal expected, header
+ assert_equal expected.split("\n").sort, header.split("\n").sort
else
assert_equal expected.split("\n"), header
end
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index 7e9a2625f1..eb2af523a2 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -40,11 +40,13 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
setup do
# Used as secret in generating nonce to prevent tampering of timestamp
- @old_secret, ActionController::Base.session_options[:secret] = ActionController::Base.session_options[:secret], "session_options_secret"
+ @secret = "session_options_secret"
+ @controller.config.secret = @secret
+ # @old_secret, ActionController::Base.config.secret[:secret] = ActionController::Base.session_options[:secret], @secret
end
teardown do
- ActionController::Base.session_options[:secret] = @old_secret
+ # ActionController::Base.session_options[:secret] = @old_secret
end
AUTH_HEADERS.each do |header|
@@ -138,7 +140,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "authentication request with request-uri that doesn't match credentials digest-uri" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
- @request.env['REQUEST_URI'] = "/http_digest_authentication_test/dummy_digest/altered/uri"
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest/altered/uri"
get :display
assert_response :unauthorized
@@ -147,7 +149,8 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "authentication request with absolute request uri (as in webrick)" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
- @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest"
+ @request.env["SERVER_NAME"] = "test.host"
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest"
get :display
@@ -170,7 +173,8 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "authentication request with absolute uri in both request and credentials (as in Webrick with IE)" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest",
:username => 'pretty', :password => 'please')
- @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest"
+ @request.env['SERVER_NAME'] = "test.host"
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest"
get :display
@@ -202,7 +206,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "validate_digest_response should fail with nil returning password_procedure" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => nil, :password => nil)
- assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret"){nil}
+ assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@secret, @request, "SuperSecret"){nil}
end
private
@@ -225,7 +229,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
credentials = decode_credentials(@response.headers['WWW-Authenticate'])
credentials.merge!(options)
- credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s)
+ credentials.merge!(:uri => @request.env['PATH_INFO'].to_s)
ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 683ab5236c..2180466ca7 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -75,23 +75,6 @@ class SessionTest < Test::Unit::TestCase
@session.delete_via_redirect(path, args, headers)
end
- def test_url_for_with_controller
- options = {:action => 'show'}
- mock_controller = mock()
- mock_controller.expects(:url_for).with(options).returns('/show')
- @session.stubs(:controller).returns(mock_controller)
- assert_equal '/show', @session.url_for(options)
- end
-
- def test_url_for_without_controller
- options = {:action => 'show'}
- mock_rewriter = mock()
- mock_rewriter.expects(:rewrite).with(options).returns('/show')
- @session.stubs(:generic_url_rewriter).returns(mock_rewriter)
- @session.stubs(:controller).returns(nil)
- assert_equal '/show', @session.url_for(options)
- end
-
def test_get
path = "/index"; params = "blah"; headers = {:location => 'blah'}
@session.expects(:process).with(:get,path,params,headers)
@@ -195,8 +178,8 @@ class IntegrationTestTest < Test::Unit::TestCase
session1 = @test.open_session { |sess| }
session2 = @test.open_session # implicit session
- assert_equal ::ActionController::Integration::Session, session1.class
- assert_equal ::ActionController::Integration::Session, session2.class
+ assert_kind_of ::ActionController::Integration::Session, session1
+ assert_kind_of ::ActionController::Integration::Session, session2
assert_not_equal session1, session2
end
@@ -301,18 +284,13 @@ class IntegrationProcessTest < ActionController::IntegrationTest
end
end
- def test_cookie_monster
+ test 'response cookies are added to the cookie jar for the next request' do
with_test_route_set do
self.cookies['cookie_1'] = "sugar"
self.cookies['cookie_2'] = "oatmeal"
get '/cookie_monster'
- assert_equal 410, status
- assert_equal "Gone", status_message
- assert_response 410
- assert_response :gone
assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"]
assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies.to_hash)
- assert_equal "Gone", response.body
end
end
@@ -350,7 +328,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest
with_test_route_set do
get '/get_with_params?foo=bar'
assert_equal '/get_with_params?foo=bar', request.env["REQUEST_URI"]
- assert_equal '/get_with_params?foo=bar', request.request_uri
+ assert_equal '/get_with_params?foo=bar', request.fullpath
assert_equal "foo=bar", request.env["QUERY_STRING"]
assert_equal 'foo=bar', request.query_string
assert_equal 'bar', request.parameters['foo']
@@ -363,8 +341,8 @@ class IntegrationProcessTest < ActionController::IntegrationTest
def test_get_with_parameters
with_test_route_set do
get '/get_with_params', :foo => "bar"
- assert_equal '/get_with_params', request.env["REQUEST_URI"]
- assert_equal '/get_with_params', request.request_uri
+ assert_equal '/get_with_params', request.env["PATH_INFO"]
+ assert_equal '/get_with_params', request.path_info
assert_equal 'foo=bar', request.env["QUERY_STRING"]
assert_equal 'foo=bar', request.query_string
assert_equal 'bar', request.parameters['foo']
@@ -401,16 +379,26 @@ class IntegrationProcessTest < ActionController::IntegrationTest
private
def with_test_route_set
with_routing do |set|
+ controller = ::IntegrationProcessTest::IntegrationController.clone
+ controller.class_eval do
+ include set.url_helpers
+ end
+
set.draw do |map|
- match ':action', :to => ::IntegrationProcessTest::IntegrationController
- get 'get/:action', :to => ::IntegrationProcessTest::IntegrationController
+ match ':action', :to => controller
+ get 'get/:action', :to => controller
end
+
+ self.singleton_class.send(:include, set.url_helpers)
+
yield
end
end
end
class MetalIntegrationTest < ActionController::IntegrationTest
+ include SharedTestRoutes.url_helpers
+
class Poller
def self.call(env)
if env["PATH_INFO"] =~ /^\/success/
diff --git a/actionpack/test/controller/subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index d7c1166f14..20d3e77b6e 100644
--- a/actionpack/test/controller/subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -1,9 +1,9 @@
require "abstract_unit"
-require "rails/subscriber/test_helper"
-require "action_controller/railties/subscriber"
+require "rails/log_subscriber/test_helper"
+require "action_controller/railties/log_subscriber"
module Another
- class SubscribersController < ActionController::Base
+ class LogSubscribersController < ActionController::Base
def show
render :nothing => true
end
@@ -35,24 +35,24 @@ module Another
end
end
-class ACSubscriberTest < ActionController::TestCase
- tests Another::SubscribersController
- include Rails::Subscriber::TestHelper
+class ACLogSubscriberTest < ActionController::TestCase
+ tests Another::LogSubscribersController
+ include Rails::LogSubscriber::TestHelper
def setup
+ super
+
@old_logger = ActionController::Base.logger
@cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__))
ActionController::Base.page_cache_directory = @cache_path
- ActionController::Base.cache_store = :file_store, @cache_path
-
- Rails::Subscriber.add(:action_controller, ActionController::Railties::Subscriber.new)
- super
+ @controller.cache_store = :file_store, @cache_path
+ Rails::LogSubscriber.add(:action_controller, ActionController::Railties::LogSubscriber.new)
end
def teardown
super
- Rails::Subscriber.subscribers.clear
+ Rails::LogSubscriber.log_subscribers.clear
FileUtils.rm_rf(@cache_path)
ActionController::Base.logger = @old_logger
end
@@ -65,7 +65,7 @@ class ACSubscriberTest < ActionController::TestCase
get :show
wait
assert_equal 2, logs.size
- assert_equal "Processing by Another::SubscribersController#show as HTML", logs.first
+ assert_equal "Processing by Another::LogSubscribersController#show as HTML", logs.first
end
def test_process_action
@@ -134,11 +134,11 @@ class ACSubscriberTest < ActionController::TestCase
end
def test_send_xfile
- get :xfile_sender
+ assert_deprecated { get :xfile_sender }
wait
assert_equal 3, logs.size
- assert_match /Sent X\-Sendfile header/, logs[1]
+ assert_match /Sent file/, logs[1]
assert_match /test\/fixtures\/company\.rb/, logs[1]
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 3bd3369242..5c1eaf453c 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -155,13 +155,11 @@ class RespondToControllerTest < ActionController::TestCase
def setup
super
- ActionController::Base.use_accept_header = true
@request.host = "www.example.com"
end
def teardown
super
- ActionController::Base.use_accept_header = false
end
def test_html
@@ -513,7 +511,7 @@ class RespondWithController < ActionController::Base
protected
def resource
- Customer.new("david", 13)
+ Customer.new("david", request.delete? ? nil : 13)
end
def _render_js(js, options)
@@ -544,13 +542,11 @@ class RespondWithControllerTest < ActionController::TestCase
def setup
super
- ActionController::Base.use_accept_header = true
@request.host = "www.example.com"
end
def teardown
super
- ActionController::Base.use_accept_header = false
end
def test_using_resource
@@ -717,7 +713,7 @@ class RespondWithControllerTest < ActionController::TestCase
delete :using_resource
assert_equal "text/html", @response.content_type
assert_equal 302, @response.status
- assert_equal "http://www.example.com/customers/13", @response.location
+ assert_equal "http://www.example.com/customers", @response.location
end
end
diff --git a/actionpack/test/controller/new_base/render_rjs_test.rb b/actionpack/test/controller/new_base/render_rjs_test.rb
index 8c47b38ab6..f4516ade63 100644
--- a/actionpack/test/controller/new_base/render_rjs_test.rb
+++ b/actionpack/test/controller/new_base/render_rjs_test.rb
@@ -17,7 +17,7 @@ module RenderRjs
end
def index_locale
- old_locale, I18n.locale = I18n.locale, :da
+ self.locale = :da
end
end
diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb
index 6b6d154faa..813dedc80d 100644
--- a/actionpack/test/controller/record_identifier_test.rb
+++ b/actionpack/test/controller/record_identifier_test.rb
@@ -5,6 +5,7 @@ class Comment
include ActiveModel::Conversion
attr_reader :id
+ def to_key; id ? [id] : nil end
def save; @id = 1 end
def new_record?; @id.nil? end
def name
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 570ff4a41b..441bc47908 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -6,14 +6,14 @@ end
class Workshop
extend ActiveModel::Naming
include ActiveModel::Conversion
- attr_accessor :id, :new_record
+ attr_accessor :id
- def initialize(id, new_record)
- @id, @new_record = id, new_record
+ def initialize(id)
+ @id = id
end
- def new_record?
- @new_record
+ def persisted?
+ id.present?
end
def to_s
@@ -88,11 +88,11 @@ class RedirectController < ActionController::Base
end
def redirect_to_existing_record
- redirect_to Workshop.new(5, false)
+ redirect_to Workshop.new(5)
end
def redirect_to_new_record
- redirect_to Workshop.new(5, true)
+ redirect_to Workshop.new(nil)
end
def redirect_to_nil
@@ -239,11 +239,11 @@ class RedirectTest < ActionController::TestCase
get :redirect_to_existing_record
assert_equal "http://test.host/workshops/5", redirect_to_url
- assert_redirected_to Workshop.new(5, false)
+ assert_redirected_to Workshop.new(5)
get :redirect_to_new_record
assert_equal "http://test.host/workshops", redirect_to_url
- assert_redirected_to Workshop.new(5, true)
+ assert_redirected_to Workshop.new(nil)
end
end
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
index 3938fc7061..2580ada88b 100644
--- a/actionpack/test/controller/render_json_test.rb
+++ b/actionpack/test/controller/render_json_test.rb
@@ -18,6 +18,10 @@ class RenderJsonTest < ActionController::TestCase
render :json => ActiveSupport::JSON.encode(:hello => 'world')
end
+ def render_json_hello_world_with_status
+ render :json => ActiveSupport::JSON.encode(:hello => 'world'), :status => 401
+ end
+
def render_json_hello_world_with_callback
render :json => ActiveSupport::JSON.encode(:hello => 'world'), :callback => 'alert'
end
@@ -58,6 +62,12 @@ class RenderJsonTest < ActionController::TestCase
assert_equal 'application/json', @response.content_type
end
+ def test_render_json_with_status
+ get :render_json_hello_world_with_status
+ assert_equal '{"hello":"world"}', @response.body
+ assert_equal 401, @response.status
+ end
+
def test_render_json_with_callback
get :render_json_hello_world_with_callback
assert_equal 'alert({"hello":"world"})', @response.body
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 2c3dc2a72d..e3c4869391 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -21,6 +21,10 @@ class TestController < ActionController::Base
def hello_world
end
+ def hello_world_file
+ render :file => File.expand_path("../../fixtures/hello.html", __FILE__)
+ end
+
def conditional_hello
if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123])
render :action => 'hello_world'
@@ -214,6 +218,10 @@ class TestController < ActionController::Base
render :text => false
end
+ def render_text_with_resource
+ render :text => Customer.new("David")
+ end
+
# :ported:
def render_nothing_with_appendix
render :text => "appended"
@@ -347,7 +355,7 @@ class TestController < ActionController::Base
@before = "i'm before the render"
render_to_string :text => "foo"
@after = "i'm after the render"
- render :action => "test/hello_world"
+ render :template => "test/hello_world"
end
def render_to_string_with_exception
@@ -361,7 +369,7 @@ class TestController < ActionController::Base
rescue
end
@after = "i'm after the render"
- render :action => "test/hello_world"
+ render :template => "test/hello_world"
end
def accessing_params_in_template_with_layout
@@ -506,7 +514,7 @@ class TestController < ActionController::Base
def render_to_string_with_partial
@partial_only = render_to_string :partial => "partial_only"
@partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") }
- render :action => "test/hello_world"
+ render :template => "test/hello_world"
end
def partial_with_counter
@@ -747,6 +755,11 @@ class RenderTest < ActionController::TestCase
assert_equal "The secret is in the sauce\n", @response.body
end
+ def test_render_file
+ get :hello_world_file
+ assert_equal "Hello world!", @response.body
+ end
+
# :ported:
def test_render_file_as_string_with_instance_variables
get :render_file_as_string_with_instance_variables
@@ -817,6 +830,11 @@ class RenderTest < ActionController::TestCase
assert_equal 'appended', @response.body
end
+ def test_render_text_with_resource
+ get :render_text_with_resource
+ assert_equal 'name: "David"', @response.body
+ end
+
# :ported:
def test_attempt_to_access_object_method
assert_raise(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb
index b5b0d0b9d5..4da6c954cf 100644
--- a/actionpack/test/controller/render_xml_test.rb
+++ b/actionpack/test/controller/render_xml_test.rb
@@ -62,7 +62,8 @@ class RenderXmlTest < ActionController::TestCase
with_routing do |set|
set.draw do |map|
resources :customers
- match ':controller/:action'
+ # match ':controller/:action'
+ map.connect ':controller/:action/:id'
end
get :render_with_object_location
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 37367eaafc..dd991898a8 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -326,7 +326,7 @@ class RescueTest < ActionController::IntegrationTest
end
test 'rescue routing exceptions' do
- @app = ActionDispatch::Rescue.new(ActionController::Routing::Routes) do
+ @app = ActionDispatch::Rescue.new(SharedTestRoutes) do
rescue_from ActionController::RoutingError, lambda { |env| [200, {"Content-Type" => "text/html"}, ["Gotcha!"]] }
end
@@ -335,7 +335,7 @@ class RescueTest < ActionController::IntegrationTest
end
test 'unrescued exception' do
- @app = ActionDispatch::Rescue.new(ActionController::Routing::Routes)
+ @app = ActionDispatch::Rescue.new(SharedTestRoutes)
assert_raise(ActionController::RoutingError) { get '/b00m' }
end
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 01ed491732..f60045bba6 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -31,10 +31,10 @@ end
class ResourcesTest < ActionController::TestCase
def test_should_arrange_actions
- resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages,
+ resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages, {
:collection => { :rss => :get, :reorder => :post, :csv => :post },
:member => { :rss => :get, :atom => :get, :upload => :post, :fix => :post },
- :new => { :preview => :get, :draft => :get })
+ :new => { :preview => :get, :draft => :get }}, {})
assert_resource_methods [:rss], resource, :collection, :get
assert_resource_methods [:csv, :reorder], resource, :collection, :post
@@ -44,18 +44,18 @@ class ResourcesTest < ActionController::TestCase
end
def test_should_resource_controller_name_equal_resource_name_by_default
- resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages, {})
+ resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages, {}, {})
assert_equal 'messages', resource.controller
end
def test_should_resource_controller_name_equal_controller_option
- resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages, :controller => 'posts')
+ resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages, {:controller => 'posts'}, {})
assert_equal 'posts', resource.controller
end
def test_should_all_singleton_paths_be_the_same
[ :path, :nesting_path_prefix, :member_path ].each do |method|
- resource = ActionDispatch::Routing::DeprecatedMapper::SingletonResource.new(:messages, :path_prefix => 'admin')
+ resource = ActionDispatch::Routing::DeprecatedMapper::SingletonResource.new(:messages, {:path_prefix => 'admin'}, {})
assert_equal 'admin/messages', resource.send(method)
end
end
@@ -111,8 +111,8 @@ class ResourcesTest < ActionController::TestCase
end
def test_override_paths_for_default_restful_actions
- resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages,
- :path_names => {:new => 'nuevo', :edit => 'editar'})
+ resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages, {
+ :path_names => {:new => 'nuevo', :edit => 'editar'}}, {})
assert_equal resource.new_path, "#{resource.path}/nuevo"
end
@@ -125,7 +125,7 @@ class ResourcesTest < ActionController::TestCase
def test_with_custom_conditions
with_restful_routing :messages, :conditions => { :subdomain => 'app' } do
- assert ActionDispatch::Routing::Routes.recognize_path("/messages", :method => :get, :subdomain => 'app')
+ assert @router.recognize_path("/messages", :method => :get, :subdomain => 'app')
end
end
@@ -394,7 +394,7 @@ class ResourcesTest < ActionController::TestCase
assert_restful_routes_for :messages do |options|
assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
assert_raise(ActionController::RoutingError) do
- ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post)
+ @router.recognize_path("/messages/new", :method => :post)
end
end
end
@@ -504,7 +504,7 @@ class ResourcesTest < ActionController::TestCase
def test_restful_routes_dont_generate_duplicates
with_restful_routing :messages do
- routes = ActionController::Routing::Routes.routes
+ routes = @router.routes
routes.each do |route|
routes.each do |r|
next if route === r # skip the comparison instance
@@ -1162,8 +1162,9 @@ class ResourcesTest < ActionController::TestCase
options[:shallow_options] = options[:options]
end
- new_action = ActionController::Base.resources_path_names[:new] || "new"
- edit_action = ActionController::Base.resources_path_names[:edit] || "edit"
+ new_action = @router.resources_path_names[:new] || "new"
+ edit_action = @router.resources_path_names[:edit] || "edit"
+
if options[:path_names]
new_action = options[:path_names][:new] if options[:path_names][:new]
edit_action = options[:path_names][:edit] if options[:path_names][:edit]
@@ -1229,6 +1230,7 @@ class ResourcesTest < ActionController::TestCase
end
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
+ @controller.singleton_class.send(:include, @router.url_helpers)
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index, options[:options]
@@ -1298,6 +1300,7 @@ class ResourcesTest < ActionController::TestCase
def assert_singleton_named_routes_for(singleton_name, options = {})
(options[:options] ||= {})[:controller] ||= singleton_name.to_s.pluralize
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
+ @controller.singleton_class.send(:include, @router.url_helpers)
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :show, options[:options]
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index f390bbdc89..fc85b01394 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -52,29 +52,17 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
end
class MockController
- attr_accessor :routes
+ def self.build(helpers)
+ Class.new do
+ def url_for(options)
+ options[:protocol] ||= "http"
+ options[:host] ||= "test.host"
- def initialize(routes)
- self.routes = routes
- end
-
- def url_for(options)
- only_path = options.delete(:only_path)
-
- port = options.delete(:port) || 80
- port_string = port == 80 ? '' : ":#{port}"
-
- protocol = options.delete(:protocol) || "http"
- host = options.delete(:host) || "test.host"
- anchor = "##{options.delete(:anchor)}" if options.key?(:anchor)
-
- path = routes.generate(options)
-
- only_path ? "#{path}#{anchor}" : "#{protocol}://#{host}#{port_string}#{path}#{anchor}"
- end
+ super(options)
+ end
- def request
- @request ||= ActionController::TestRequest.new
+ include helpers
+ end
end
end
@@ -268,9 +256,7 @@ class LegacyRouteSetTests < Test::Unit::TestCase
end
def setup_for_named_route
- klass = Class.new(MockController)
- rs.install_helpers(klass)
- klass.new(rs)
+ MockController.build(rs.url_helpers).new
end
def test_named_route_without_hash
@@ -759,9 +745,7 @@ class RouteSetTest < ActiveSupport::TestCase
map.users '/admin/users', :controller => 'admin/users', :action => 'index'
end
- klass = Class.new(MockController)
- set.install_helpers(klass)
- klass.new(set)
+ MockController.build(set.url_helpers).new
end
def test_named_route_hash_access_method
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 0afebac68c..30c9a65b7c 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -46,44 +46,32 @@ class SendFileTest < ActionController::TestCase
response = nil
assert_nothing_raised { response = process('file') }
assert_not_nil response
- assert_kind_of String, response.body
- assert_equal file_data, response.body
+ body = response.body
+ assert_kind_of String, body
+ assert_equal file_data, body
end
def test_file_stream
- pending do
- response = nil
- assert_nothing_raised { response = process('file') }
- assert_not_nil response
- assert_kind_of Array, response.body_parts
-
- require 'stringio'
- output = StringIO.new
- output.binmode
- output.string.force_encoding(file_data.encoding) if output.string.respond_to?(:force_encoding)
- assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } }
- assert_equal file_data, output.string
- end
- end
-
- def test_file_url_based_filename
- @controller.options = { :url_based_filename => true }
response = nil
assert_nothing_raised { response = process('file') }
assert_not_nil response
- assert_equal "attachment", response.headers["Content-Disposition"]
- end
+ assert response.body_parts.respond_to?(:each)
+ assert response.body_parts.respond_to?(:to_path)
- def test_x_sendfile_header
- @controller.options = { :x_sendfile => true }
+ require 'stringio'
+ output = StringIO.new
+ output.binmode
+ output.string.force_encoding(file_data.encoding) if output.string.respond_to?(:force_encoding)
+ assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } }
+ assert_equal file_data, output.string
+ end
+ def test_file_url_based_filename
+ @controller.options = { :url_based_filename => true }
response = nil
assert_nothing_raised { response = process('file') }
assert_not_nil response
-
- assert_equal @controller.file_path, response.headers['X-Sendfile']
- assert response.body.blank?
- assert !response.etag?
+ assert_equal "attachment", response.headers["Content-Disposition"]
end
def test_data
@@ -104,9 +92,8 @@ class SendFileTest < ActionController::TestCase
end
# Test that send_file_headers! is setting the correct HTTP headers.
- def test_send_file_headers!
+ def test_send_file_headers_bang
options = {
- :length => 1,
:type => Mime::PNG,
:disposition => 'disposition',
:filename => 'filename'
@@ -121,13 +108,11 @@ class SendFileTest < ActionController::TestCase
@controller.send(:send_file_headers!, options)
h = @controller.headers
- assert_equal '1', h['Content-Length']
assert_equal 'image/png', @controller.content_type
assert_equal 'disposition; filename="filename"', h['Content-Disposition']
assert_equal 'binary', h['Content-Transfer-Encoding']
# test overriding Cache-Control: no-cache header to fix IE open/save dialog
- @controller.headers = { 'Cache-Control' => 'no-cache' }
@controller.send(:send_file_headers!, options)
@controller.response.prepare!
assert_equal 'private', h['Cache-Control']
@@ -135,7 +120,6 @@ class SendFileTest < ActionController::TestCase
def test_send_file_headers_with_mime_lookup_with_symbol
options = {
- :length => 1,
:type => :png
}
@@ -148,7 +132,6 @@ class SendFileTest < ActionController::TestCase
def test_send_file_headers_with_bad_symbol
options = {
- :length => 1,
:type => :this_type_is_not_registered
}
@@ -163,17 +146,16 @@ class SendFileTest < ActionController::TestCase
assert_equal 500, @response.status
end
+ define_method "test_send_#{method}_content_type" do
+ @controller.options = { :stream => false, :content_type => "application/x-ruby" }
+ assert_nothing_raised { assert_not_nil process(method) }
+ assert_equal "application/x-ruby", @response.content_type
+ end
+
define_method "test_default_send_#{method}_status" do
@controller.options = { :stream => false }
assert_nothing_raised { assert_not_nil process(method) }
assert_equal 200, @response.status
end
end
-
- def test_send_data_content_length_header
- @controller.headers = {}
- @controller.options = { :type => :text, :filename => 'file_with_utf8_text' }
- process('multibyte_text_data')
- assert_equal '29', @controller.headers['Content-Length']
- end
end
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index 0f074b32e6..f6ba275849 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -42,7 +42,7 @@ class TestTest < ActionController::TestCase
end
def test_uri
- render :text => request.request_uri
+ render :text => request.fullpath
end
def test_query_string
@@ -128,6 +128,7 @@ XML
@controller = TestController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
+ @request.env['PATH_INFO'] = nil
end
def test_raw_post_handling
@@ -199,7 +200,7 @@ XML
end
def test_process_with_request_uri_with_params_with_explicit_uri
- @request.request_uri = "/explicit/uri"
+ @request.env['PATH_INFO'] = "/explicit/uri"
process :test_uri, :id => 7
assert_equal "/explicit/uri", @response.body
end
@@ -210,7 +211,8 @@ XML
end
def test_process_with_query_string_with_explicit_uri
- @request.request_uri = "/explicit/uri?q=test?extra=question"
+ @request.env['PATH_INFO'] = '/explicit/uri'
+ @request.env['QUERY_STRING'] = 'q=test?extra=question'
process :test_query_string
assert_equal "q=test?extra=question", @response.body
end
@@ -476,8 +478,8 @@ XML
end
def test_with_routing_places_routes_back
- assert ActionController::Routing::Routes
- routes_id = ActionController::Routing::Routes.object_id
+ assert @router
+ routes_id = @router.object_id
begin
with_routing { raise 'fail' }
@@ -485,8 +487,8 @@ XML
rescue RuntimeError
end
- assert ActionController::Routing::Routes
- assert_equal routes_id, ActionController::Routing::Routes.object_id
+ assert @router
+ assert_equal routes_id, @router.object_id
end
def test_remote_addr
diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index 749fa5861f..fc7773dffe 100644
--- a/actionpack/test/controller/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -5,7 +5,7 @@ module AbstractController
class UrlForTests < ActionController::TestCase
class W
- include ActionController::UrlFor
+ include SharedTestRoutes.url_helpers
end
def teardown
@@ -113,15 +113,13 @@ module AbstractController
end
def test_relative_url_root_is_respected
- orig_relative_url_root = ActionController::Base.relative_url_root
- ActionController::Base.relative_url_root = '/subdir'
+ # ROUTES TODO: Tests should not have to pass :relative_url_root directly. This
+ # should probably come from the router.
add_host!
assert_equal('https://www.basecamphq.com/subdir/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https', :script_name => '/subdir')
)
- ensure
- ActionController::Base.relative_url_root = orig_relative_url_root
end
def test_named_routes
@@ -132,7 +130,8 @@ module AbstractController
end
# We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlFor }
+ kls = Class.new { include set.url_helpers }
+
controller = kls.new
assert controller.respond_to?(:home_url)
assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
@@ -145,22 +144,17 @@ module AbstractController
end
def test_relative_url_root_is_respected_for_named_routes
- orig_relative_url_root = ActionController::Base.relative_url_root
- ActionController::Base.relative_url_root = '/subdir'
-
with_routing do |set|
set.draw do |map|
match '/home/sweet/home/:user', :to => 'home#index', :as => :home
end
- kls = Class.new { include ActionController::UrlFor }
+ kls = Class.new { include set.url_helpers }
controller = kls.new
assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
- controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
+ controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again', :script_name => "/subdir")
end
- ensure
- ActionController::Base.relative_url_root = orig_relative_url_root
end
def test_only_path
@@ -171,7 +165,7 @@ module AbstractController
end
# We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlFor }
+ kls = Class.new { include set.url_helpers }
controller = kls.new
assert controller.respond_to?(:home_url)
assert_equal '/brave/new/world',
@@ -239,7 +233,7 @@ module AbstractController
end
# We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlFor }
+ kls = Class.new { include set.url_helpers }
kls.default_url_options[:host] = 'www.basecamphq.com'
controller = kls.new
diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb
index c2b8cd85d8..7b46a48a1d 100644
--- a/actionpack/test/controller/url_rewriter_test.rb
+++ b/actionpack/test/controller/url_rewriter_test.rb
@@ -1,102 +1,85 @@
require 'abstract_unit'
require 'controller/fake_controllers'
-ActionController::UrlRewriter
-
class UrlRewriterTests < ActionController::TestCase
+ class Rewriter
+ def initialize(request)
+ @options = {
+ :host => request.host_with_port,
+ :protocol => request.protocol
+ }
+ end
+
+ def rewrite(router, options)
+ router.url_for(@options.merge(options))
+ end
+ end
+
def setup
@request = ActionController::TestRequest.new
@params = {}
- @rewriter = ActionController::UrlRewriter.new(@request, @params)
+ @rewriter = Rewriter.new(@request) #.new(@request, @params)
end
def test_port
assert_equal('http://test.host:1271/c/a/i',
- @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :port => 1271)
+ @rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :port => 1271)
)
end
def test_protocol_with_and_without_separator
assert_equal('https://test.host/c/a/i',
- @rewriter.rewrite(:protocol => 'https', :controller => 'c', :action => 'a', :id => 'i')
+ @rewriter.rewrite(@router, :protocol => 'https', :controller => 'c', :action => 'a', :id => 'i')
)
assert_equal('https://test.host/c/a/i',
- @rewriter.rewrite(:protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i')
+ @rewriter.rewrite(@router, :protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i')
)
end
def test_user_name_and_password
assert_equal(
'http://david:secret@test.host/c/a/i',
- @rewriter.rewrite(:user => "david", :password => "secret", :controller => 'c', :action => 'a', :id => 'i')
+ @rewriter.rewrite(@router, :user => "david", :password => "secret", :controller => 'c', :action => 'a', :id => 'i')
)
end
def test_user_name_and_password_with_escape_codes
assert_equal(
'http://openid.aol.com%2Fnextangler:one+two%3F@test.host/c/a/i',
- @rewriter.rewrite(:user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i')
+ @rewriter.rewrite(@router, :user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i')
)
end
def test_anchor
assert_equal(
'http://test.host/c/a/i#anchor',
- @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
+ @rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
)
end
def test_anchor_should_call_to_param
assert_equal(
'http://test.host/c/a/i#anchor',
- @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anchor'))
+ @rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anchor'))
)
end
def test_anchor_should_be_cgi_escaped
assert_equal(
'http://test.host/c/a/i#anc%2Fhor',
- @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor'))
+ @rewriter.rewrite(@router, :controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor'))
)
end
- def test_overwrite_params
- @params[:controller] = 'hi'
- @params[:action] = 'bye'
- @params[:id] = '2'
-
- assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'})
- u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'})
- assert_match %r(/hi/hi/2$), u
- end
-
- def test_overwrite_removes_original
- @params[:controller] = 'search'
- @params[:action] = 'list'
- @params[:list_page] = 1
-
- assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2})
- u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2})
- assert_equal 'http://test.host/search/list?list_page=2', u
- end
-
- def test_to_str
- @params[:controller] = 'hi'
- @params[:action] = 'bye'
- @request.parameters[:id] = '2'
-
- assert_equal 'http://, test.host, /, hi, bye, {"id"=>"2"}', @rewriter.to_str
- end
-
def test_trailing_slash
options = {:controller => 'foo', :action => 'bar', :id => '3', :only_path => true}
- assert_equal '/foo/bar/3', @rewriter.rewrite(options)
- assert_equal '/foo/bar/3?query=string', @rewriter.rewrite(options.merge({:query => 'string'}))
+ assert_equal '/foo/bar/3', @rewriter.rewrite(@router, options)
+ assert_equal '/foo/bar/3?query=string', @rewriter.rewrite(@router, options.merge({:query => 'string'}))
options.update({:trailing_slash => true})
- assert_equal '/foo/bar/3/', @rewriter.rewrite(options)
+ assert_equal '/foo/bar/3/', @rewriter.rewrite(@router, options)
options.update({:query => 'string'})
- assert_equal '/foo/bar/3/?query=string', @rewriter.rewrite(options)
+ assert_equal '/foo/bar/3/?query=string', @rewriter.rewrite(@router, options)
end
end
diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb
index 56821332c5..b8972b04b6 100644
--- a/actionpack/test/controller/view_paths_test.rb
+++ b/actionpack/test/controller/view_paths_test.rb
@@ -36,9 +36,12 @@ class ViewLoadPathsTest < ActionController::TestCase
@old_behavior = ActiveSupport::Deprecation.behavior
@last_message = nil
ActiveSupport::Deprecation.behavior = Proc.new { |message, callback| @last_message = message }
+
+ @paths = TestController.view_paths
end
def teardown
+ TestController.view_paths = @paths
ActiveSupport::Deprecation.behavior = @old_behavior
end
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index 5882a8cfa3..05545395fb 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -245,7 +245,7 @@ class WebServiceTest < ActionController::IntegrationTest
private
def with_params_parsers(parsers = {})
old_session = @integration_session
- @app = ActionDispatch::ParamsParser.new(ActionController::Routing::Routes, parsers)
+ @app = ActionDispatch::ParamsParser.new(app.router, parsers)
reset!
yield
ensure
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
new file mode 100644
index 0000000000..00ca5ec9dc
--- /dev/null
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -0,0 +1,36 @@
+require 'abstract_unit'
+
+class TestRoutingMount < ActionDispatch::IntegrationTest
+ Router = ActionDispatch::Routing::RouteSet.new
+ Router.draw do
+ SprocketsApp = lambda { |env|
+ [200, {"Content-Type" => "text/html"}, ["#{env["SCRIPT_NAME"]} -- #{env["PATH_INFO"]}"]]
+ }
+
+ mount SprocketsApp, :at => "/sprockets"
+ mount SprocketsApp => "/shorthand"
+
+ scope "/its_a" do
+ mount SprocketsApp, :at => "/sprocket"
+ end
+ end
+
+ def app
+ Router
+ end
+
+ def test_mounting_sets_script_name
+ get "/sprockets/omg"
+ assert_equal "/sprockets -- /omg", response.body
+ end
+
+ def test_mounting_works_with_scope
+ get "/its_a/sprocket/omg"
+ assert_equal "/its_a/sprocket -- /omg", response.body
+ end
+
+ def test_mounting_with_shorthand
+ get "/shorthand/omg"
+ assert_equal "/shorthand -- /omg", response.body
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 2b5c19361a..badef4e92e 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -1,14 +1,6 @@
require 'abstract_unit'
class RequestTest < ActiveSupport::TestCase
- def setup
- ActionController::Base.relative_url_root = nil
- end
-
- def teardown
- ActionController::Base.relative_url_root = nil
- end
-
test "remote ip" do
request = stub_request 'REMOTE_ADDR' => '1.2.3.4'
assert_equal '1.2.3.4', request.remote_ip
@@ -50,7 +42,7 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
'HTTP_CLIENT_IP' => '2.2.2.2'
- e = assert_raise(ActionController::ActionControllerError) {
+ e = assert_raise(ActionDispatch::RemoteIp::IpSpoofAttackError) {
request.remote_ip
}
assert_match /IP spoofing attack/, e.message
@@ -62,18 +54,17 @@ class RequestTest < ActiveSupport::TestCase
# example is WAP. Since the cellular network is not IP based, it's a
# leap of faith to assume that their proxies are ever going to set the
# HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly.
- ActionController::Base.ip_spoofing_check = false
request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
- 'HTTP_CLIENT_IP' => '2.2.2.2'
+ 'HTTP_CLIENT_IP' => '2.2.2.2',
+ :ip_spoofing_check => false
assert_equal '2.2.2.2', request.remote_ip
- ActionController::Base.ip_spoofing_check = true
request = stub_request 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
assert_equal '9.9.9.9', request.remote_ip
end
test "remote ip with user specified trusted proxies" do
- ActionController::Base.trusted_proxies = /^67\.205\.106\.73$/i
+ @trusted_proxies = /^67\.205\.106\.73$/i
request = stub_request 'REMOTE_ADDR' => '67.205.106.73',
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
@@ -96,8 +87,6 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73'
assert_equal '3.4.5.6', request.remote_ip
-
- ActionController::Base.trusted_proxies = nil
end
test "domains" do
@@ -151,104 +140,34 @@ class RequestTest < ActiveSupport::TestCase
assert_equal ":8080", request.port_string
end
- test "request uri" do
- request = stub_request 'REQUEST_URI' => "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
- assert_equal "/path/of/some/uri?mapped=1", request.request_uri
- assert_equal "/path/of/some/uri", request.path
-
- request = stub_request 'REQUEST_URI' => "http://www.rubyonrails.org/path/of/some/uri"
- assert_equal "/path/of/some/uri", request.request_uri
- assert_equal "/path/of/some/uri", request.path
-
- request = stub_request 'REQUEST_URI' => "/path/of/some/uri"
- assert_equal "/path/of/some/uri", request.request_uri
- assert_equal "/path/of/some/uri", request.path
-
- request = stub_request 'REQUEST_URI' => "/"
- assert_equal "/", request.request_uri
- assert_equal "/", request.path
-
- request = stub_request 'REQUEST_URI' => "/?m=b"
- assert_equal "/?m=b", request.request_uri
- assert_equal "/", request.path
-
- request = stub_request 'REQUEST_URI' => "/", 'SCRIPT_NAME' => '/dispatch.cgi'
- assert_equal "/", request.request_uri
- assert_equal "/", request.path
-
- ActionController::Base.relative_url_root = "/hieraki"
- request = stub_request 'REQUEST_URI' => "/hieraki/", 'SCRIPT_NAME' => "/hieraki/dispatch.cgi"
- assert_equal "/hieraki/", request.request_uri
- assert_equal "/", request.path
- ActionController::Base.relative_url_root = nil
-
- ActionController::Base.relative_url_root = "/collaboration/hieraki"
- request = stub_request 'REQUEST_URI' => "/collaboration/hieraki/books/edit/2",
- 'SCRIPT_NAME' => "/collaboration/hieraki/dispatch.cgi"
- assert_equal "/collaboration/hieraki/books/edit/2", request.request_uri
- assert_equal "/books/edit/2", request.path
- ActionController::Base.relative_url_root = nil
-
- # The following tests are for when REQUEST_URI is not supplied (as in IIS)
- request = stub_request 'PATH_INFO' => "/path/of/some/uri?mapped=1",
- 'SCRIPT_NAME' => nil,
- 'REQUEST_URI' => nil
- assert_equal "/path/of/some/uri?mapped=1", request.request_uri
- assert_equal "/path/of/some/uri", request.path
-
- ActionController::Base.relative_url_root = '/path'
- request = stub_request 'PATH_INFO' => "/path/of/some/uri?mapped=1",
- 'SCRIPT_NAME' => "/path/dispatch.rb",
- 'REQUEST_URI' => nil
- assert_equal "/path/of/some/uri?mapped=1", request.request_uri
- assert_equal "/of/some/uri", request.path
- ActionController::Base.relative_url_root = nil
-
- request = stub_request 'PATH_INFO' => "/path/of/some/uri",
- 'SCRIPT_NAME' => nil,
- 'REQUEST_URI' => nil
- assert_equal "/path/of/some/uri", request.request_uri
- assert_equal "/path/of/some/uri", request.path
-
- request = stub_request 'PATH_INFO' => '/', 'REQUEST_URI' => nil
- assert_equal "/", request.request_uri
- assert_equal "/", request.path
-
- request = stub_request 'PATH_INFO' => '/?m=b', 'REQUEST_URI' => nil
- assert_equal "/?m=b", request.request_uri
- assert_equal "/", request.path
-
- request = stub_request 'PATH_INFO' => "/",
- 'SCRIPT_NAME' => "/dispatch.cgi",
- 'REQUEST_URI' => nil
- assert_equal "/", request.request_uri
- assert_equal "/", request.path
-
- ActionController::Base.relative_url_root = '/hieraki'
- request = stub_request 'PATH_INFO' => "/hieraki/",
- 'SCRIPT_NAME' => "/hieraki/dispatch.cgi",
- 'REQUEST_URI' => nil
- assert_equal "/hieraki/", request.request_uri
- assert_equal "/", request.path
- ActionController::Base.relative_url_root = nil
-
- request = stub_request 'REQUEST_URI' => '/hieraki/dispatch.cgi'
- ActionController::Base.relative_url_root = '/hieraki'
- assert_equal "/dispatch.cgi", request.path
- ActionController::Base.relative_url_root = nil
-
- request = stub_request 'REQUEST_URI' => '/hieraki/dispatch.cgi'
- ActionController::Base.relative_url_root = '/foo'
- assert_equal "/hieraki/dispatch.cgi", request.path
- ActionController::Base.relative_url_root = nil
-
- # This test ensures that Rails uses REQUEST_URI over PATH_INFO
- ActionController::Base.relative_url_root = nil
- request = stub_request 'REQUEST_URI' => "/some/path",
- 'PATH_INFO' => "/another/path",
- 'SCRIPT_NAME' => "/dispatch.cgi"
- assert_equal "/some/path", request.request_uri
- assert_equal "/some/path", request.path
+ test "full path" do
+ request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri', 'QUERY_STRING' => 'mapped=1'
+ assert_equal "/path/of/some/uri?mapped=1", request.fullpath
+ assert_equal "/path/of/some/uri", request.path_info
+
+ request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri'
+ assert_equal "/path/of/some/uri", request.fullpath
+ assert_equal "/path/of/some/uri", request.path_info
+
+ request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/'
+ assert_equal "/", request.fullpath
+ assert_equal "/", request.path_info
+
+ request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/', 'QUERY_STRING' => 'm=b'
+ assert_equal "/?m=b", request.fullpath
+ assert_equal "/", request.path_info
+
+ request = stub_request 'SCRIPT_NAME' => '/hieraki', 'PATH_INFO' => '/'
+ assert_equal "/hieraki/", request.fullpath
+ assert_equal "/", request.path_info
+
+ request = stub_request 'SCRIPT_NAME' => '/collaboration/hieraki', 'PATH_INFO' => '/books/edit/2'
+ assert_equal "/collaboration/hieraki/books/edit/2", request.fullpath
+ assert_equal "/books/edit/2", request.path_info
+
+ request = stub_request 'SCRIPT_NAME' => '/path', 'PATH_INFO' => '/of/some/uri', 'QUERY_STRING' => 'mapped=1'
+ assert_equal "/path/of/some/uri?mapped=1", request.fullpath
+ assert_equal "/of/some/uri", request.path_info
end
@@ -462,7 +381,7 @@ class RequestTest < ActiveSupport::TestCase
[{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
[{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
[{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'],
- [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, [/foo/]]]
+ [{'baz'=>[{'foo'=>'baz'}, "1"]}, {'baz'=>[{'foo'=>'[FILTERED]'}, "1"]}, [/foo/]]]
test_hashes.each do |before_filter, after_filter, filter_words|
request = stub_request('action_dispatch.parameter_filter' => filter_words)
@@ -506,18 +425,14 @@ class RequestTest < ActiveSupport::TestCase
protected
- def stub_request(env={})
+ def stub_request(env = {})
+ ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true
+ ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies)
+ ip_app.call(env)
ActionDispatch::Request.new(env)
end
def with_set(*args)
args
end
-
- def with_accept_header(value)
- ActionController::Base.use_accept_header, old = value, ActionController::Base.use_accept_header
- yield
- ensure
- ActionController::Base.use_accept_header = old
- end
end
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 4697fa3e2b..c7f7f3102d 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -165,10 +165,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_equal('public', @response.headers['Cache-Control'])
assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag'])
- pending do
- assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag)
- assert_equal({:public => true}, @response.cache_control)
- end
+ assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag)
+ assert_equal({:public => true}, @response.cache_control)
end
test "response cache control from rackish app" do
@@ -184,10 +182,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_equal('public', @response.headers['Cache-Control'])
assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag'])
- pending do
- assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag)
- assert_equal({:public => true}, @response.cache_control)
- end
+ assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag)
+ assert_equal({:public => true}, @response.cache_control)
end
test "response charset and content type from railsish app" do
@@ -202,10 +198,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
get '/'
assert_response :success
- pending do
- assert_equal('utf-16', @response.charset)
- assert_equal(Mime::XML, @response.content_type)
- end
+ assert_equal('utf-16', @response.charset)
+ assert_equal(Mime::XML, @response.content_type)
assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type'])
end
@@ -220,10 +214,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
get '/'
assert_response :success
- pending do
- assert_equal('utf-16', @response.charset)
- assert_equal(Mime::XML, @response.content_type)
- end
+ assert_equal('utf-16', @response.charset)
+ assert_equal(Mime::XML, @response.content_type)
assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type'])
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index bcb97e4ae0..f5fcf9b0df 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -15,6 +15,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
stub_controllers do |routes|
Routes = routes
Routes.draw do
+ default_url_options :host => "rubyonrails.org"
+
controller :sessions do
get 'login' => :new, :as => :login
post 'login' => :create
@@ -24,6 +26,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resource :session do
get :create
+
+ resource :info
end
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
@@ -104,7 +108,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
- resources :posts, :only => [:index, :show]
+ resources :posts, :only => [:index, :show] do
+ resources :comments, :except => :destroy
+ end
match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp
@@ -162,6 +168,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
Routes
end
+ include Routes.url_helpers
+
def test_logout
with_test_routes do
delete '/logout'
@@ -183,6 +191,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/login', url_for(:controller => 'sessions', :action => 'create', :only_path => true)
assert_equal '/login', url_for(:controller => 'sessions', :action => 'new', :only_path => true)
+
+ assert_equal 'http://rubyonrails.org/login', Routes.url_for(:controller => 'sessions', :action => 'create')
end
end
@@ -230,6 +240,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_session_info_nested_singleton_resource
+ with_test_routes do
+ get '/session/info'
+ assert_equal 'infos#show', @response.body
+ assert_equal '/session/info', session_info_path
+ end
+ end
+
def test_redirect_modulo
with_test_routes do
get '/account/modulo/name'
@@ -471,7 +489,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
- def test_posts
+ def test_resource_routes_with_only_and_except
with_test_routes do
get '/posts'
assert_equal 'posts#index', @response.body
@@ -481,9 +499,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'posts#show', @response.body
assert_equal '/posts/1', post_path(:id => 1)
+ get '/posts/1/comments'
+ assert_equal 'comments#index', @response.body
+ assert_equal '/posts/1/comments', post_comments_path(:post_id => 1)
+
assert_raise(ActionController::RoutingError) { post '/posts' }
assert_raise(ActionController::RoutingError) { put '/posts/1' }
assert_raise(ActionController::RoutingError) { delete '/posts/1' }
+ assert_raise(ActionController::RoutingError) { delete '/posts/1/comments' }
end
end
@@ -721,14 +744,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
private
def with_test_routes
- real_routes, temp_routes = ActionController::Routing::Routes, Routes
-
- ActionController::Routing.module_eval { remove_const :Routes }
- ActionController::Routing.module_eval { const_set :Routes, temp_routes }
-
yield
- ensure
- ActionController::Routing.module_eval { remove_const :Routes }
- ActionController::Routing.const_set(:Routes, real_routes)
end
end
diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb
new file mode 100644
index 0000000000..18b5b7ee00
--- /dev/null
+++ b/actionpack/test/dispatch/url_generation_test.rb
@@ -0,0 +1,38 @@
+require 'abstract_unit'
+
+module TestUrlGeneration
+ class WithMountPoint < ActionDispatch::IntegrationTest
+ Router = ActionDispatch::Routing::RouteSet.new
+ Router.draw { match "/foo", :to => "my_route_generating#index", :as => :foo }
+
+ class ::MyRouteGeneratingController < ActionController::Base
+ include Router.url_helpers
+ def index
+ render :text => foo_path
+ end
+ end
+
+ include Router.url_helpers
+
+ def _router
+ Router
+ end
+
+ def app
+ Router
+ end
+
+ test "generating URLS normally" do
+ assert_equal "/foo", foo_path
+ end
+
+ test "accepting a :script_name option" do
+ assert_equal "/bar/foo", foo_path(:script_name => "/bar")
+ end
+
+ test "the request's SCRIPT_NAME takes precedence over the router's" do
+ get "/foo", {}, 'SCRIPT_NAME' => "/new"
+ assert_equal "/new/foo", response.body
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/fixtures/hello.html b/actionpack/test/fixtures/hello.html
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/hello.html
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/layout_render_file.erb b/actionpack/test/fixtures/test/layout_render_file.erb
new file mode 100644
index 0000000000..2f8e921c5f
--- /dev/null
+++ b/actionpack/test/fixtures/test/layout_render_file.erb
@@ -0,0 +1,2 @@
+<% content_for :title do %>title<% end -%>
+<%= render :file => 'layouts/yield' -%> \ No newline at end of file
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index 7346cb22bc..bf3e175f1f 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -6,10 +6,6 @@ class Customer < Struct.new(:name, :id)
undef_method :to_json
- def to_param
- id.to_s
- end
-
def to_xml(options={})
if options[:builder]
options[:builder].name name
@@ -21,13 +17,14 @@ class Customer < Struct.new(:name, :id)
def to_js(options={})
"name: #{name.inspect}"
end
+ alias :to_text :to_js
def errors
[]
end
- def destroyed?
- false
+ def persisted?
+ id.present?
end
end
@@ -42,8 +39,8 @@ module Quiz
extend ActiveModel::Naming
include ActiveModel::Conversion
- def to_param
- id.to_s
+ def persisted?
+ id.present?
end
end
@@ -58,12 +55,12 @@ class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost
alias_method :secret?, :secret
- def new_record=(boolean)
- @new_record = boolean
+ def persisted=(boolean)
+ @persisted = boolean
end
- def new_record?
- @new_record
+ def persisted?
+ @persisted
end
attr_accessor :author
@@ -83,8 +80,9 @@ class Comment
attr_reader :id
attr_reader :post_id
def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end
+ def to_key; id ? [id] : nil end
def save; @id = 1; @post_id = 1 end
- def new_record?; @id.nil? end
+ def persisted?; @id.present? end
def to_param; @id; end
def name
@id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}"
@@ -102,8 +100,9 @@ class Tag
attr_reader :id
attr_reader :post_id
def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end
+ def to_key; id ? [id] : nil end
def save; @id = 1; @post_id = 1 end
- def new_record?; @id.nil? end
+ def persisted?; @id.present? end
def to_param; @id; end
def value
@id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}"
@@ -121,8 +120,9 @@ class CommentRelevance
attr_reader :id
attr_reader :comment_id
def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end
+ def to_key; id ? [id] : nil end
def save; @id = 1; @comment_id = 1 end
- def new_record?; @id.nil? end
+ def persisted?; @id.present? end
def to_param; @id; end
def value
@id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}"
@@ -136,8 +136,9 @@ class TagRelevance
attr_reader :id
attr_reader :tag_id
def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end
+ def to_key; id ? [id] : nil end
def save; @id = 1; @tag_id = 1 end
- def new_record?; @id.nil? end
+ def persisted?; @id.present? end
def to_param; @id; end
def value
@id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}"
diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb
index 6b9e7c5270..02248d84ad 100644
--- a/actionpack/test/lib/fixture_template.rb
+++ b/actionpack/test/lib/fixture_template.rb
@@ -1,23 +1,28 @@
module ActionView #:nodoc:
class FixtureResolver < PathResolver
- def initialize(hash = {}, options = {})
- super(options)
+ attr_reader :hash
+
+ def initialize(hash = {})
+ super()
@hash = hash
end
private
- def query(path, exts)
+ def query(partial, path, exts)
query = Regexp.escape(path)
exts.each do |ext|
- query << '(?:' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << ')'
+ query << '(' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << ')'
end
templates = []
@hash.select { |k,v| k =~ /^#{query}$/ }.each do |path, source|
- templates << Template.new(source, path, *path_to_details(path))
+ handler, format = extract_handler_and_format(path)
+ templates << Template.new(source, path, handler,
+ :partial => partial, :virtual_path => path, :format => format)
end
- templates.sort_by {|t| -t.details.values.compact.size }
+
+ templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
end
end
diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb
index 3e01ae78c3..371aa53c68 100644
--- a/actionpack/test/template/active_model_helper_test.rb
+++ b/actionpack/test/template/active_model_helper_test.rb
@@ -64,7 +64,7 @@ class ActiveModelHelperTest < ActionView::TestCase
}.new
end
- def @post.new_record?() true end
+ def @post.persisted?() false end
def @post.to_param() nil end
def @post.column_for_attribute(attr_name)
@@ -155,7 +155,7 @@ class ActiveModelHelperTest < ActionView::TestCase
silence_warnings do
class << @post
- def new_record?() false end
+ def persisted?() true end
def to_param() id end
def id() 1 end
end
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 586de66714..d9a89959da 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -1,14 +1,16 @@
require 'abstract_unit'
+require 'active_support/ordered_options'
-class AssetTagHelperTest < ActionView::TestCase
- tests ActionView::Helpers::AssetTagHelper
+class FakeController
+ attr_accessor :request
- DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG.merge(
- :assets_dir => File.dirname(__FILE__) + "/../fixtures/public",
- :javascripts_dir => File.dirname(__FILE__) + "/../fixtures/public/javascripts",
- :stylesheets_dir => File.dirname(__FILE__) + "/../fixtures/public/stylesheets")
+ def config
+ @config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config)
+ end
+end
- include ActiveSupport::Configurable
+class AssetTagHelperTest < ActionView::TestCase
+ tests ActionView::Helpers::AssetTagHelper
def setup
super
@@ -32,8 +34,7 @@ class AssetTagHelperTest < ActionView::TestCase
)
end
- @controller = Class.new do
- attr_accessor :request
+ @controller = Class.new(BasicController) do
def url_for(*args) "http://www.example.com" end
end.new
@@ -50,7 +51,6 @@ class AssetTagHelperTest < ActionView::TestCase
def teardown
ActionController::Base.perform_caching = false
- ActionController::Base.asset_host = nil
ENV.delete('RAILS_ASSET_ID')
end
@@ -141,13 +141,16 @@ class AssetTagHelperTest < ActionView::TestCase
ImageLinkToTag = {
%(image_tag("xml.png")) => %(<img alt="Xml" src="/images/xml.png" />),
- %(image_tag("..jpg")) => %(<img alt="" src="/images/..jpg" />),
+ %(image_tag("..jpg")) => %(<img alt="..jpg" src="/images/..jpg" />),
%(image_tag("rss.gif", :alt => "rss syndication")) => %(<img alt="rss syndication" src="/images/rss.gif" />),
%(image_tag("gold.png", :size => "45x70")) => %(<img alt="Gold" height="70" src="/images/gold.png" width="45" />),
%(image_tag("gold.png", "size" => "45x70")) => %(<img alt="Gold" height="70" src="/images/gold.png" width="45" />),
%(image_tag("error.png", "size" => "45")) => %(<img alt="Error" src="/images/error.png" />),
%(image_tag("error.png", "size" => "45 x 70")) => %(<img alt="Error" src="/images/error.png" />),
%(image_tag("error.png", "size" => "x")) => %(<img alt="Error" src="/images/error.png" />),
+ %(image_tag("google.com.png")) => %(<img alt="Google.com" src="/images/google.com.png" />),
+ %(image_tag("slash..png")) => %(<img alt="Slash." src="/images/slash..png" />),
+ %(image_tag(".pdf.png")) => %(<img alt=".pdf" src="/images/.pdf.png" />),
%(image_tag("http://www.rubyonrails.com/images/rails.png")) => %(<img alt="Rails" src="http://www.rubyonrails.com/images/rails.png" />),
%(image_tag("mouse.png", :mouseover => "/images/mouse_over.png")) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
%(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />)
@@ -372,11 +375,9 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_timebased_asset_id_with_relative_url_root
- ActionController::Base.relative_url_root = "/collaboration/hieraki"
- expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
- assert_equal %(<img alt="Rails" src="#{ActionController::Base.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- ensure
- ActionController::Base.relative_url_root = ""
+ @controller.config.relative_url_root = "/collaboration/hieraki"
+ expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.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
def test_should_skip_asset_id_on_complete_url
@@ -402,7 +403,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_image_path_with_caching_and_proc_asset_host_using_request
ENV['RAILS_ASSET_ID'] = ''
- ActionController::Base.asset_host = Proc.new do |source, request|
+ @controller.config.asset_host = Proc.new do |source, request|
if request.ssl?
"#{request.protocol}#{request.host_with_port}"
else
@@ -410,8 +411,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
end
- ActionController::Base.perform_caching = true
-
@controller.request.stubs(:ssl?).returns(false)
assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png")
@@ -422,7 +421,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_when_caching_on
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.asset_host = 'http://a0.example.com'
+ @controller.config.asset_host = 'http://a0.example.com'
ActionController::Base.perform_caching = true
assert_dom_equal(
@@ -454,7 +453,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_when_caching_on_with_proc_asset_host
ENV['RAILS_ASSET_ID'] = ''
- ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
+ @controller.config.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
ActionController::Base.perform_caching = true
assert_equal '/javascripts/scripts.js'.length, 23
@@ -471,7 +470,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_when_caching_on_with_2_argument_proc_asset_host
ENV['RAILS_ASSET_ID'] = ''
- ActionController::Base.asset_host = Proc.new { |source, request|
+ @controller.config.asset_host = Proc.new { |source, request|
if request.ssl?
"#{request.protocol}#{request.host_with_port}"
else
@@ -508,7 +507,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_when_caching_on_with_2_argument_object_asset_host
ENV['RAILS_ASSET_ID'] = ''
- ActionController::Base.asset_host = Class.new do
+ @controller.config.asset_host = Class.new do
def call(source, request)
if request.ssl?
"#{request.protocol}#{request.host_with_port}"
@@ -548,7 +547,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_when_caching_on_and_using_subdirectory
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.asset_host = 'http://a%d.example.com'
+ @controller.config.asset_host = 'http://a%d.example.com'
ActionController::Base.perform_caching = true
hash = '/javascripts/cache/money.js'.hash % 4
@@ -564,7 +563,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.asset_host = 'http://a0.example.com'
+ @controller.config.asset_host = 'http://a0.example.com'
ActionController::Base.perform_caching = true
assert_dom_equal(
@@ -585,7 +584,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_with_all_puts_defaults_at_the_start_of_the_file
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.asset_host = 'http://a0.example.com'
+ @controller.config.asset_host = 'http://a0.example.com'
ActionController::Base.perform_caching = true
assert_dom_equal(
@@ -606,7 +605,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_with_relative_url_root
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.relative_url_root = "/collaboration/hieraki"
+ @controller.config.relative_url_root = "/collaboration/hieraki"
ActionController::Base.perform_caching = true
assert_dom_equal(
@@ -624,7 +623,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
ensure
- ActionController::Base.relative_url_root = nil
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
@@ -694,7 +692,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_stylesheet_link_tag_when_caching_on
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.asset_host = 'http://a0.example.com'
+ @controller.config.asset_host = 'http://a0.example.com'
ActionController::Base.perform_caching = true
assert_dom_equal(
@@ -804,7 +802,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
+ @controller.config.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
ActionController::Base.perform_caching = true
assert_equal '/stylesheets/styles.css'.length, 23
@@ -821,7 +819,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_stylesheet_link_tag_with_relative_url_root
ENV["RAILS_ASSET_ID"] = ""
- ActionController::Base.relative_url_root = "/collaboration/hieraki"
+ @controller.config.relative_url_root = "/collaboration/hieraki"
ActionController::Base.perform_caching = true
assert_dom_equal(
@@ -841,7 +839,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
ensure
- ActionController::Base.relative_url_root = nil
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
@@ -879,21 +876,16 @@ end
class AssetTagHelperNonVhostTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
- DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
- include ActiveSupport::Configurable
-
def setup
super
- ActionController::Base.relative_url_root = "/collaboration/hieraki"
-
- @controller = Class.new do
- attr_accessor :request
-
+ @controller = Class.new(BasicController) do
def url_for(options)
"http://www.example.com/collaboration/hieraki"
end
end.new
+ @controller.config.relative_url_root = "/collaboration/hieraki"
+
@request = Class.new do
def protocol
'gopher://'
@@ -905,10 +897,6 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
end
- def teardown
- ActionController::Base.relative_url_root = nil
- end
-
def test_should_compute_proper_path
assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
@@ -923,43 +911,33 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
end
def test_should_compute_proper_path_with_asset_host
- ActionController::Base.asset_host = "http://assets.example.com"
+ @controller.config.asset_host = "http://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)
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
- ensure
- ActionController::Base.asset_host = ""
end
def test_should_ignore_asset_host_on_complete_url
- ActionController::Base.asset_host = "http://assets.example.com"
+ @controller.config.asset_host = "http://assets.example.com"
assert_dom_equal(%(<link href="http://bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />), stylesheet_link_tag("http://bar.example.com/stylesheets/style.css"))
- ensure
- ActionController::Base.asset_host = ""
end
def test_should_wildcard_asset_host_between_zero_and_four
- ActionController::Base.asset_host = 'http://a%d.example.com'
+ @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')
- ensure
- ActionController::Base.asset_host = nil
end
def test_asset_host_without_protocol_should_use_request_protocol
- ActionController::Base.asset_host = 'a.example.com'
+ @controller.config.asset_host = 'a.example.com'
assert_equal 'gopher://a.example.com/collaboration/hieraki/images/xml.png', image_path('xml.png')
- ensure
- ActionController::Base.asset_host = nil
end
def test_asset_host_without_protocol_should_use_request_protocol_even_if_path_present
- ActionController::Base.asset_host = 'a.example.com/files/go/here'
+ @controller.config.asset_host = 'a.example.com/files/go/here'
assert_equal 'gopher://a.example.com/files/go/here/collaboration/hieraki/images/xml.png', image_path('xml.png')
- ensure
- ActionController::Base.asset_host = nil
end
def test_assert_css_and_js_of_the_same_name_return_correct_extension
diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb
index 347cb98303..869ea22469 100644
--- a/actionpack/test/template/atom_feed_helper_test.rb
+++ b/actionpack/test/template/atom_feed_helper_test.rb
@@ -4,8 +4,8 @@ class Scroll < Struct.new(:id, :to_param, :title, :body, :updated_at, :created_a
extend ActiveModel::Naming
include ActiveModel::Conversion
- def new_record?
- true
+ def persisted?
+ false
end
end
@@ -34,7 +34,7 @@ class ScrollsController < ActionController::Base
feed.updated((@scrolls.first.created_at))
for scroll in @scrolls
- feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
+ feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param.to_s, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
entry.title(scroll.title)
entry.content(scroll.body, :type => 'html')
@@ -55,7 +55,7 @@ class ScrollsController < ActionController::Base
end
for scroll in @scrolls
- feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
+ feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param.to_s, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
entry.title(scroll.title)
entry.content(scroll.body, :type => 'html')
end
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
index 632988bb2e..2c719757e4 100644
--- a/actionpack/test/template/compiled_templates_test.rb
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -14,9 +14,6 @@ class CompiledTemplatesTest < Test::Unit::TestCase
assert_equal "two", render(:file => "test/render_file_with_locals_and_default.erb", :locals => { :secret => "two" })
end
- # This is broken in 1.8.6 (not supported in Rails 3.0) because the cache uses a Hash
- # key. Since Ruby 1.8.6 implements Hash#hash using the hash's object_id, it will never
- # successfully get a cache hit here.
def test_template_changes_are_not_reflected_with_cached_templates
assert_equal "Hello world!", render(:file => "test/hello_world.erb")
modify_template "test/hello_world.erb", "Goodbye world!" do
@@ -44,7 +41,7 @@ class CompiledTemplatesTest < Test::Unit::TestCase
end
def render_without_cache(*args)
- path = ActionView::FileSystemResolverWithFallback.new(FIXTURE_LOAD_PATH)
+ path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::Base.process_view_paths(path)
ActionView::Base.new(view_paths, {}).render(*args)
end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index fb51b67185..da2477b6f8 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1229,11 +1229,28 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on", :order => [ :month, :year ])
end
+ def test_date_select_without_day_and_with_disabled_html_option
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = "<input type=\"hidden\" id=\"post_written_on_3i\" disabled=\"disabled\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
+
+ expected << %{<select id="post_written_on_2i" disabled="disabled" name="post[written_on(2i)]">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_1i" disabled="disabled" name="post[written_on(1i)]">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", { :order => [ :month, :year ] }, :disabled => true)
+ end
+
def test_date_select_within_fields_for
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.date_select(:written_on)
end
@@ -1249,7 +1266,7 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Date.new(2004, 6, 15)
id = 27
- fields_for :post, @post, :index => id do |f|
+ output_buffer = fields_for :post, @post, :index => id do |f|
concat f.date_select(:written_on)
end
@@ -1265,7 +1282,7 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Date.new(2004, 6, 15)
id = nil
- fields_for :post, @post, :index => id do |f|
+ output_buffer = fields_for :post, @post, :index => id do |f|
concat f.date_select(:written_on)
end
@@ -1461,7 +1478,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.date_select(:written_on, {}, :class => 'selector')
end
@@ -1625,7 +1642,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.time_select(:written_on, {}, :class => 'selector')
end
@@ -1713,6 +1730,25 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute'})
end
+ def test_time_select_with_disabled_html_option
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" disabled="disabled" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" disabled="disabled" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" disabled="disabled" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" disabled="disabled" name="post[written_on(4i)]">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+ expected << " : "
+ expected << %(<select id="post_written_on_5i" disabled="disabled" name="post[written_on(5i)]">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", {}, :disabled => true)
+ end
+
def test_datetime_select
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
@@ -1780,7 +1816,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.datetime_select(:updated_at, {}, :class => 'selector')
end
@@ -2016,7 +2052,7 @@ class DateHelperTest < ActionView::TestCase
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
id = 456
- fields_for :post, @post, :index => id do |f|
+ output_buffer = fields_for :post, @post, :index => id do |f|
concat f.datetime_select(:updated_at)
end
@@ -2173,6 +2209,66 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true)
end
+ def test_datetime_select_discard_year_and_month_with_disabled_html_option
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_updated_at_1i" disabled="disabled" name="post[updated_at(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_updated_at_2i" disabled="disabled" name="post[updated_at(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_updated_at_3i" disabled="disabled" name="post[updated_at(3i)]" value="15" />\n}
+
+ expected << %{<select id="post_updated_at_4i" disabled="disabled" name="post[updated_at(4i)]">\n}
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+ expected << " : "
+ expected << %{<select id="post_updated_at_5i" disabled="disabled" name="post[updated_at(5i)]">\n}
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, datetime_select("post", "updated_at", { :discard_year => true, :discard_month => true }, :disabled => true)
+ end
+
+ def test_datetime_select_discard_hour
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+ 1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+ 1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+ 1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, datetime_select("post", "updated_at", :discard_hour => true)
+ end
+
+ def test_datetime_select_discard_minute
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+ 1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+ 1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+ 1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<input type="hidden" id="post_updated_at_5i" name="post[updated_at(5i)]" value="16" />\n}
+
+ assert_dom_equal expected, datetime_select("post", "updated_at", :discard_minute => true)
+ end
+
def test_datetime_select_invalid_order
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
diff --git a/actionpack/test/template/erb/tag_helper_test.rb b/actionpack/test/template/erb/tag_helper_test.rb
new file mode 100644
index 0000000000..cc96a43901
--- /dev/null
+++ b/actionpack/test/template/erb/tag_helper_test.rb
@@ -0,0 +1,82 @@
+require "abstract_unit"
+
+module ERBTest
+ class ViewContext
+ mock_controller = Class.new do
+ include SharedTestRoutes.url_helpers
+ end
+
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::FormHelper
+
+ attr_accessor :output_buffer
+
+ def protect_against_forgery?() false end
+
+ define_method(:controller) do
+ mock_controller.new
+ end
+ end
+
+ class DeprecatedViewContext < ViewContext
+ include ActionView::Helpers::DeprecatedBlockHelpers
+ end
+
+ module SharedTagHelpers
+ extend ActiveSupport::Testing::Declarative
+
+ def render_content(start, inside)
+ template = block_helper(start, inside)
+ ActionView::Template::Handlers::Erubis.new(template).evaluate(context.new)
+ end
+
+ test "percent equals works for content_tag and does not require parenthesis on method call" do
+ assert_equal "<div>Hello world</div>", render_content("content_tag :div", "Hello world")
+ end
+
+ test "percent equals works for javascript_tag" do
+ expected_output = "<script type=\"text/javascript\">\n//<![CDATA[\nalert('Hello')\n//]]>\n</script>"
+ assert_equal expected_output, render_content("javascript_tag", "alert('Hello')")
+ end
+
+ test "percent equals works for javascript_tag with options" do
+ expected_output = "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('Hello')\n//]]>\n</script>"
+ assert_equal expected_output, render_content("javascript_tag(:id => 'the_js_tag')", "alert('Hello')")
+ end
+
+ test "percent equals works with form tags" do
+ expected_output = "<form action=\"foo\" method=\"post\">hello</form>"
+ assert_equal expected_output, render_content("form_tag('foo')", "<%= 'hello' %>")
+ end
+
+ test "percent equals works with fieldset tags" do
+ expected_output = "<fieldset><legend>foo</legend>hello</fieldset>"
+ assert_equal expected_output, render_content("field_set_tag('foo')", "<%= 'hello' %>")
+ end
+ end
+
+ class TagHelperTest < ActiveSupport::TestCase
+ def context
+ ViewContext
+ end
+
+ def block_helper(str, rest)
+ "<%= #{str} do %>#{rest}<% end %>"
+ end
+
+ include SharedTagHelpers
+ end
+
+ class DeprecatedTagHelperTest < ActiveSupport::TestCase
+ def context
+ DeprecatedViewContext
+ end
+
+ def block_helper(str, rest)
+ "<% __in_erb_template=true %><% #{str} do %>#{rest}<% end %>"
+ end
+
+ include SharedTagHelpers
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 7b909fff82..7c5ccfd6ed 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -4,6 +4,10 @@ require 'controller/fake_models'
class FormHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormHelper
+ def form_for(*)
+ @output_buffer = super
+ end
+
def setup
super
@@ -53,6 +57,7 @@ class FormHelperTest < ActionView::TestCase
def @post.id_before_type_cast; 123; end
def @post.to_param; '123'; end
+ @post.persisted = true
@post.title = "Hello World"
@post.author_name = ""
@post.body = "Back to the hill and over it again!"
@@ -529,7 +534,7 @@ class FormHelperTest < ActionView::TestCase
def test_submit_with_object_as_new_record_and_locale_strings
old_locale, I18n.locale = I18n.locale, :submit
- def @post.new_record?() true; end
+ @post.persisted = false
form_for(:post, @post) do |f|
concat f.submit
end
@@ -589,9 +594,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for
form_for(:post, @post) do |f|
- f.fields_for(:comment, @post) do |c|
+ concat f.fields_for(:comment, @post) { |c|
concat c.text_field(:title)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -604,9 +609,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_nested_collections
form_for('post[]', @post) do |f|
concat f.text_field(:title)
- f.fields_for('comment[]', @comment) do |c|
+ concat f.fields_for('comment[]', @comment) { |c|
concat c.text_field(:name)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -620,9 +625,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_index_and_parent_fields
form_for('post', @post, :index => 1) do |c|
concat c.text_field(:title)
- c.fields_for('comment', @comment, :index => 1) do |r|
+ concat c.fields_for('comment', @comment, :index => 1) { |r|
concat r.text_field(:name)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -634,10 +639,10 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_index_and_nested_fields_for
- form_for(:post, @post, :index => 1) do |f|
- f.fields_for(:comment, @post) do |c|
+ output_buffer = form_for(:post, @post, :index => 1) do |f|
+ concat f.fields_for(:comment, @post) { |c|
concat c.text_field(:title)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -649,9 +654,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_index_on_both
form_for(:post, @post, :index => 1) do |f|
- f.fields_for(:comment, @post, :index => 5) do |c|
+ concat f.fields_for(:comment, @post, :index => 5) { |c|
concat c.text_field(:title)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -663,9 +668,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_auto_index
form_for("post[]", @post) do |f|
- f.fields_for(:comment, @post) do |c|
+ concat f.fields_for(:comment, @post) { |c|
concat c.text_field(:title)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -677,9 +682,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_index_radio_button
form_for(:post, @post) do |f|
- f.fields_for(:comment, @post, :index => 5) do |c|
+ concat f.fields_for(:comment, @post, :index => 5) { |c|
concat c.radio_button(:title, "hello")
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -691,9 +696,9 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_auto_index_on_both
form_for("post[]", @post) do |f|
- f.fields_for("comment[]", @post) do |c|
+ concat f.fields_for("comment[]", @post) { |c|
concat c.text_field(:title)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -704,16 +709,16 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_index_and_auto_index
- form_for("post[]", @post) do |f|
- f.fields_for(:comment, @post, :index => 5) do |c|
+ output_buffer = form_for("post[]", @post) do |f|
+ concat f.fields_for(:comment, @post, :index => 5) { |c|
concat c.text_field(:title)
- end
+ }
end
- form_for(:post, @post, :index => 1) do |f|
- f.fields_for("comment[]", @post) do |c|
+ output_buffer << form_for(:post, @post, :index => 1) do |f|
+ concat f.fields_for("comment[]", @post) { |c|
concat c.text_field(:title)
- end
+ }
end
expected = "<form action='http://www.example.com' method='post'>" +
@@ -731,9 +736,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
- f.fields_for(:author) do |af|
+ concat f.fields_for(:author) { |af|
concat af.text_field(:name)
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -758,9 +763,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
- f.fields_for(:author) do |af|
+ concat f.fields_for(:author) { |af|
concat af.text_field(:name)
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -777,10 +782,10 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
- f.fields_for(:author) do |af|
+ concat f.fields_for(:author) { |af|
concat af.hidden_field(:id)
concat af.text_field(:name)
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -798,9 +803,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
@post.comments.each do |comment|
- f.fields_for(:comments, comment) do |cf|
+ concat f.fields_for(:comments, comment) { |cf|
concat cf.text_field(:name)
- end
+ }
end
end
@@ -821,10 +826,10 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
@post.comments.each do |comment|
- f.fields_for(:comments, comment) do |cf|
+ concat f.fields_for(:comments, comment) { |cf|
concat cf.hidden_field(:id)
concat cf.text_field(:name)
- end
+ }
end
end
@@ -845,9 +850,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
@post.comments.each do |comment|
- f.fields_for(:comments, comment) do |cf|
+ concat f.fields_for(:comments, comment) { |cf|
concat cf.text_field(:name)
- end
+ }
end
end
@@ -866,9 +871,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
@post.comments.each do |comment|
- f.fields_for(:comments, comment) do |cf|
+ concat f.fields_for(:comments, comment) { |cf|
concat cf.text_field(:name)
- end
+ }
end
end
@@ -902,9 +907,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
- f.fields_for(:comments, @post.comments) do |cf|
+ concat f.fields_for(:comments, @post.comments) { |cf|
concat cf.text_field(:name)
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -924,9 +929,9 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
- f.fields_for(:comments, comments) do |cf|
+ concat f.fields_for(:comments, comments) { |cf|
concat cf.text_field(:name)
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -946,10 +951,10 @@ class FormHelperTest < ActionView::TestCase
form_for(:post, @post) do |f|
concat f.text_field(:title)
- f.fields_for(:comments) do |cf|
+ concat f.fields_for(:comments) { |cf|
concat cf.text_field(:name)
yielded_comments << cf.object
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -967,9 +972,9 @@ class FormHelperTest < ActionView::TestCase
@post.comments = []
form_for(:post, @post) do |f|
- f.fields_for(:comments, Comment.new(321), :child_index => 'abc') do |cf|
+ concat f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf|
concat cf.text_field(:name)
- end
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -987,24 +992,24 @@ class FormHelperTest < ActionView::TestCase
@post.tags[0].relevances = []
@post.tags[1].relevances = []
form_for(:post, @post) do |f|
- f.fields_for(:comments, @post.comments[0]) do |cf|
+ concat f.fields_for(:comments, @post.comments[0]) { |cf|
concat cf.text_field(:name)
- cf.fields_for(:relevances, CommentRelevance.new(314)) do |crf|
+ concat cf.fields_for(:relevances, CommentRelevance.new(314)) { |crf|
concat crf.text_field(:value)
- end
- end
- f.fields_for(:tags, @post.tags[0]) do |tf|
+ }
+ }
+ concat f.fields_for(:tags, @post.tags[0]) { |tf|
concat tf.text_field(:value)
- tf.fields_for(:relevances, TagRelevance.new(3141)) do |trf|
+ concat tf.fields_for(:relevances, TagRelevance.new(3141)) { |trf|
concat trf.text_field(:value)
- end
- end
- f.fields_for('tags', @post.tags[1]) do |tf|
+ }
+ }
+ concat f.fields_for('tags', @post.tags[1]) { |tf|
concat tf.text_field(:value)
- tf.fields_for(:relevances, TagRelevance.new(31415)) do |trf|
+ concat tf.fields_for(:relevances, TagRelevance.new(31415)) { |trf|
concat trf.text_field(:value)
- end
- end
+ }
+ }
end
expected = '<form action="http://www.example.com" method="post">' +
@@ -1026,7 +1031,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for
- fields_for(:post, @post) do |f|
+ output_buffer = fields_for(:post, @post) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1042,7 +1047,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_index
- fields_for("post[]", @post) do |f|
+ output_buffer = fields_for("post[]", @post) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1058,7 +1063,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_nil_index_option_override
- fields_for("post[]", @post, :index => nil) do |f|
+ output_buffer = fields_for("post[]", @post, :index => nil) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1074,7 +1079,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_index_option_override
- fields_for("post[]", @post, :index => "abc") do |f|
+ output_buffer = fields_for("post[]", @post, :index => "abc") do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1090,7 +1095,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_without_object
- fields_for(:post) do |f|
+ output_buffer = fields_for(:post) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1106,7 +1111,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_only_object
- fields_for(@post) do |f|
+ output_buffer = fields_for(@post) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1122,7 +1127,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_object_with_bracketed_name
- fields_for("author[post]", @post) do |f|
+ output_buffer = fields_for("author[post]", @post) do |f|
concat f.label(:title)
concat f.text_field(:title)
end
@@ -1133,7 +1138,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_object_with_bracketed_name_and_index
- fields_for("author[post]", @post, :index => 1) do |f|
+ output_buffer = fields_for("author[post]", @post, :index => 1) do |f|
concat f.label(:title)
concat f.text_field(:title)
end
@@ -1152,9 +1157,9 @@ class FormHelperTest < ActionView::TestCase
concat post_form.text_field(:title)
concat post_form.text_area(:body)
- fields_for(:parent_post, @post) do |parent_fields|
+ concat fields_for(:parent_post, @post) { |parent_fields|
concat parent_fields.check_box(:secret)
- end
+ }
end
expected =
@@ -1173,9 +1178,9 @@ class FormHelperTest < ActionView::TestCase
concat post_form.text_field(:title)
concat post_form.text_area(:body)
- post_form.fields_for(@comment) do |comment_fields|
+ concat post_form.fields_for(@comment) { |comment_fields|
concat comment_fields.text_field(:name)
- end
+ }
end
expected =
@@ -1272,7 +1277,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_labelled_builder
- fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1363,7 +1368,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_new_object
post = Post.new
- post.new_record = true
+ post.persisted = false
def post.id() nil end
form_for(post) do |f| end
@@ -1373,9 +1378,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_existing_object_in_list
- @post.new_record = false
@comment.save
-
form_for([@post, @comment]) {}
expected = %(<form action="#{comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="_method" type="hidden" value="put" /></div></form>)
@@ -1383,8 +1386,6 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_new_object_in_list
- @post.new_record = false
-
form_for([@post, @comment]) {}
expected = %(<form action="#{comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>)
@@ -1392,9 +1393,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_existing_object_and_namespace_in_list
- @post.new_record = false
@comment.save
-
form_for([:admin, @post, @comment]) {}
expected = %(<form action="#{admin_comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="_method" type="hidden" value="put" /></div></form>)
@@ -1402,8 +1401,6 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_new_object_and_namespace_in_list
- @post.new_record = false
-
form_for([:admin, @post, @comment]) {}
expected = %(<form action="#{admin_comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>)
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index aa40e46aa8..5799e3db53 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -293,7 +293,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
@post.category = "<mus>"
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.select(:category, %w( abe <mus> hest))
end
@@ -307,7 +307,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
@post.category = "<mus>"
- fields_for :post, @post, :index => 108 do |f|
+ output_buffer = fields_for :post, @post, :index => 108 do |f|
concat f.select(:category, %w( abe <mus> hest))
end
@@ -322,7 +322,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post.category = "<mus>"
def @post.to_param; 108; end
- fields_for "post[]", @post do |f|
+ output_buffer = fields_for "post[]", @post do |f|
concat f.select(:category, %w( abe <mus> hest))
end
@@ -336,7 +336,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
options = "<option value=\"abe\">abe</option><option value=\"mus\">mus</option><option value=\"hest\">hest</option>"
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.select(:category, options, :prompt => 'The prompt')
end
@@ -462,7 +462,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
@post.author_name = "Babe"
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.collection_select(:author_name, dummy_posts, :author_name, :author_name)
end
@@ -476,7 +476,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
@post.author_name = "Babe"
- fields_for :post, @post, :index => 815 do |f|
+ output_buffer = fields_for :post, @post, :index => 815 do |f|
concat f.collection_select(:author_name, dummy_posts, :author_name, :author_name)
end
@@ -491,7 +491,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post.author_name = "Babe"
def @post.to_param; 815; end
- fields_for "post[]", @post do |f|
+ output_buffer = fields_for "post[]", @post do |f|
concat f.collection_select(:author_name, dummy_posts, :author_name, :author_name)
end
@@ -570,7 +570,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_under_fields_for
@firm = Firm.new("D")
- fields_for :firm, @firm do |f|
+ output_buffer = fields_for :firm, @firm do |f|
concat f.time_zone_select(:time_zone)
end
@@ -589,7 +589,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_under_fields_for_with_index
@firm = Firm.new("D")
- fields_for :firm, @firm, :index => 305 do |f|
+ output_buffer = fields_for :firm, @firm, :index => 305 do |f|
concat f.time_zone_select(:time_zone)
end
@@ -609,7 +609,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@firm = Firm.new("D")
def @firm.to_param; 305; end
- fields_for "firm[]", @firm do |f|
+ output_buffer = fields_for "firm[]", @firm do |f|
concat f.time_zone_select(:time_zone)
end
@@ -787,7 +787,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
@post.origin = 'dk'
- fields_for :post, @post do |f|
+ output_buffer = fields_for :post, @post do |f|
concat f.grouped_collection_select("origin", @continents, :countries, :continent_name, :country_id, :country_name)
end
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 6ac5df1ea9..868a35c476 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -3,17 +3,16 @@ require 'abstract_unit'
class FormTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormTagHelper
- include ActiveSupport::Configurable
- DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
+ # include ActiveSupport::Configurable
+ # DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
def setup
super
- @controller = Class.new do
+ @controller = Class.new(BasicController) do
def url_for(options)
"http://www.example.com"
end
- end
- @controller = @controller.new
+ end.new
end
VALID_HTML_ID = /^[A-Za-z][-_:.A-Za-z0-9]*$/ # see http://www.w3.org/TR/html4/types.html#type-name
@@ -60,16 +59,14 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_form_tag_with_block_in_erb
- __in_erb_template = ''
- form_tag("http://example.com") { concat "Hello world!" }
+ output_buffer = form_tag("http://example.com") { concat "Hello world!" }
expected = %(<form action="http://example.com" method="post">Hello world!</form>)
assert_dom_equal expected, output_buffer
end
def test_form_tag_with_block_and_method_in_erb
- __in_erb_template = ''
- form_tag("http://example.com", :method => :put) { concat "Hello world!" }
+ output_buffer = form_tag("http://example.com", :method => :put) { concat "Hello world!" }
expected = %(<form action="http://example.com" method="post"><div style='margin:0;padding:0;display:inline'><input type="hidden" name="_method" value="put" /></div>Hello world!</form>)
assert_dom_equal expected, output_buffer
@@ -127,19 +124,19 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_select_tag
- actual = select_tag "people", "<option>david</option>"
+ actual = select_tag "people", "<option>david</option>".html_safe
expected = %(<select id="people" name="people"><option>david</option></select>)
assert_dom_equal expected, actual
end
def test_select_tag_with_multiple
- actual = select_tag "colors", "<option>Red</option><option>Blue</option><option>Green</option>", :multiple => :true
+ actual = select_tag "colors", "<option>Red</option><option>Blue</option><option>Green</option>".html_safe, :multiple => :true
expected = %(<select id="colors" multiple="multiple" name="colors"><option>Red</option><option>Blue</option><option>Green</option></select>)
assert_dom_equal expected, actual
end
def test_select_tag_disabled
- actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :disabled => :true
+ actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>".html_safe, :disabled => :true
expected = %(<select id="places" disabled="disabled" name="places"><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
@@ -150,13 +147,13 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_select_tag_with_include_blank
- actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :include_blank => true
+ actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>".html_safe, :include_blank => true
expected = %(<select id="places" name="places"><option value=""></option><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
def test_select_tag_with_include_blank_with_string
- actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :include_blank => "string"
+ actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>".html_safe, :include_blank => "string"
expected = %(<select id="places" name="places"><option value="">string</option><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
@@ -282,9 +279,9 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)
assert_dom_equal %(<input type="checkbox" />), tag(:input, :type => "checkbox", :checked => false)
- assert_dom_equal %(<select id="people" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => true)
- assert_dom_equal %(<select id="people_" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people[]", "<option>david</option>", :multiple => true)
- assert_dom_equal %(<select id="people" name="people"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => nil)
+ assert_dom_equal %(<select id="people" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people", "<option>david</option>".html_safe, :multiple => true)
+ assert_dom_equal %(<select id="people_" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people[]", "<option>david</option>".html_safe, :multiple => true)
+ assert_dom_equal %(<select id="people" name="people"><option>david</option></select>), select_tag("people", "<option>david</option>".html_safe, :multiple => nil)
end
def test_stringify_symbol_keys
@@ -333,26 +330,22 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_field_set_tag_in_erb
- __in_erb_template = ''
- field_set_tag("Your details") { concat "Hello world!" }
+ output_buffer = field_set_tag("Your details") { concat "Hello world!" }
expected = %(<fieldset><legend>Your details</legend>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
- self.output_buffer = ''.html_safe
- field_set_tag { concat "Hello world!" }
+ output_buffer = field_set_tag { concat "Hello world!" }
expected = %(<fieldset>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
- self.output_buffer = ''.html_safe
- field_set_tag('') { concat "Hello world!" }
+ output_buffer = field_set_tag('') { concat "Hello world!" }
expected = %(<fieldset>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
- self.output_buffer = ''.html_safe
- field_set_tag('', :class => 'format') { concat "Hello world!" }
+ output_buffer = field_set_tag('', :class => 'format') { concat "Hello world!" }
expected = %(<fieldset class="format">Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index b3e7abc387..f49b763881 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -7,8 +7,9 @@ class JavaScriptHelperTest < ActionView::TestCase
attr_accessor :formats, :output_buffer
- def reset_formats(format)
- @format = format
+ def update_details(details)
+ @details = details
+ yield if block_given?
end
def setup
@@ -73,18 +74,6 @@ class JavaScriptHelperTest < ActionView::TestCase
javascript_tag("alert('hello')", :id => "the_js_tag")
end
- def test_javascript_tag_with_block_in_erb
- __in_erb_template = ''
- javascript_tag { concat "alert('hello')" }
- assert_dom_equal "<script type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>", output_buffer
- end
-
- def test_javascript_tag_with_block_and_options_in_erb
- __in_erb_template = ''
- javascript_tag(:id => "the_js_tag") { concat "alert('hello')" }
- assert_dom_equal "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>", output_buffer
- end
-
def test_javascript_cdata_section
assert_dom_equal "\n//<![CDATA[\nalert('hello')\n//]]>\n", javascript_cdata_section("alert('hello')")
end
diff --git a/actionpack/test/template/subscriber_test.rb b/actionpack/test/template/log_subscriber_test.rb
index 8bacab7088..5076dfa70f 100644
--- a/actionpack/test/template/subscriber_test.rb
+++ b/actionpack/test/template/log_subscriber_test.rb
@@ -1,22 +1,22 @@
require "abstract_unit"
-require "rails/subscriber/test_helper"
-require "action_view/railties/subscriber"
+require "rails/log_subscriber/test_helper"
+require "action_view/railties/log_subscriber"
require "controller/fake_models"
-class AVSubscriberTest < ActiveSupport::TestCase
- include Rails::Subscriber::TestHelper
+class AVLogSubscriberTest < ActiveSupport::TestCase
+ include Rails::LogSubscriber::TestHelper
def setup
+ super
@old_logger = ActionController::Base.logger
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
Rails.stubs(:root).returns(File.expand_path(FIXTURE_LOAD_PATH))
- Rails::Subscriber.add(:action_view, ActionView::Railties::Subscriber.new)
- super
+ Rails::LogSubscriber.add(:action_view, ActionView::Railties::LogSubscriber.new)
end
def teardown
super
- Rails::Subscriber.subscribers.clear
+ Rails::LogSubscriber.log_subscribers.clear
ActionController::Base.logger = @old_logger
end
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
new file mode 100644
index 0000000000..697ebc694a
--- /dev/null
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -0,0 +1,165 @@
+require "abstract_unit"
+require "abstract_controller/rendering"
+
+ActionView::LookupContext::DetailsKey.class_eval do
+ def self.details_keys
+ @details_keys
+ end
+end
+
+class LookupContextTest < ActiveSupport::TestCase
+ def setup
+ @lookup_context = ActionView::LookupContext.new(FIXTURE_LOAD_PATH, {})
+ end
+
+ def teardown
+ I18n.locale = :en
+ ActionView::LookupContext::DetailsKey.details_keys.clear
+ end
+
+ test "process view paths on initialization" do
+ assert_kind_of ActionView::PathSet, @lookup_context.view_paths
+ end
+
+ test "normalizes details on initialization" do
+ formats = Mime::SET + [nil]
+ locale = [I18n.locale, nil]
+ assert_equal Hash[:formats => formats, :locale => locale], @lookup_context.details
+ end
+
+ test "allows me to set details" do
+ @lookup_context.details = { :formats => [:html], :locale => :pt }
+ assert_equal Hash[:formats => [:html, nil], :locale => [:pt, nil]], @lookup_context.details
+ end
+
+ test "does not allow details to be modified in place" do
+ assert @lookup_context.details.frozen?
+ end
+
+ test "allows me to update an specific detail" do
+ @lookup_context.update_details(:locale => :pt)
+ assert_equal :pt, I18n.locale
+ formats = Mime::SET + [nil]
+ locale = [I18n.locale, nil]
+ assert_equal Hash[:formats => formats, :locale => locale], @lookup_context.details
+ end
+
+ test "allows me to change some details to execute an specific block of code" do
+ formats = Mime::SET + [nil]
+ @lookup_context.update_details(:locale => :pt) do
+ assert_equal Hash[:formats => formats, :locale => [:pt, nil]], @lookup_context.details
+ end
+ assert_equal Hash[:formats => formats, :locale => [:en, nil]], @lookup_context.details
+ end
+
+ test "provides getters and setters for formats" do
+ @lookup_context.formats = :html
+ assert_equal [:html], @lookup_context.formats
+ end
+
+ test "handles */* formats" do
+ @lookup_context.formats = [:"*/*"]
+ assert_equal Mime::SET, @lookup_context.formats
+ end
+
+ test "provides getters and setters for locale" do
+ @lookup_context.locale = :pt
+ assert_equal :pt, @lookup_context.locale
+ end
+
+ test "changing lookup_context locale, changes I18n.locale" do
+ @lookup_context.locale = :pt
+ assert_equal :pt, I18n.locale
+ end
+
+ test "delegates changing the locale to the I18n configuration object if it contains a lookup_context object" do
+ begin
+ I18n.config = AbstractController::I18nProxy.new(I18n.config, @lookup_context)
+ @lookup_context.locale = :pt
+ assert_equal :pt, I18n.locale
+ assert_equal :pt, @lookup_context.locale
+ ensure
+ I18n.config = I18n.config.i18n_config
+ end
+
+ assert_equal :pt, I18n.locale
+ end
+
+ test "find templates using the given view paths and configured details" do
+ template = @lookup_context.find("hello_world", "test")
+ assert_equal "Hello world!", template.source
+
+ @lookup_context.locale = :da
+ template = @lookup_context.find("hello_world", "test")
+ assert_equal "Hey verden", template.source
+ end
+
+ test "adds fallbacks to view paths when required" do
+ assert_equal 1, @lookup_context.view_paths.size
+
+ @lookup_context.with_fallbacks do
+ assert_equal 3, @lookup_context.view_paths.size
+ assert @lookup_context.view_paths.include?(ActionView::FileSystemResolver.new(""))
+ assert @lookup_context.view_paths.include?(ActionView::FileSystemResolver.new("/"))
+ end
+ end
+
+ test "add fallbacks just once in nested fallbacks calls" do
+ @lookup_context.with_fallbacks do
+ @lookup_context.with_fallbacks do
+ assert_equal 3, @lookup_context.view_paths.size
+ end
+ end
+ end
+
+ test "generates a new details key for each details hash" do
+ keys = []
+ keys << @lookup_context.details_key
+ assert_equal 1, keys.uniq.size
+
+ @lookup_context.locale = :da
+ keys << @lookup_context.details_key
+ assert_equal 2, keys.uniq.size
+
+ @lookup_context.locale = :en
+ keys << @lookup_context.details_key
+ assert_equal 2, keys.uniq.size
+
+ @lookup_context.formats = :html
+ keys << @lookup_context.details_key
+ assert_equal 3, keys.uniq.size
+
+ @lookup_context.formats = nil
+ keys << @lookup_context.details_key
+ assert_equal 3, keys.uniq.size
+ end
+
+ test "gives the key forward to the resolver, so it can be used as cache key" do
+ @lookup_context.view_paths = ActionView::FixtureResolver.new("test/_foo.erb" => "Foo")
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # Now we are going to change the template, but it won't change the returned template
+ # since we will hit the cache.
+ @lookup_context.view_paths.first.hash["test/_foo.erb"] = "Bar"
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # This time we will change the locale. The updated template should be picked since
+ # lookup_context generated a new key after we changed the locale.
+ @lookup_context.locale = :da
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Bar", template.source
+
+ # Now we will change back the locale and it will still pick the old template.
+ # This is expected because lookup_context will reuse the previous key for :en locale.
+ @lookup_context.locale = :en
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # Finally, we can expire the cache. And the expected template will be used.
+ @lookup_context.view_paths.first.clear_cache
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Bar", template.source
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index d95bdc2b90..619293dc43 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -4,6 +4,7 @@ require 'active_model'
class Bunny < Struct.new(:Bunny, :id)
extend ActiveModel::Naming
include ActiveModel::Conversion
+ def to_key() id ? [id] : nil end
end
class Author
@@ -11,6 +12,7 @@ class Author
include ActiveModel::Conversion
attr_reader :id
+ def to_key() id ? [id] : nil end
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@@ -23,6 +25,7 @@ class Article
include ActiveModel::Conversion
attr_reader :id
attr_reader :author_id
+ def to_key() id ? [id] : nil end
def save; @id = 1; @author_id = 1 end
def new_record?; @id.nil? end
def name
@@ -36,8 +39,9 @@ class Author::Nested < Author; end
class PrototypeHelperBaseTest < ActionView::TestCase
attr_accessor :formats, :output_buffer
- def reset_formats(format)
- @format = format
+ def update_details(details)
+ @details = details
+ yield if block_given?
end
def setup
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb
index 1cd18c0692..74d7bba4fe 100644
--- a/actionpack/test/template/record_tag_helper_test.rb
+++ b/actionpack/test/template/record_tag_helper_test.rb
@@ -18,6 +18,7 @@ class RecordTagHelperTest < ActionView::TestCase
def setup
super
@post = Post.new
+ @post.persisted = true
end
def test_content_tag_for
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index fdf3db1cdb..cea8ab1bce 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -33,17 +33,17 @@ module RenderTestCases
end
def test_render_file_with_localization
- old_locale, I18n.locale = I18n.locale, :da
+ old_locale, @view.locale = @view.locale, :da
assert_equal "Hey verden", @view.render(:file => "test/hello_world")
ensure
- I18n.locale = old_locale
+ @view.locale = old_locale
end
def test_render_file_with_dashed_locale
- old_locale, I18n.locale = I18n.locale, :"pt-BR"
+ old_locale, @view.locale = @view.locale, :"pt-BR"
assert_equal "Ola mundo", @view.render(:file => "test/hello_world")
ensure
- I18n.locale = old_locale
+ @view.locale = old_locale
end
def test_render_file_at_top_level
@@ -108,7 +108,7 @@ module RenderTestCases
@view.render(:partial => "test/raise")
flunk "Render did not raise Template::Error"
rescue ActionView::Template::Error => e
- assert_match "undefined local variable or method `doesnt_exist'", e.message
+ assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
@@ -118,7 +118,7 @@ module RenderTestCases
@view.render(:file => "test/sub_template_raise")
flunk "Render did not raise Template::Error"
rescue ActionView::Template::Error => e
- assert_match "undefined local variable or method `doesnt_exist'", e.message
+ assert_match %r!method.*doesnt_exist!, e.message
assert_equal "Trace of template inclusion: #{File.expand_path("#{FIXTURE_LOAD_PATH}/test/sub_template_raise.html.erb")}", e.sub_template_message
assert_equal "1", e.line_number
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
@@ -233,6 +233,11 @@ module RenderTestCases
@view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield")
end
+ def test_render_with_file_in_layout
+ assert_equal %(\n<title>title</title>\n\n),
+ @view.render(:file => "test/layout_render_file.erb")
+ end
+
if '1.9'.respond_to?(:force_encoding)
def test_render_utf8_template_with_magic_comment
with_external_encoding Encoding::ASCII_8BIT do
@@ -265,7 +270,7 @@ class CachedViewRenderTest < ActiveSupport::TestCase
# Ensure view path cache is primed
def setup
view_paths = ActionController::Base.view_paths
- assert_equal ActionView::FileSystemResolverWithFallback, view_paths.first.class
+ assert_equal ActionView::FileSystemResolver, view_paths.first.class
setup_view(view_paths)
end
end
@@ -276,9 +281,9 @@ class LazyViewRenderTest < ActiveSupport::TestCase
# Test the same thing as above, but make sure the view path
# is not eager loaded
def setup
- path = ActionView::FileSystemResolverWithFallback.new(FIXTURE_LOAD_PATH)
+ path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::Base.process_view_paths(path)
- assert_equal ActionView::FileSystemResolverWithFallback, view_paths.first.class
+ assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first
setup_view(view_paths)
end
end
diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb
index 433f6514cf..256d9bdcde 100644
--- a/actionpack/test/template/tag_helper_test.rb
+++ b/actionpack/test/template/tag_helper_test.rb
@@ -37,18 +37,18 @@ class TagHelperTest < ActionView::TestCase
assert content_tag("a", "Create", "href" => "create").html_safe?
assert_equal content_tag("a", "Create", "href" => "create"),
content_tag("a", "Create", :href => "create")
+ assert_equal "<p>&lt;script&gt;evil_js&lt;/script&gt;</p>",
+ content_tag(:p, '<script>evil_js</script>')
end
def test_content_tag_with_block_in_erb
- __in_erb_template = ''
- content_tag(:div) { concat "Hello world!" }
- assert_dom_equal "<div>Hello world!</div>", output_buffer
+ buffer = content_tag(:div) { concat "Hello world!" }
+ assert_dom_equal "<div>Hello world!</div>", buffer
end
def test_content_tag_with_block_and_options_in_erb
- __in_erb_template = ''
- content_tag(:div, :class => "green") { concat "Hello world!" }
- assert_dom_equal %(<div class="green">Hello world!</div>), output_buffer
+ buffer = content_tag(:div, :class => "green") { concat "Hello world!" }
+ assert_dom_equal %(<div class="green">Hello world!</div>), buffer
end
def test_content_tag_with_block_and_options_out_of_erb
@@ -66,10 +66,10 @@ class TagHelperTest < ActionView::TestCase
output_buffer
end
+ # TAG TODO: Move this into a real template
def test_content_tag_nested_in_content_tag_in_erb
- __in_erb_template = true
- content_tag("p") { concat content_tag("b", "Hello") }
- assert_equal '<p><b>Hello</b></p>', output_buffer
+ buffer = content_tag("p") { concat content_tag("b", "Hello") }
+ assert_equal '<p><b>Hello</b></p>', buffer
end
def test_content_tag_with_escaped_array_class
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index be2c6b3108..195a6ea3ae 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -89,16 +89,23 @@ module ActionView
end
test "helper class that is being tested is always included in view instance" do
- self.class.helper_class.module_eval do
- def render_from_helper
- render :partial => 'customer', :collection => @customers
+ # This ensure is a hidious hack to deal with these tests bleeding
+ # methods between eachother
+ begin
+ self.class.helper_class.module_eval do
+ def render_from_helper
+ render :partial => 'customer', :collection => @customers
+ end
end
- end
- TestController.stubs(:controller_path).returns('test')
+ TestController.stubs(:controller_path).returns('test')
- @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')]
- assert_match /Hello: EloyHello: Manfred/, render(:partial => 'test/from_helper')
+ @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')]
+ assert_match /Hello: EloyHello: Manfred/, render(:partial => 'test/from_helper')
+
+ ensure
+ self.class.helper_class.send(:remove_method, :render_from_helper)
+ end
end
test "no additional helpers should shared across test cases" do
@@ -107,7 +114,7 @@ module ActionView
end
test "is able to use routes" do
- controller.request.assign_parameters('foo', 'index')
+ controller.request.assign_parameters(@router, 'foo', 'index')
assert_equal '/foo', url_for
assert_equal '/bar', url_for(:controller => 'bar')
end
@@ -147,10 +154,16 @@ module ActionView
end
test "is able to make methods available to the view" do
- _helpers.module_eval do
- def render_from_helper; from_test_case end
+ # This ensure is a hidious hack to deal with these tests bleeding
+ # methods between eachother
+ begin
+ _helpers.module_eval do
+ def render_from_helper; from_test_case end
+ end
+ assert_equal 'Word!', render(:partial => 'test/from_helper')
+ ensure
+ _helpers.send(:remove_method, :render_from_helper)
end
- assert_equal 'Word!', render(:partial => 'test/from_helper')
end
def from_test_case; 'Word!'; end
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 4b73c44f7e..699fb2f5bc 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -18,6 +18,11 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert_equal expected, translate(:foo)
end
+ def test_translation_of_an_array
+ I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"])
+ assert_equal ["foo", "bar"], translate(["foo", "bar"])
+ end
+
def test_delegates_localize_to_i18n
@time = Time.utc(2008, 7, 8, 12, 18, 38)
I18n.expects(:localize).with(@time)
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index e904e88f49..165cb655da 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -1,22 +1,21 @@
# encoding: utf-8
require 'abstract_unit'
+require 'active_support/ordered_options'
require 'controller/fake_controllers'
-RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port, :env)
-
class UrlHelperTest < ActionView::TestCase
- include ActiveSupport::Configurable
- DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
def setup
super
- @controller = Class.new do
- attr_accessor :url, :request
+ @controller = Class.new(BasicController) do
+ attr_accessor :url
def url_for(options)
url
end
end
+
@controller = @controller.new
+ @request = @controller.request = ActionDispatch::TestRequest.new
@controller.url = "http://www.example.com"
end
@@ -38,12 +37,13 @@ class UrlHelperTest < ActionView::TestCase
end
def test_url_for_with_back
- @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+ @request.env['HTTP_REFERER'] = 'http://www.example.com/referer'
assert_equal 'http://www.example.com/referer', url_for(:back)
end
def test_url_for_with_back_and_no_referer
- @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+ @request.env['HOST_NAME'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
assert_equal 'javascript:history.back()', url_for(:back)
end
@@ -122,7 +122,6 @@ class UrlHelperTest < ActionView::TestCase
url = {:controller => 'weblog', :action => 'show'}
@controller = ActionController::Base.new
@controller.request = ActionController::TestRequest.new
- @controller.url = ActionController::UrlRewriter.new(@controller.request, url)
assert_dom_equal(%q{<a href="/weblog/show">Test Link</a>}, link_to('Test Link', url))
end
@@ -131,7 +130,6 @@ class UrlHelperTest < ActionView::TestCase
url = {:controller => 'weblog', :action => 'show', :host => 'www.example.com'}
@controller = ActionController::Base.new
@controller.request = ActionController::TestRequest.new
- @controller.url = ActionController::UrlRewriter.new(@controller.request, url)
assert_dom_equal(%q{<a href="http://www.example.com/weblog/show">Test Link</a>}, link_to('Test Link', url))
end
@@ -144,22 +142,28 @@ class UrlHelperTest < ActionView::TestCase
end
def test_link_tag_with_back
- @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+ @request.env['HOST_NAME'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
+ @request.env['HTTP_REFERER'] = 'http://www.example.com/referer'
assert_dom_equal "<a href=\"http://www.example.com/referer\">go back</a>", link_to('go back', :back)
end
def test_link_tag_with_back_and_no_referer
- @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+ @request.env['HOST_NAME'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
assert_dom_equal "<a href=\"javascript:history.back()\">go back</a>", link_to('go back', :back)
end
def test_link_tag_with_back
- @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+ @request.env['HOST_NAME'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
+ @request.env['HTTP_REFERER'] = 'http://www.example.com/referer'
assert_dom_equal "<a href=\"http://www.example.com/referer\">go back</a>", link_to('go back', :back)
end
def test_link_tag_with_back_and_no_referer
- @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+ @request.env['HOST_NAME'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
assert_dom_equal "<a href=\"javascript:history.back()\">go back</a>", link_to('go back', :back)
end
@@ -263,55 +267,60 @@ class UrlHelperTest < ActionView::TestCase
end
def test_current_page_with_simple_url
- @controller.request = RequestMock.new("http://www.example.com/weblog/show")
+ @request.env['HTTP_HOST'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
@controller.url = "http://www.example.com/weblog/show"
assert current_page?({ :action => "show", :controller => "weblog" })
assert current_page?("http://www.example.com/weblog/show")
end
def test_current_page_ignoring_params
- @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
+ @request.env['HTTP_HOST'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
+ @request.env['QUERY_STRING'] = 'order=desc&page=1'
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert current_page?({ :action => "show", :controller => "weblog" })
assert current_page?("http://www.example.com/weblog/show")
end
def test_current_page_with_params_that_match
- @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
+ @request.env['HTTP_HOST'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
+ @request.env['QUERY_STRING'] = 'order=desc&page=1'
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert current_page?({ :action => "show", :controller => "weblog", :order => "desc", :page => "1" })
assert current_page?("http://www.example.com/weblog/show?order=desc&amp;page=1")
end
def test_link_unless_current
- @controller.request = RequestMock.new("http://www.example.com/weblog/show")
+ @request.env['HTTP_HOST'] = 'www.example.com'
+ @request.env['PATH_INFO'] = '/weblog/show'
@controller.url = "http://www.example.com/weblog/show"
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
- @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
+ @request.env['QUERY_STRING'] = 'order=desc'
@controller.url = "http://www.example.com/weblog/show"
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show")
- @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
+ @request.env['QUERY_STRING'] = 'order=desc&page=1'
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog", :order=>'desc', :page=>'1' })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=1")
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=1")
- @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
+ @request.env['QUERY_STRING'] = 'order=desc'
@controller.url = "http://www.example.com/weblog/show?order=asc"
assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "<a href=\"http://www.example.com/weblog/show?order=asc\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=asc")
- @controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
+ @request.env['QUERY_STRING'] = 'order=desc&page=1'
@controller.url = "http://www.example.com/weblog/show?order=desc&page=2"
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&page=2\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&amp;page=2\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=2")
-
- @controller.request = RequestMock.new("http://www.example.com/weblog/show")
+ @request.env['QUERY_STRING'] = ''
@controller.url = "http://www.example.com/weblog/list"
assert_equal "<a href=\"http://www.example.com/weblog/list\">Listing</a>",
link_to_unless_current("Listing", :action => "list", :controller => "weblog")
@@ -346,7 +355,7 @@ class UrlHelperTest < ActionView::TestCase
end
def test_mail_to_with_img
- assert_dom_equal %(<a href="mailto:feedback@example.com"><img src="/feedback.png" /></a>), mail_to('feedback@example.com', '<img src="/feedback.png" />')
+ 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
@@ -464,8 +473,6 @@ end
class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase
def setup
super
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@controller = TasksController.new
end
@@ -499,14 +506,14 @@ end
class Workshop
extend ActiveModel::Naming
include ActiveModel::Conversion
- attr_accessor :id, :new_record
+ attr_accessor :id
- def initialize(id, new_record)
- @id, @new_record = id, new_record
+ def initialize(id)
+ @id = id
end
- def new_record?
- @new_record
+ def persisted?
+ id.present?
end
def to_s
@@ -517,14 +524,14 @@ end
class Session
extend ActiveModel::Naming
include ActiveModel::Conversion
- attr_accessor :id, :workshop_id, :new_record
+ attr_accessor :id, :workshop_id
- def initialize(id, new_record)
- @id, @new_record = id, new_record
+ def initialize(id)
+ @id = id
end
- def new_record?
- @new_record
+ def persisted?
+ id.present?
end
def to_s
@@ -534,12 +541,12 @@ end
class WorkshopsController < ActionController::Base
def index
- @workshop = Workshop.new(1, true)
+ @workshop = Workshop.new(nil)
render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
def show
- @workshop = Workshop.new(params[:id], false)
+ @workshop = Workshop.new(params[:id])
render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
@@ -548,14 +555,14 @@ end
class SessionsController < ActionController::Base
def index
- @workshop = Workshop.new(params[:workshop_id], false)
- @session = Session.new(1, true)
+ @workshop = Workshop.new(params[:workshop_id])
+ @session = Session.new(nil)
render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
def show
- @workshop = Workshop.new(params[:workshop_id], false)
- @session = Session.new(params[:id], false)
+ @workshop = Workshop.new(params[:workshop_id])
+ @session = Session.new(params[:id])
render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
@@ -565,8 +572,7 @@ end
class PolymorphicControllerTest < ActionController::TestCase
def setup
super
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ @response = ActionController::TestResponse.new
end
def test_new_resource
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index 14c02f183f..d4a00c5073 100755
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -1,19 +1,11 @@
dir = File.dirname(__FILE__)
-require "#{dir}/lib/active_model/version"
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'activemodel'
-PKG_VERSION = ActiveModel::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
require 'rake/testtask'
task :default => :test
Rake::TestTask.new do |t|
- t.libs << "#{dir}/test"
+ t.libs << "test"
t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort
t.warning = true
end
diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index b4fbe26d97..eb9ef7b7c0 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'activemodel'
- s.version = '3.0.0.beta1'
- s.summary = "A toolkit for building other modeling frameworks like ActiveRecord"
- s.description = %q{Extracts common modeling concerns from ActiveRecord to share between similar frameworks like ActiveResource.}
+ s.version = version
+ s.summary = 'A toolkit for building modeling frameworks (part of Rails).'
+ s.description = 'A toolkit for building modeling frameworks like Active Record and Active Resource. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.'
s.required_ruby_version = '>= 1.8.7'
s.author = "David Heinemeier Hansson"
@@ -13,7 +15,7 @@ Gem::Specification.new do |s|
s.has_rdoc = true
- s.add_dependency('activesupport', '= 3.0.0.beta1')
+ s.add_dependency('activesupport', version)
s.require_path = 'lib'
s.files = Dir["CHANGELOG", "MIT-LICENSE", "README", "lib/**/*"]
diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb
index b039897ea5..0b2706076f 100644
--- a/activemodel/examples/validations.rb
+++ b/activemodel/examples/validations.rb
@@ -16,7 +16,7 @@ class Person
@persisted = true
end
- def new_record?
+ def persisted?
@persisted
end
end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index f7fb66bdfc..c1334069fa 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -21,7 +21,6 @@ module ActiveModel
# A minimal implementation could be:
#
# class Person
- #
# include ActiveModel::AttributeMethods
#
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
@@ -44,9 +43,13 @@ module ActiveModel
# def reset_attribute_to_default!(attr)
# send("#{attr}=", "Default Name")
# end
- #
# end
- #
+ #
+ # Please notice that whenever you include ActiveModel::AtributeMethods in your class,
+ # it requires you to implement a <tt>attributes</tt> methods which returns a hash with
+ # each attribute name in your model as hash key and the attribute value as hash value.
+ # Hash keys must be a string.
+ #
module AttributeMethods
extend ActiveSupport::Concern
@@ -85,7 +88,7 @@ module ActiveModel
# AttributePerson.inheritance_column
# # => 'address_id'
def define_attr_method(name, value=nil, &block)
- sing = metaclass
+ sing = singleton_class
sing.send :alias_method, "original_#{name}", name
if block_given?
sing.send :define_method, name, &block
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index c14a07c7dc..585c20dcdf 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -1,19 +1,44 @@
module ActiveModel
- # If your object is already designed to implement all of the Active Model featurs
- # include this module in your Class.
- #
- # class MyClass
+ # Handle default conversions: to_model, to_key and to_param.
+ #
+ # == Example
+ #
+ # Let's take for example this non persisted object.
+ #
+ # class ContactMessage
# include ActiveModel::Conversion
+ #
+ # # ContactMessage are never persisted in the DB
+ # def persisted?
+ # false
+ # end
# end
- #
- # Returns self to the <tt>:to_model</tt> method.
- #
- # If your model does not act like an Active Model object, then you should define
- # <tt>:to_model</tt> yourself returning a proxy object that wraps your object
- # with Active Model compliant methods.
+ #
+ # cm = ContactMessage.new
+ # cm.to_model == self #=> true
+ # cm.to_key #=> nil
+ # cm.to_param #=> nil
+ #
module Conversion
+ # If your object is already designed to implement all of the Active Model you can use
+ # the default to_model implementation, which simply returns self.
+ #
+ # If your model does not act like an Active Model object, then you should define
+ # <tt>:to_model</tt> yourself returning a proxy object that wraps your object
+ # with Active Model compliant methods.
def to_model
self
end
+
+ # Returns an Enumerable of all (primary) key attributes or nil if persisted? is fakse
+ def to_key
+ persisted? ? [id] : nil
+ end
+
+ # Returns a string representing the object's key suitable for use in URLs,
+ # or nil if persisted? is false
+ def to_param
+ to_key ? to_key.join('-') : nil
+ end
end
end
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 7bf0ad712d..13ddb622d1 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -13,6 +13,33 @@
module ActiveModel
module Lint
module Tests
+
+ # == Responds to <tt>to_key</tt>
+ #
+ # Returns an Enumerable of all (primary) key attributes
+ # or nil if model.persisted? is false
+ def test_to_key
+ assert model.respond_to?(:to_key), "The model should respond to to_key"
+ def model.persisted?() false end
+ assert model.to_key.nil?
+ end
+
+ # == Responds to <tt>to_param</tt>
+ #
+ # Returns a string representing the object's key suitable for use in URLs
+ # or nil if model.persisted? is false.
+ #
+ # Implementers can decide to either raise an exception or provide a default
+ # in case the record uses a composite primary key. There are no tests for this
+ # behavior in lint because it doesn't make sense to force any of the possible
+ # implementation strategies on the implementer. However, if the resource is
+ # not persisted?, then to_param should always return nil.
+ def test_to_param
+ assert model.respond_to?(:to_param), "The model should respond to to_param"
+ def model.persisted?() false end
+ assert model.to_param.nil?
+ end
+
# == Responds to <tt>valid?</tt>
#
# Returns a boolean that specifies whether the object is in a valid or invalid
@@ -22,21 +49,16 @@ module ActiveModel
assert_boolean model.valid?, "valid?"
end
- # == Responds to <tt>new_record?</tt>
+ # == Responds to <tt>persisted?</tt>
#
# Returns a boolean that specifies whether the object has been persisted yet.
# This is used when calculating the URL for an object. If the object is
# not persisted, a form for that object, for instance, will be POSTed to the
# collection. If it is persisted, a form for the object will put PUTed to the
# URL for the object.
- def test_new_record?
- assert model.respond_to?(:new_record?), "The model should respond to new_record?"
- assert_boolean model.new_record?, "new_record?"
- end
-
- def test_destroyed?
- assert model.respond_to?(:destroyed?), "The model should respond to destroyed?"
- assert_boolean model.destroyed?, "destroyed?"
+ def test_persisted?
+ assert model.respond_to?(:persisted?), "The model should respond to persisted?"
+ assert_boolean model.persisted?, "persisted?"
end
# == Naming
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index b9fb5fe0c8..8cdd3d2fe8 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -42,7 +42,7 @@ module ActiveModel
# To implement, just extend ActiveModel::Naming in your object:
#
# class BookCover
- # exten ActiveModel::Naming
+ # extend ActiveModel::Naming
# end
#
# BookCover.model_name #=> "BookCover"
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 03733a9c89..ba8648f8c9 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
@@ -45,6 +46,9 @@ module ActiveModel
included do
extend ActiveModel::Translation
define_callbacks :validate, :scope => :name
+
+ class_attribute :_validators
+ self._validators = Hash.new { |h,k| h[k] = [] }
end
module ClassMethods
@@ -117,12 +121,23 @@ module ActiveModel
end
set_callback(:validate, *args, &block)
end
-
- private
-
+
+ # List all validators that being used to validate the model using +validates_with+
+ # method.
+ def validators
+ _validators.values.flatten.uniq
+ end
+
+ # List all validators that being used to validate a specific attribute.
+ def validators_on(attribute)
+ _validators[attribute.to_sym]
+ end
+
+ private
+
def _merge_attributes(attr_names)
options = attr_names.extract_options!
- options.merge(:attributes => attr_names)
+ options.merge(:attributes => attr_names.flatten)
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index db563876af..83d3ea80d6 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -62,6 +62,15 @@ module ActiveModel
args.each do |klass|
validator = klass.new(options, &block)
validator.setup(self) if validator.respond_to?(:setup)
+
+ if validator.respond_to?(:attributes) && !validator.attributes.empty?
+ validator.attributes.each do |attribute|
+ _validators[attribute.to_sym] << validator
+ end
+ else
+ _validators[nil] << validator
+ end
+
validate(validator, options)
end
end
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index ad9729de00..b61f0cb266 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/module/anonymous"
+
module ActiveModel #:nodoc:
# A simple base class that can be used along with
# +ActiveModel::Validations::ClassMethods.validates_with+
@@ -88,11 +90,27 @@ module ActiveModel #:nodoc:
class Validator
attr_reader :options
+ # Returns the kind of the validator.
+ #
+ # == Examples
+ #
+ # PresenceValidator.kind #=> :presence
+ # UniquenessValidator.kind #=> :uniqueness
+ #
+ def self.kind
+ @kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
+ end
+
# Accepts options that will be made availible through the +options+ reader.
def initialize(options)
@options = options
end
+ # Return the kind for this validator.
+ def kind
+ self.class.kind
+ end
+
# Override this method in subclasses with validation logic, adding errors
# to the records +errors+ array where necessary.
def validate(record)
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index d51423ae1b..85d0eed180 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -2,8 +2,9 @@ module ActiveModel
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/activemodel/test/cases/conversion_test.rb b/activemodel/test/cases/conversion_test.rb
new file mode 100644
index 0000000000..7669bf5f65
--- /dev/null
+++ b/activemodel/test/cases/conversion_test.rb
@@ -0,0 +1,25 @@
+require 'cases/helper'
+require 'models/contact'
+
+class ConversionTest < ActiveModel::TestCase
+ test "to_model default implementation returns self" do
+ contact = Contact.new
+ assert_equal contact, contact.to_model
+ end
+
+ test "to_key default implementation returns nil for new records" do
+ assert_nil Contact.new.to_key
+ end
+
+ test "to_key default implementation returns the id in an array for persisted records" do
+ assert_equal [1], Contact.new(:id => 1).to_key
+ end
+
+ test "to_param default implementation returns nil for new records" do
+ assert_nil Contact.new.to_param
+ end
+
+ test "to_param default implementation returns a string of ids for persisted records" do
+ assert_equal "1", Contact.new(:id => 1).to_param
+ end
+end \ No newline at end of file
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index 8bcbe54651..8578ab7dbd 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -1,5 +1,8 @@
require File.expand_path('../../../../load_paths', __FILE__)
+lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
require 'config'
require 'active_model'
diff --git a/activemodel/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb
index 63804004ee..68372160cd 100644
--- a/activemodel/test/cases/lint_test.rb
+++ b/activemodel/test/cases/lint_test.rb
@@ -1,18 +1,14 @@
-require "cases/helper"
+require 'cases/helper'
class LintTest < ActiveModel::TestCase
include ActiveModel::Lint::Tests
class CompliantModel
extend ActiveModel::Naming
-
- def to_model
- self
- end
+ include ActiveModel::Conversion
def valid?() true end
- def new_record?() true end
- def destroyed?() true end
+ def persisted?() false end
def errors
obj = Object.new
diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb
index 8b9795a90c..c4d787dadb 100644
--- a/activemodel/test/cases/validations/presence_validation_test.rb
+++ b/activemodel/test/cases/validations/presence_validation_test.rb
@@ -10,6 +10,12 @@ require 'models/custom_reader'
class PresenceValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
+ teardown do
+ Topic.reset_callbacks(:validate)
+ Person.reset_callbacks(:validate)
+ CustomReader.reset_callbacks(:validate)
+ end
+
def test_validate_presences
Topic.validates_presence_of(:title, :content)
@@ -27,17 +33,21 @@ class PresenceValidationTest < ActiveModel::TestCase
t.content = "like stuff"
assert t.save
- ensure
- Topic.reset_callbacks(:validate)
+ end
+
+ test 'accepts array arguments' do
+ Topic.validates_presence_of %w(title content)
+ t = Topic.new
+ assert !t.valid?
+ assert_equal ["can't be blank"], t.errors[:title]
+ assert_equal ["can't be blank"], t.errors[:content]
end
def test_validates_acceptance_of_with_custom_error_using_quotes
- Person.validates_presence_of :karma, :message=> "This string contains 'single' and \"double\" quotes"
+ Person.validates_presence_of :karma, :message => "This string contains 'single' and \"double\" quotes"
p = Person.new
assert !p.valid?
assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last
- ensure
- Person.reset_callbacks(:validate)
end
def test_validates_presence_of_for_ruby_class
@@ -50,10 +60,8 @@ class PresenceValidationTest < ActiveModel::TestCase
p.karma = "Cold"
assert p.valid?
- ensure
- Person.reset_callbacks(:validate)
end
-
+
def test_validates_presence_of_for_ruby_class_with_custom_reader
CustomReader.validates_presence_of :karma
@@ -64,7 +72,5 @@ class PresenceValidationTest < ActiveModel::TestCase
p[:karma] = "Cold"
assert p.valid?
- ensure
- CustomReader.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index 66b072ea38..92df4dd6cd 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -9,6 +9,7 @@ class ValidatesWithTest < ActiveRecord::TestCase
def teardown
Topic.reset_callbacks(:validate)
+ Topic._validators.clear
end
ERROR_MESSAGE = "Validation error from validator"
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index eb100d1c35..9fedd84c73 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -10,6 +10,10 @@ require 'models/custom_reader'
class ValidationsTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
+ def setup
+ Topic._validators.clear
+ end
+
# Most of the tests mess with the validations of Topic, so lets repair it all the time.
# Other classes we mess with will be dealt with in the specific tests
def teardown
@@ -220,4 +224,27 @@ class ValidationsTest < ActiveModel::TestCase
assert !t.valid?
assert ["NO BLANKS HERE"], t.errors[:title]
end
+
+ def test_list_of_validators_for_model
+ Topic.validates_presence_of :title
+ Topic.validates_length_of :title, :minimum => 2
+
+ assert_equal 2, Topic.validators.count
+ assert_equal [:presence, :length], Topic.validators.map(&:kind)
+ end
+
+ def test_list_of_validators_on_an_attribute
+ Topic.validates_presence_of :title, :content
+ Topic.validates_length_of :title, :minimum => 2
+
+ assert_equal 2, Topic.validators_on(:title).count
+ assert_equal [:presence, :length], Topic.validators_on(:title).map(&:kind)
+ assert_equal 1, Topic.validators_on(:content).count
+ assert_equal [:presence], Topic.validators_on(:content).map(&:kind)
+ end
+
+ def test_accessing_instance_of_validator_on_an_attribute
+ Topic.validates_length_of :title, :minimum => 10
+ assert_equal 10, Topic.validators_on(:title).first.options[:minimum]
+ end
end
diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb
index f9fb0af027..a9009fbdef 100644
--- a/activemodel/test/models/contact.rb
+++ b/activemodel/test/models/contact.rb
@@ -1,7 +1,13 @@
class Contact
- attr_accessor :name, :age, :created_at, :awesome, :preferences
+ include ActiveModel::Conversion
+
+ attr_accessor :id, :name, :age, :created_at, :awesome, :preferences
def initialize(options = {})
options.each { |name, value| send("#{name}=", value) }
end
+
+ def persisted?
+ id
+ end
end
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 8a414a751b..e638da0f3e 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -5,25 +5,10 @@ require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'activerecord'
-PKG_VERSION = ActiveRecord::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "activerecord"
-RUBY_FORGE_USER = "webster132"
-
MYSQL_DB_USER = 'rails'
-PKG_FILES = FileList[
- "lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile"
-].exclude(/\bCVS\b|~$/)
-
def run_without_aborting(*tasks)
errors = []
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index aeaafbb694..8b1ea8596a 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'activerecord'
- s.version = '3.0.0.beta1'
+ s.version = version
s.summary = 'Object-relational mapper framework (part of Rails).'
- s.description = 'Object-relational mapper framework (part of Rails).'
+ s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'David Heinemeier Hansson'
@@ -18,7 +20,7 @@ Gem::Specification.new do |s|
s.extra_rdoc_files = %w( README )
s.rdoc_options.concat ['--main', 'README']
- s.add_dependency('activesupport', '= 3.0.0.beta1')
- s.add_dependency('activemodel', '= 3.0.0.beta1')
- s.add_dependency('arel', '~> 0.2.1')
+ s.add_dependency('activesupport', version)
+ s.add_dependency('activemodel', version)
+ s.add_dependency('arel', '~> 0.3.1')
end
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index b79da4565d..5942640c85 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -30,7 +30,6 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include
require 'active_support'
require 'active_model'
-require 'arel'
module ActiveRecord
extend ActiveSupport::Autoload
@@ -38,8 +37,8 @@ module ActiveRecord
eager_autoload do
autoload :VERSION
- autoload :ActiveRecordError, 'active_record/base'
- autoload :ConnectionNotEstablished, 'active_record/base'
+ autoload :ActiveRecordError, 'active_record/errors'
+ autoload :ConnectionNotEstablished, 'active_record/errors'
autoload :Aggregations
autoload :AssociationPreload
@@ -106,12 +105,16 @@ module ActiveRecord
eager_autoload do
autoload :AbstractAdapter
+ autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool"
end
end
autoload :TestCase
autoload :TestFixtures, 'active_record/fixtures'
+
+ base_hook do
+ Arel::Table.engine = Arel::Sql::Engine.new(self)
+ end
end
-Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)
-I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
+I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml' \ No newline at end of file
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index a5b06460fe..6725d4e88b 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -126,7 +126,7 @@ module ActiveRecord
parent_records.each do |parent_record|
association_proxy = parent_record.send(reflection_name)
association_proxy.loaded
- association_proxy.target.push *Array.wrap(associated_record)
+ association_proxy.target.push(*Array.wrap(associated_record))
association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 57785b4c93..b69577f8dd 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1537,58 +1537,42 @@ module ActiveRecord
# has_one associated objects, according to the defined :dependent rule.
def configure_dependency_for_has_one(reflection)
if reflection.options.include?(:dependent)
- case reflection.options[:dependent]
- when :destroy
- method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
- define_method(method_name) do
- association = send(reflection.name)
- association.destroy unless association.nil?
- end
- before_destroy method_name
- when :delete
- method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
+ name = reflection.options[:dependent]
+ method_name = :"has_one_dependent_#{name}_for_#{reflection.name}"
+
+ case name
+ when :destroy, :delete
define_method(method_name) do
- # Retrieve the associated object and delete it. The retrieval
- # is necessary because there may be multiple associated objects
- # with foreign keys pointing to this object, and we only want
- # to delete the correct one, not all of them.
association = send(reflection.name)
- association.delete unless association.nil?
+ association.send(name) if association
end
- before_destroy method_name
when :nullify
- method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
define_method(method_name) do
association = send(reflection.name)
- association.update_attribute(reflection.primary_key_name, nil) unless association.nil?
+ association.update_attribute(reflection.primary_key_name, nil) if association
end
- before_destroy method_name
else
raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
end
+
+ before_destroy method_name
end
end
def configure_dependency_for_belongs_to(reflection)
if reflection.options.include?(:dependent)
- case reflection.options[:dependent]
- when :destroy
- method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
- define_method(method_name) do
- association = send(reflection.name)
- association.destroy unless association.nil?
- end
- after_destroy method_name
- when :delete
- method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
- define_method(method_name) do
- association = send(reflection.name)
- association.delete unless association.nil?
- end
- after_destroy method_name
- else
- raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
+ name = reflection.options[:dependent]
+
+ unless [:destroy, :delete].include?(name)
+ raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
+ end
+
+ method_name = :"belongs_to_dependent_#{name}_for_#{reflection.name}"
+ define_method(method_name) do
+ association = send(reflection.name)
+ association.send(name) if association
end
+ after_destroy method_name
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 365fdeb55a..095814b635 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -39,6 +39,22 @@ module ActiveRecord
end
alias :primary_key= :set_primary_key
end
+
+ module InstanceMethods
+
+ # Returns this record's primary key value wrapped in an Array
+ # or nil if the record is a new_record?
+ # This is done to comply with the AMo interface that expects
+ # every AMo compliant object to respond_to?(:to_key) and return
+ # an Enumerable object from that call, or nil if new_record?
+ # This method also takes custom primary keys specified via
+ # the +set_primary_key+ into account.
+ def to_key
+ new_record? ? nil : [ self.primary_key ]
+ end
+
+ end
+
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 318bfbe826..52587ef251 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -11,174 +11,12 @@ require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/behavior'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'active_support/core_ext/module/delegation'
+require 'arel'
+require 'active_record/errors'
module ActiveRecord #:nodoc:
- # Generic Active Record exception class.
- class ActiveRecordError < StandardError
- end
-
- # Raised when the single-table inheritance mechanism fails to locate the subclass
- # (for example due to improper usage of column that +inheritance_column+ points to).
- class SubclassNotFound < ActiveRecordError #:nodoc:
- end
-
- # Raised when an object assigned to an association has an incorrect type.
- #
- # class Ticket < ActiveRecord::Base
- # has_many :patches
- # end
- #
- # class Patch < ActiveRecord::Base
- # belongs_to :ticket
- # end
- #
- # # Comments are not patches, this assignment raises AssociationTypeMismatch.
- # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
- class AssociationTypeMismatch < ActiveRecordError
- end
-
- # Raised when unserialized object's type mismatches one specified for serializable field.
- class SerializationTypeMismatch < ActiveRecordError
- end
-
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
- class AdapterNotSpecified < ActiveRecordError
- end
-
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
- class AdapterNotFound < ActiveRecordError
- end
-
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
- class ConnectionNotEstablished < ActiveRecordError
- end
-
- # Raised when Active Record cannot find record by given id or set of ids.
- class RecordNotFound < ActiveRecordError
- end
-
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
- # saved because record is invalid.
- class RecordNotSaved < ActiveRecordError
- end
-
- # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
- class StatementInvalid < ActiveRecordError
- end
-
- # Raised when SQL statement is invalid and the application gets a blank result.
- class ThrowResult < ActiveRecordError
- end
-
- # Parent class for all specific exceptions which wrap database driver exceptions
- # provides access to the original exception also.
- class WrappedDatabaseException < StatementInvalid
- attr_reader :original_exception
-
- def initialize(message, original_exception)
- super(message)
- @original_exception = original_exception
- end
- end
-
- # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
- class RecordNotUnique < WrappedDatabaseException
- end
-
- # Raised when a record cannot be inserted or updated because it references a non-existent record.
- class InvalidForeignKey < WrappedDatabaseException
- end
-
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
- # does not match number of expected variables.
- #
- # For example, in
- #
- # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
- #
- # two placeholders are given but only one variable to fill them.
- class PreparedStatementInvalid < ActiveRecordError
- end
-
- # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
- # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
- # the page before the other.
- #
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
- class StaleObjectError < ActiveRecordError
- end
-
- # Raised when association is being configured improperly or
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
- class ConfigurationError < ActiveRecordError
- end
-
- # Raised on attempt to update record that is instantiated as read only.
- class ReadOnlyRecord < ActiveRecordError
- end
-
- # <tt>ActiveRecord::Transactions::ClassMethods.transaction</tt> uses this exception
- # to distinguish a deliberate rollback from other exceptional situations.
- # Normally, raising an exception will cause the +transaction+ method to rollback
- # the database transaction *and* pass on the exception. But if you raise an
- # <tt>ActiveRecord::Rollback</tt> exception, then the database transaction will be rolled back,
- # without passing on the exception.
- #
- # For example, you could do this in your controller to rollback a transaction:
- #
- # class BooksController < ActionController::Base
- # def create
- # Book.transaction do
- # book = Book.new(params[:book])
- # book.save!
- # if today_is_friday?
- # # The system must fail on Friday so that our support department
- # # won't be out of job. We silently rollback this transaction
- # # without telling the user.
- # raise ActiveRecord::Rollback, "Call tech support!"
- # end
- # end
- # # ActiveRecord::Rollback is the only exception that won't be passed on
- # # by ActiveRecord::Base.transaction, so this line will still be reached
- # # even on Friday.
- # redirect_to root_url
- # end
- # end
- class Rollback < ActiveRecordError
- end
-
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
- class DangerousAttributeError < ActiveRecordError
- end
-
- # Raised when unknown attributes are supplied via mass assignment.
- class UnknownAttributeError < NoMethodError
- end
-
- # Raised when an error occurred while doing a mass assignment to an attribute through the
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
- # offending attribute.
- class AttributeAssignmentError < ActiveRecordError
- attr_reader :exception, :attribute
- def initialize(message, exception, attribute)
- @exception = exception
- @attribute = attribute
- @message = message
- end
- end
-
- # Raised when there are multiple errors while doing a mass assignment through the +attributes+
- # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
- # objects, each corresponding to the error while assigning to an attribute.
- class MultiparameterAssignmentErrors < ActiveRecordError
- attr_reader :errors
- def initialize(errors)
- @errors = errors
- end
- end
-
# Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
# which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
# is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
@@ -551,8 +389,8 @@ module ActiveRecord #:nodoc:
class << self # Class methods
def colorize_logging(*args)
ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " <<
- "config.active_record.colorize_logging are deprecated. Please use " <<
- "Rails::Subscriber.colorize_logging or config.colorize_logging instead", caller
+ "config.active_record.colorize_logging are deprecated. Please use " <<
+ "Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller
end
alias :colorize_logging= :colorize_logging
@@ -1669,12 +1507,12 @@ module ActiveRecord #:nodoc:
@attributes_cache = {}
@new_record = true
ensure_proper_type
- self.attributes = attributes unless attributes.nil?
if scope = self.class.send(:current_scoped_methods)
create_with = scope.scope_for_create
create_with.each { |att,value| self.send("#{att}=", value) } if create_with
end
+ self.attributes = attributes unless attributes.nil?
result = yield self if block_given?
_run_initialize_callbacks
@@ -1767,6 +1605,11 @@ module ActiveRecord #:nodoc:
@destroyed || false
end
+ # Returns if the record is persisted, i.e. it's not a new record and it was not destroyed.
+ def persisted?
+ !(new_record? || destroyed?)
+ end
+
# :call-seq:
# save(options)
#
@@ -1816,7 +1659,7 @@ module ActiveRecord #:nodoc:
# callbacks, Observer methods, or any <tt>:dependent</tt> association
# options, use <tt>#destroy</tt>.
def delete
- self.class.delete(id) unless new_record?
+ self.class.delete(id) if persisted?
@destroyed = true
freeze
end
@@ -1824,7 +1667,7 @@ module ActiveRecord #:nodoc:
# Deletes the record in the database and freezes this instance to reflect that no changes should
# be made (since they can't be persisted).
def destroy
- unless new_record?
+ if persisted?
self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
end
@@ -1844,6 +1687,7 @@ module ActiveRecord #:nodoc:
became.instance_variable_set("@attributes", @attributes)
became.instance_variable_set("@attributes_cache", @attributes_cache)
became.instance_variable_set("@new_record", new_record?)
+ became.instance_variable_set("@destroyed", destroyed?)
became
end
@@ -1926,7 +1770,7 @@ module ActiveRecord #:nodoc:
def reload(options = nil)
clear_aggregation_cache
clear_association_cache
- @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
+ @attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
@attributes_cache = {}
self
end
@@ -1995,10 +1839,9 @@ module ActiveRecord #:nodoc:
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
def attributes
- self.attribute_names.inject({}) do |attrs, name|
- attrs[name] = read_attribute(name)
- attrs
- end
+ attrs = {}
+ attribute_names.each { |name| attrs[name] = read_attribute(name) }
+ attrs
end
# Returns an <tt>#inspect</tt>-like string for the value of the
@@ -2042,8 +1885,7 @@ module ActiveRecord #:nodoc:
def ==(comparison_object)
comparison_object.equal?(self) ||
(comparison_object.instance_of?(self.class) &&
- comparison_object.id == id &&
- !comparison_object.new_record?)
+ comparison_object.id == id && !comparison_object.new_record?)
end
# Delegates to ==
@@ -2343,7 +2185,7 @@ module ActiveRecord #:nodoc:
# Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
def comma_pair_list(hash)
- hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ")
+ hash.map { |k,v| "#{k} = #{v}" }.join(", ")
end
def quote_columns(quoter, hash)
@@ -2397,8 +2239,10 @@ module ActiveRecord #:nodoc:
include Aggregations, Transactions, Reflection, Serialization
+ NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
end
end
# TODO: Remove this and make it work with LAZY flag
require 'active_record/connection_adapters/abstract_adapter'
+ActiveRecord.run_base_hooks(ActiveRecord::Base) \ No newline at end of file
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 027d736484..abb695264e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -113,7 +113,7 @@ module ActiveRecord
def transaction(options = {})
options.assert_valid_keys :requires_new, :joinable
- last_transaction_joinable = @transaction_joinable
+ last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
if options.has_key?(:joinable)
@transaction_joinable = options[:joinable]
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 520f3c8c0c..64faaef4a0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -319,16 +319,19 @@ module ActiveRecord
def method_missing(symbol, *args)
if symbol.to_s == 'xml'
xml_column_fallback(args)
+ else
+ super
end
end
def xml_column_fallback(*args)
case @base.adapter_name.downcase
- when 'sqlite', 'mysql'
- options = args.extract_options!
- column(args[0], :text, options)
- end
+ when 'sqlite', 'mysql'
+ options = args.extract_options!
+ column(args[0], :text, options)
end
+ end
+
# Appends a primary key definition to the table definition.
# Can be called multiple times, but this is probably not a good idea.
def primary_key(name)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 7e80347f75..6ffffc8654 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -37,6 +37,7 @@ module ActiveRecord
@@row_even = true
def initialize(connection, logger = nil) #:nodoc:
+ @active = nil
@connection, @logger = connection, logger
@runtime = 0
@query_cache_enabled = false
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
new file mode 100644
index 0000000000..cf5ddca2ba
--- /dev/null
+++ b/activerecord/lib/active_record/errors.rb
@@ -0,0 +1,165 @@
+module ActiveRecord
+ # Generic Active Record exception class.
+ class ActiveRecordError < StandardError
+ end
+
+ # Raised when the single-table inheritance mechanism fails to locate the subclass
+ # (for example due to improper usage of column that +inheritance_column+ points to).
+ class SubclassNotFound < ActiveRecordError #:nodoc:
+ end
+
+ # Raised when an object assigned to an association has an incorrect type.
+ #
+ # class Ticket < ActiveRecord::Base
+ # has_many :patches
+ # end
+ #
+ # class Patch < ActiveRecord::Base
+ # belongs_to :ticket
+ # end
+ #
+ # # Comments are not patches, this assignment raises AssociationTypeMismatch.
+ # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
+ class AssociationTypeMismatch < ActiveRecordError
+ end
+
+ # Raised when unserialized object's type mismatches one specified for serializable field.
+ class SerializationTypeMismatch < ActiveRecordError
+ end
+
+ # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
+ class AdapterNotSpecified < ActiveRecordError
+ end
+
+ # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
+ class AdapterNotFound < ActiveRecordError
+ end
+
+ # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
+ class ConnectionNotEstablished < ActiveRecordError
+ end
+
+ # Raised when Active Record cannot find record by given id or set of ids.
+ class RecordNotFound < ActiveRecordError
+ end
+
+ # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
+ # saved because record is invalid.
+ class RecordNotSaved < ActiveRecordError
+ end
+
+ # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
+ class StatementInvalid < ActiveRecordError
+ end
+
+ # Raised when SQL statement is invalid and the application gets a blank result.
+ class ThrowResult < ActiveRecordError
+ end
+
+ # Parent class for all specific exceptions which wrap database driver exceptions
+ # provides access to the original exception also.
+ class WrappedDatabaseException < StatementInvalid
+ attr_reader :original_exception
+
+ def initialize(message, original_exception)
+ super(message)
+ @original_exception = original_exception
+ end
+ end
+
+ # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
+ class RecordNotUnique < WrappedDatabaseException
+ end
+
+ # Raised when a record cannot be inserted or updated because it references a non-existent record.
+ class InvalidForeignKey < WrappedDatabaseException
+ end
+
+ # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
+ # does not match number of expected variables.
+ #
+ # For example, in
+ #
+ # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
+ #
+ # two placeholders are given but only one variable to fill them.
+ class PreparedStatementInvalid < ActiveRecordError
+ end
+
+ # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
+ # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
+ # the page before the other.
+ #
+ # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
+ class StaleObjectError < ActiveRecordError
+ end
+
+ # Raised when association is being configured improperly or
+ # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
+ class ConfigurationError < ActiveRecordError
+ end
+
+ # Raised on attempt to update record that is instantiated as read only.
+ class ReadOnlyRecord < ActiveRecordError
+ end
+
+ # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
+ # to distinguish a deliberate rollback from other exceptional situations.
+ # Normally, raising an exception will cause the +transaction+ method to rollback
+ # the database transaction *and* pass on the exception. But if you raise an
+ # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
+ # without passing on the exception.
+ #
+ # For example, you could do this in your controller to rollback a transaction:
+ #
+ # class BooksController < ActionController::Base
+ # def create
+ # Book.transaction do
+ # book = Book.new(params[:book])
+ # book.save!
+ # if today_is_friday?
+ # # The system must fail on Friday so that our support department
+ # # won't be out of job. We silently rollback this transaction
+ # # without telling the user.
+ # raise ActiveRecord::Rollback, "Call tech support!"
+ # end
+ # end
+ # # ActiveRecord::Rollback is the only exception that won't be passed on
+ # # by ActiveRecord::Base.transaction, so this line will still be reached
+ # # even on Friday.
+ # redirect_to root_url
+ # end
+ # end
+ class Rollback < ActiveRecordError
+ end
+
+ # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
+ class DangerousAttributeError < ActiveRecordError
+ end
+
+ # Raised when unknown attributes are supplied via mass assignment.
+ class UnknownAttributeError < NoMethodError
+ end
+
+ # Raised when an error occurred while doing a mass assignment to an attribute through the
+ # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
+ # offending attribute.
+ class AttributeAssignmentError < ActiveRecordError
+ attr_reader :exception, :attribute
+ def initialize(message, exception, attribute)
+ @exception = exception
+ @attribute = attribute
+ @message = message
+ end
+ end
+
+ # Raised when there are multiple errors while doing a mass assignment through the +attributes+
+ # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
+ # objects, each corresponding to the error while assigning to an attribute.
+ class MultiparameterAssignmentErrors < ActiveRecordError
+ attr_reader :errors
+ def initialize(errors)
+ @errors = errors
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 068d2a25b2..fd5ffc6d77 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
module ActiveRecord
# Exception that can be raised to stop migrations from going backwards.
@@ -303,7 +303,7 @@ module ActiveRecord
case sym
when :up, :down
- metaclass.send(:alias_method_chain, sym, "benchmarks")
+ singleton_class.send(:alias_method_chain, sym, "benchmarks")
end
ensure
@ignore_new_methods = false
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index ff6c041ef4..394e1587e1 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -1,6 +1,6 @@
require 'active_support/core_ext/array'
require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
module ActiveRecord
module NamedScope
@@ -26,7 +26,7 @@ module ActiveRecord
if options.present?
Scope.init(self, options, &block)
else
- current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.spawn
+ current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
end
end
@@ -112,7 +112,7 @@ module ActiveRecord
options.call(*args)
end, &block)
end
- metaclass.instance_eval do
+ singleton_class.instance_eval do
define_method name do |*args|
scopes[name].call(self, *args)
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index e70b0d1bfb..60addd46c6 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -20,35 +20,43 @@ module ActiveRecord
end
# TODO If we require the wrong file, the error never comes up.
- require "active_record/railties/subscriber"
- subscriber ActiveRecord::Railties::Subscriber.new
+ require "active_record/railties/log_subscriber"
+ log_subscriber ActiveRecord::Railties::LogSubscriber.new
initializer "active_record.initialize_timezone" do
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
+ ActiveRecord.base_hook do
+ self.time_zone_aware_attributes = true
+ self.default_timezone = :utc
+ end
end
initializer "active_record.logger" do
- ActiveRecord::Base.logger ||= ::Rails.logger
+ ActiveRecord.base_hook { self.logger ||= ::Rails.logger }
end
initializer "active_record.set_configs" do |app|
- app.config.active_record.each do |k,v|
- ActiveRecord::Base.send "#{k}=", v
+ ActiveRecord.base_hook do
+ app.config.active_record.each do |k,v|
+ send "#{k}=", v
+ end
end
end
# This sets the database configuration from Configuration#database_configuration
# and then establishes the connection.
initializer "active_record.initialize_database" do |app|
- ActiveRecord::Base.configurations = app.config.database_configuration
- ActiveRecord::Base.establish_connection
+ ActiveRecord.base_hook do
+ self.configurations = app.config.database_configuration
+ establish_connection
+ end
end
# Expose database runtime to controller for logging.
initializer "active_record.log_runtime" do |app|
require "active_record/railties/controller_runtime"
- ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime
+ ActionController.base_hook do
+ include ActiveRecord::Railties::ControllerRuntime
+ end
end
# Setup database middleware after initializers have run
@@ -64,18 +72,22 @@ module ActiveRecord
end
initializer "active_record.load_observers" do
- ActiveRecord::Base.instantiate_observers
+ ActiveRecord.base_hook { instantiate_observers }
- ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
- ActiveRecord::Base.instantiate_observers
+ ActiveRecord.base_hook do
+ ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
+ ActiveRecord::Base.instantiate_observers
+ end
end
end
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
- unless app.config.cache_classes
- ActionDispatch::Callbacks.after do
- ActiveRecord::Base.reset_subclasses
- ActiveRecord::Base.clear_reloadable_connections!
+ ActiveRecord.base_hook do
+ unless app.config.cache_classes
+ ActionDispatch::Callbacks.after do
+ ActiveRecord::Base.reset_subclasses
+ ActiveRecord::Base.clear_reloadable_connections!
+ end
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index ed7d2a045e..fa6caa4910 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -109,7 +109,7 @@ namespace :db do
# Only connect to local databases
local_database?(config) { drop_database(config) }
rescue Exception => e
- puts "Couldn't drop #{config['database']} : #{e.inspect}"
+ $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
end
end
end
@@ -121,7 +121,7 @@ namespace :db do
begin
drop_database(config)
rescue Exception => e
- puts "Couldn't drop #{config['database']} : #{e.inspect}"
+ $stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
end
end
@@ -129,7 +129,7 @@ namespace :db do
if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
yield
else
- puts "This task only modifies local databases. #{config['database']} is on a remote host."
+ $stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host."
end
end
@@ -204,7 +204,7 @@ namespace :db do
ActiveRecord::Base.establish_connection(config)
puts ActiveRecord::Base.connection.encoding
else
- puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
end
end
@@ -216,7 +216,7 @@ namespace :db do
ActiveRecord::Base.establish_connection(config)
puts ActiveRecord::Base.connection.collation
else
- puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
end
end
diff --git a/activerecord/lib/active_record/railties/subscriber.rb b/activerecord/lib/active_record/railties/log_subscriber.rb
index fd873dbff8..48b25032ce 100644
--- a/activerecord/lib/active_record/railties/subscriber.rb
+++ b/activerecord/lib/active_record/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActiveRecord
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
def sql(event)
name = '%s (%.1fms)' % [event.payload[:name], event.duration]
sql = event.payload[:sql].squeeze(' ')
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 7bc3d3bf33..aca4629dd8 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -21,6 +21,10 @@ module ActiveRecord
with_create_scope { @klass.new(*args, &block) }
end
+ def initialize_copy(other)
+ reset
+ end
+
alias build new
def create(*args, &block)
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 65e5c0495c..7e83eccbb5 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -20,7 +20,7 @@ module ActiveRecord
table = Arel::Table.new(table_name, :engine => @engine)
end
- attribute = table[column] || Arel::Attribute.new(table, column.to_sym)
+ attribute = table[column]
case value
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 0266700f66..e00d9cdf27 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -8,7 +8,7 @@ module ActiveRecord
class_eval <<-CEVAL
def #{query_method}(*args)
- new_relation = spawn
+ new_relation = clone
value = Array.wrap(args.flatten).reject {|x| x.blank? }
new_relation.#{query_method}_values += value if value.present?
new_relation
@@ -19,7 +19,7 @@ module ActiveRecord
[:where, :having].each do |query_method|
class_eval <<-CEVAL
def #{query_method}(*args)
- new_relation = spawn
+ new_relation = clone
value = build_where(*args)
new_relation.#{query_method}_values += [*value] if value.present?
new_relation
@@ -32,7 +32,7 @@ module ActiveRecord
class_eval <<-CEVAL
def #{query_method}(value = true)
- new_relation = spawn
+ new_relation = clone
new_relation.#{query_method}_value = value
new_relation
end
@@ -41,12 +41,12 @@ module ActiveRecord
end
def lock(locks = true)
- relation = spawn
+ relation = clone
case locks
when String, TrueClass, NilClass
- spawn.tap {|new_relation| new_relation.lock_value = locks || true }
+ clone.tap {|new_relation| new_relation.lock_value = locks || true }
else
- spawn.tap {|new_relation| new_relation.lock_value = false }
+ clone.tap {|new_relation| new_relation.lock_value = false }
end
end
@@ -174,7 +174,7 @@ module ActiveRecord
arel = arel.lock
when String
arel = arel.lock(@lock_value)
- end
+ end if defined?(@lock_value)
arel
end
@@ -184,16 +184,16 @@ module ActiveRecord
builder = PredicateBuilder.new(table.engine)
- conditions = if [String, Array].include?(args.first.class)
- @klass.send(:sanitize_sql, args.size > 1 ? args : args.first)
- elsif args.first.is_a?(Hash)
- attributes = @klass.send(:expand_hash_conditions_for_aggregates, args.first)
+ opts = args.first
+ case opts
+ when String, Array
+ @klass.send(:sanitize_sql, args.size > 1 ? args : opts)
+ when Hash
+ attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
builder.build_from_hash(attributes, table)
else
- args.first
+ opts
end
-
- conditions
end
private
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index cccf413e67..a18380f01c 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -1,11 +1,7 @@
module ActiveRecord
module SpawnMethods
- def spawn
- clone.reset
- end
-
def merge(r)
- merged_relation = spawn
+ merged_relation = clone
return merged_relation unless r
merged_relation = merged_relation.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values)
@@ -83,7 +79,7 @@ module ActiveRecord
:order, :select, :readonly, :group, :having, :from, :lock ]
def apply_finder_options(options)
- relation = spawn
+ relation = clone
return relation unless options
options.assert_valid_keys(VALID_FIND_OPTIONS)
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index 286ecd0289..eaf5dc6545 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -2,8 +2,9 @@ module ActiveRecord
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 4c1589d965..665c387d5d 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -27,6 +27,16 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" }
assert_equal 7, ActiveRecord::Migrator::current_version
end
+
+ def test_schema_raises_an_error_for_invalid_column_ntype
+ assert_raise NoMethodError do
+ ActiveRecord::Schema.define(:version => 8) do
+ create_table :vegetables do |t|
+ t.unknown :color
+ end
+ end
+ end
+ end
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 2a77eed1b5..41a23d7f61 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -18,7 +18,8 @@ require 'models/essay'
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
- :developers_projects, :computers, :authors, :posts, :tags, :taggings, :comments
+ :developers_projects, :computers, :authors, :author_addresses,
+ :posts, :tags, :taggings, :comments
def test_belongs_to
Client.find(3).firm.name
@@ -346,14 +347,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_client).readonly_firm.save! }
assert companies(:first_client).readonly_firm.readonly?
end
-
+
def test_polymorphic_assignment_foreign_type_field_updating
# should update when assigning a saved record
sponsor = Sponsor.new
member = Member.create
sponsor.sponsorable = member
assert_equal "Member", sponsor.sponsorable_type
-
+
# should update when assigning a new record
sponsor = Sponsor.new
member = Member.new
@@ -374,15 +375,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
essay.writer = writer
assert_equal "Author", essay.writer_type
end
-
+
def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records
sponsor = Sponsor.new
saved_member = Member.create
new_member = Member.new
-
+
sponsor.sponsorable = saved_member
assert_equal saved_member.id, sponsor.sponsorable_id
-
+
sponsor.sponsorable = new_member
assert_equal nil, sponsor.sponsorable_id
end
@@ -424,4 +425,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Account.find(@account.id, :include => :firm).save!
end
end
+
+ def test_dependent_delete_and_destroy_with_belongs_to
+ author_address = author_addresses(:david_address)
+ author_address_extra = author_addresses(:david_address_extra)
+ assert_equal [], AuthorAddress.destroyed_author_address_ids
+
+ assert_difference "AuthorAddress.count", -2 do
+ authors(:david).destroy
+ end
+
+ assert_equal [], AuthorAddress.find_all_by_id([author_address.id, author_address_extra.id])
+ assert_equal [author_address.id], AuthorAddress.destroyed_author_address_ids
+ end
+
+ def test_invalid_belongs_to_dependent_option_raises_exception
+ assert_raise ArgumentError do
+ Author.belongs_to :special_author_address, :dependent => :nullify
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ce7eedbb54..54624e79ce 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -14,7 +14,7 @@ require 'models/tagging'
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
- :developers_projects, :topics, :authors, :comments, :author_addresses,
+ :developers_projects, :topics, :authors, :comments,
:people, :posts, :readers, :taggings
def setup
@@ -684,24 +684,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 'Microsoft', another_ms_client.name
end
- def test_dependent_delete_and_destroy_with_belongs_to
- author_address = author_addresses(:david_address)
- assert_equal [], AuthorAddress.destroyed_author_address_ids[authors(:david).id]
-
- assert_difference "AuthorAddress.count", -2 do
- authors(:david).destroy
- end
-
- assert_equal nil, AuthorAddress.find_by_id(authors(:david).author_address_id)
- assert_equal nil, AuthorAddress.find_by_id(authors(:david).author_address_extra_id)
- end
-
- def test_invalid_belongs_to_dependent_option_raises_exception
- assert_raise ArgumentError do
- Author.belongs_to :special_author_address, :dependent => :nullify
- end
- end
-
def test_clearing_without_initial_access
firm = companies(:first_firm)
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index d359ad48c5..d5dbb88886 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -14,7 +14,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal companies(:first_firm).account, Account.find(1)
assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
end
-
+
def test_has_one_cache_nils
firm = companies(:another_firm)
assert_queries(1) { assert_nil firm.account }
@@ -96,7 +96,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nil Account.find(old_account_id).firm_id
end
- def test_association_changecalls_delete
+ def test_association_change_calls_delete
companies(:first_firm).deletable_account = Account.new
assert_equal [], Account.destroyed_account_ids[companies(:first_firm).id]
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 1441b4278d..e3047fe873 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1330,11 +1330,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_destroyed_returns_boolean
- developer = Developer.new
- assert_equal developer.destroyed?, false
- developer.destroy
- assert_equal developer.destroyed?, true
-
developer = Developer.first
assert_equal developer.destroyed?, false
developer.destroy
@@ -1346,6 +1341,23 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal developer.destroyed?, true
end
+ def test_persisted_returns_boolean
+ developer = Developer.new(:name => "Jose")
+ assert_equal developer.persisted?, false
+ developer.save!
+ assert_equal developer.persisted?, true
+
+ developer = Developer.first
+ assert_equal developer.persisted?, true
+ developer.destroy
+ assert_equal developer.persisted?, false
+
+ developer = Developer.last
+ assert_equal developer.persisted?, true
+ developer.delete
+ assert_equal developer.persisted?, false
+ end
+
def test_clone
topic = Topic.find(1)
cloned_topic = nil
@@ -1711,6 +1723,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal t1.title, t2.title
end
+ def test_reload_with_exclusive_scope
+ dev = DeveloperCalledDavid.first
+ dev.update_attributes!( :name => "NotDavid" )
+ assert_equal dev, dev.reload
+ end
+
def test_define_attr_method_with_value
k = Class.new( ActiveRecord::Base )
k.send(:define_attr_method, :table_name, "foo")
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index e6d56a7193..28a1ae5feb 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -38,12 +38,12 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_get_maximum_of_field_with_include
- assert_equal 50, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
+ assert_equal 55, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
end
def test_should_get_maximum_of_field_with_scoped_include
Account.send :with_scope, :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
- assert_equal 50, Account.maximum(:credit_limit)
+ assert_equal 55, Account.maximum(:credit_limit)
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 9e8bfbbee8..e831ebf36c 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -1,11 +1,15 @@
require File.expand_path('../../../../load_paths', __FILE__)
+lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
require 'config'
require 'test/unit'
require 'stringio'
require 'active_record'
+require 'active_support/dependencies'
require 'connection'
begin
diff --git a/activerecord/test/cases/subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 5328d4468b..f3b94eb829 100644
--- a/activerecord/test/cases/subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -1,15 +1,16 @@
require "cases/helper"
require "models/developer"
-require "rails/subscriber/test_helper"
-require "active_record/railties/subscriber"
+require "rails/log_subscriber/test_helper"
+require "active_record/railties/log_subscriber"
-class SubscriberTest < ActiveSupport::TestCase
- include Rails::Subscriber::TestHelper
- Rails::Subscriber.add(:active_record, ActiveRecord::Railties::Subscriber.new)
+class LogSubscriberTest < ActiveSupport::TestCase
+ include Rails::LogSubscriber::TestHelper
def setup
@old_logger = ActiveRecord::Base.logger
super
+
+ Rails::LogSubscriber.add(:active_record, ActiveRecord::Railties::LogSubscriber.new)
end
def teardown
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 1081aa40a9..3151457440 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -663,6 +663,16 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal 2, posts.count
assert_equal posts(:thinking), posts.first
end
+
+ def test_create_attribute_overwrites_default_scoping
+ assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name
+ assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
+ end
+
+ def test_create_attribute_overwrites_default_values
+ assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary
+ assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary
+ end
end
=begin
diff --git a/activerecord/test/cases/pk_test.rb b/activerecord/test/cases/pk_test.rb
index c121e0aa0f..5bba1d5625 100644
--- a/activerecord/test/cases/pk_test.rb
+++ b/activerecord/test/cases/pk_test.rb
@@ -9,6 +9,20 @@ require 'models/mixed_case_monkey'
class PrimaryKeysTest < ActiveRecord::TestCase
fixtures :topics, :subscribers, :movies, :mixed_case_monkeys
+ def test_to_key_with_default_primary_key
+ topic = Topic.new
+ assert topic.to_key.nil?
+ topic = Topic.find(1)
+ assert_equal topic.to_key, [1]
+ end
+
+ def test_to_key_with_customized_primary_key
+ keyboard = Keyboard.new
+ assert keyboard.to_key.nil?
+ keyboard.save
+ assert_equal keyboard.to_key, [keyboard.id]
+ end
+
def test_integer_key
topic = Topic.find(1)
assert_equal(topics(:first).author_name, topic.author_name)
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 7cbc6e803f..025f6207f8 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -35,7 +35,7 @@ class Author < ActiveRecord::Base
has_many :ordered_uniq_comments, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id'
has_many :ordered_uniq_comments_desc, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id DESC'
has_many :readonly_comments, :through => :posts, :source => :comments, :readonly => true
-
+
has_many :special_posts
has_many :special_post_comments, :through => :special_posts, :source => :comments
@@ -130,14 +130,11 @@ class AuthorAddress < ActiveRecord::Base
has_one :author
def self.destroyed_author_address_ids
- @destroyed_author_address_ids ||= Hash.new { |h,k| h[k] = [] }
+ @destroyed_author_address_ids ||= []
end
before_destroy do |author_address|
- if author_address.author
- AuthorAddress.destroyed_author_address_ids[author_address.author.id] << author_address.id
- end
- true
+ AuthorAddress.destroyed_author_address_ids << author_address.id
end
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index df5fd10b6b..f31d5f87e5 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -82,7 +82,7 @@ class Firm < Company
has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account", :order => "id"
has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account"
has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
-
+
has_one :account_limit_500_with_hash_conditions, :foreign_key => "firm_id", :class_name => "Account", :conditions => { :credit_limit => 500 }
has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
@@ -155,7 +155,7 @@ class VerySpecialClient < SpecialClient
end
class Account < ActiveRecord::Base
- belongs_to :firm
+ belongs_to :firm, :class_name => 'Company'
belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false
def self.destroyed_account_ids
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index e7a1e110d7..e35de3b9b0 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -99,3 +99,8 @@ class DeveloperCalledJamis < ActiveRecord::Base
self.table_name = 'developers'
default_scope :conditions => { :name => 'Jamis' }
end
+
+class PoorDeveloperCalledJamis < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope :conditions => { :name => 'Jamis', :salary => 50000 }
+end \ No newline at end of file
diff --git a/activeresource/Rakefile b/activeresource/Rakefile
index 829752516f..175b379699 100644
--- a/activeresource/Rakefile
+++ b/activeresource/Rakefile
@@ -5,22 +5,6 @@ require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version')
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'activeresource'
-PKG_VERSION = ActiveResource::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "activerecord"
-RUBY_FORGE_USER = "webster132"
-
-PKG_FILES = FileList[
- "lib/**/*", "test/**/*", "[A-Z]*", "Rakefile"
-].exclude(/\bCVS\b|~$/)
-
desc "Default Task"
task :default => [ :test ]
diff --git a/activeresource/activeresource.gemspec b/activeresource/activeresource.gemspec
index 1722ebeb8e..3718bb4f2e 100644
--- a/activeresource/activeresource.gemspec
+++ b/activeresource/activeresource.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'activeresource'
- s.version = '3.0.0.beta1'
- s.summary = 'REST-model framework (part of Rails).'
- s.description = 'REST-model framework (part of Rails).'
+ s.version = version
+ s.summary = 'REST modeling framework (part of Rails).'
+ s.description = 'REST on Rails. Wrap your RESTful web app with Ruby classes and work with them like Active Record models.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'David Heinemeier Hansson'
@@ -18,6 +20,6 @@ Gem::Specification.new do |s|
s.extra_rdoc_files = %w( README )
s.rdoc_options.concat ['--main', 'README']
- s.add_dependency('activesupport', '= 3.0.0.beta1')
- s.add_dependency('activemodel', '= 3.0.0.beta1')
+ s.add_dependency('activesupport', version)
+ s.add_dependency('activemodel', version)
end
diff --git a/activeresource/lib/active_resource/railtie.rb b/activeresource/lib/active_resource/railtie.rb
index 7e35fdc0eb..27c88415f6 100644
--- a/activeresource/lib/active_resource/railtie.rb
+++ b/activeresource/lib/active_resource/railtie.rb
@@ -5,8 +5,8 @@ module ActiveResource
class Railtie < Rails::Railtie
railtie_name :active_resource
- require "active_resource/railties/subscriber"
- subscriber ActiveResource::Railties::Subscriber.new
+ require "active_resource/railties/log_subscriber"
+ log_subscriber ActiveResource::Railties::LogSubscriber.new
initializer "active_resource.set_configs" do |app|
app.config.active_resource.each do |k,v|
diff --git a/activeresource/lib/active_resource/railties/subscriber.rb b/activeresource/lib/active_resource/railties/log_subscriber.rb
index fb98061b71..86806a93d0 100644
--- a/activeresource/lib/active_resource/railties/subscriber.rb
+++ b/activeresource/lib/active_resource/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActiveResource
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
def request(event)
result = event.payload[:result]
info "#{event.payload[:method].to_s.upcase} #{event.payload[:request_uri]}"
diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb
index 461fef5283..bb60954e05 100644
--- a/activeresource/lib/active_resource/version.rb
+++ b/activeresource/lib/active_resource/version.rb
@@ -2,8 +2,9 @@ module ActiveResource
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb
index 1af535e811..fcb770d612 100644
--- a/activeresource/test/abstract_unit.rb
+++ b/activeresource/test/abstract_unit.rb
@@ -1,12 +1,14 @@
require File.expand_path('../../../load_paths', __FILE__)
+lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
require 'rubygems'
require 'test/unit'
require 'active_resource'
require 'active_support'
require 'active_support/test_case'
-$:.unshift "#{File.dirname(__FILE__)}/../test"
require 'setter_trap'
require 'logger'
diff --git a/activeresource/test/cases/subscriber_test.rb b/activeresource/test/cases/log_subscriber_test.rb
index fb890e86cb..f0330e8f51 100644
--- a/activeresource/test/cases/subscriber_test.rb
+++ b/activeresource/test/cases/log_subscriber_test.rb
@@ -1,20 +1,22 @@
require "abstract_unit"
require "fixtures/person"
-require "rails/subscriber/test_helper"
-require "active_resource/railties/subscriber"
+require "rails/log_subscriber/test_helper"
+require "active_resource/railties/log_subscriber"
require "active_support/core_ext/hash/conversions"
-class SubscriberTest < ActiveSupport::TestCase
- include Rails::Subscriber::TestHelper
- Rails::Subscriber.add(:active_resource, ActiveResource::Railties::Subscriber.new)
+# TODO: This test should be part of Railties
+class LogSubscriberTest < ActiveSupport::TestCase
+ include Rails::LogSubscriber::TestHelper
def setup
+ super
+
@matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/1.xml", {}, @matz
end
- super
+ Rails::LogSubscriber.add(:active_resource, ActiveResource::Railties::LogSubscriber.new)
end
def set_logger(logger)
@@ -26,6 +28,6 @@ class SubscriberTest < ActiveSupport::TestCase
wait
assert_equal 2, @logger.logged(:info).size
assert_equal "GET http://37s.sunrise.i:3000/people/1.xml", @logger.logged(:info)[0]
- assert_match /\-\-\> 200 200 106/, @logger.logged(:info)[1]
+ assert_match(/\-\-\> 200 200 106/, @logger.logged(:info)[1])
end
-end \ No newline at end of file
+end
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 8ec903e376..56c81cf63b 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.0 (pending)*
+* Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. [Jeremy Kemper]
+
* JSON backend for YAJL. Preferred if available. #2666 [Brian Lopez]
diff --git a/activesupport/Rakefile b/activesupport/Rakefile
index 357bdca715..43f4722dbc 100644
--- a/activesupport/Rakefile
+++ b/activesupport/Rakefile
@@ -2,18 +2,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/gempackagetask'
-require File.join(File.dirname(__FILE__), 'lib', 'active_support', 'version')
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'activesupport'
-PKG_VERSION = ActiveSupport::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "activesupport"
-RUBY_FORGE_USER = "webster132"
-
task :default => :test
Rake::TestTask.new do |t|
t.libs << 'test'
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index d0557230aa..78fb48924e 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'activesupport'
- s.version = '3.0.0.beta1'
- s.summary = 'Support and utility classes used by the Rails framework.'
- s.description = 'Support and utility classes used by the Rails framework.'
+ s.version = version
+ s.summary = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.'
+ s.description = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. Rich support for multibyte strings, internationalization, time zones, and testing.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'David Heinemeier Hansson'
@@ -16,7 +18,7 @@ Gem::Specification.new do |s|
s.has_rdoc = true
- s.add_dependency('i18n', '~> 0.3.0')
+ s.add_dependency('i18n', '~> 0.3.6.pre')
s.add_dependency('tzinfo', '~> 0.3.16')
s.add_dependency('builder', '~> 2.1.2')
s.add_dependency('memcache-client', '~> 1.7.5')
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index ae31d191c0..e34e46b4cf 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -53,6 +53,7 @@ module ActiveSupport
autoload :Deprecation
autoload :Gzip
autoload :Inflector
+ autoload :JSON
autoload :Memoizable
autoload :MessageEncryptor
autoload :MessageVerifier
@@ -70,3 +71,5 @@ module ActiveSupport
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
autoload :TestCase
end
+
+autoload :I18n, "active_support/i18n"
diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb
index 64600575d9..f537818300 100644
--- a/activesupport/lib/active_support/all.rb
+++ b/activesupport/lib/active_support/all.rb
@@ -1,4 +1,3 @@
require 'active_support'
-require 'active_support/i18n'
require 'active_support/time'
require 'active_support/core_ext'
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 6727eda811..b230bb8a40 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,7 +1,7 @@
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/inheritable_attributes'
require 'active_support/core_ext/kernel/reporting'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
module ActiveSupport
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
@@ -312,7 +312,7 @@ module ActiveSupport
def _normalize_legacy_filter(kind, filter)
if !filter.respond_to?(kind) && filter.respond_to?(:filter)
- filter.metaclass.class_eval(
+ filter.singleton_class.class_eval(
"def #{kind}(context, &block) filter(context, &block) end",
__FILE__, __LINE__ - 1)
elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 814567a5a6..2119322bfe 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/inflector'
-require 'active_support/i18n'
class Array
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index d74219cb93..c18905b369 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'active_support/core_ext/module/delegation'
class Class
@@ -24,13 +24,35 @@ class Class
# For convenience, a query method is defined as well:
#
# Subclass.setting? # => false
+ #
+ # Instances may overwrite the class value in the same way:
+ #
+ # Base.setting = true
+ # object = Base.new
+ # object.setting # => true
+ # object.setting = false
+ # object.setting # => false
+ # Base.setting # => true
+ #
+ # To opt out of the instance writer method, pass :instance_writer => false.
+ #
+ # object.setting = false # => NoMethodError
def class_attribute(*attrs)
+ instance_writer = !attrs.last.is_a?(Hash) || attrs.pop[:instance_writer]
+
+ s = singleton_class
attrs.each do |attr|
- metaclass.send(:define_method, attr) { }
- metaclass.send(:define_method, "#{attr}?") { !!send(attr) }
- metaclass.send(:define_method, "#{attr}=") do |value|
- metaclass.send(:define_method, attr) { value }
+ s.send(:define_method, attr) { }
+ s.send(:define_method, :"#{attr}?") { !!send(attr) }
+ s.send(:define_method, :"#{attr}=") do |value|
+ singleton_class.send(:define_method, attr) { value }
end
+
+ define_method(attr) { self.class.send(attr) }
+ define_method(:"#{attr}?") { !!send(attr) }
+ define_method(:"#{attr}=") do |value|
+ singleton_class.send(:define_method, attr) { value }
+ end if instance_writer
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
index 19382abb76..b5785bdcd3 100644
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
@@ -1,6 +1,6 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/array/extract_options'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
class Class
def superclass_delegating_accessor(name, options = {})
@@ -11,9 +11,9 @@ class Class
# Generate the public methods name, name=, and name?
# These methods dispatch to the private _name, and _name= methods, making them
# overridable
- metaclass.send(:define_method, name) { send("_#{name}") }
- metaclass.send(:define_method, "#{name}?") { !!send("_#{name}") }
- metaclass.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
+ singleton_class.send(:define_method, name) { send("_#{name}") }
+ singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
+ singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
# If an instance_reader is needed, generate methods for name and name= on the
# class itself, so instances will be able to see them
@@ -27,12 +27,12 @@ private
# inheritance behavior, without having to store the object in an instance
# variable and look up the superclass chain manually.
def _stash_object_in_method(object, method, instance_reader = true)
- metaclass.send(:define_method, method) { object }
+ singleton_class.send(:define_method, method) { object }
define_method(method) { object } if instance_reader
end
def _superclass_delegating_accessor(name, options = {})
- metaclass.send(:define_method, "#{name}=") do |value|
+ singleton_class.send(:define_method, "#{name}=") do |value|
_stash_object_in_method(value, name, options[:instance_reader] != false)
end
self.send("#{name}=", nil)
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index 49d28e8a34..26b73ed442 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -14,6 +14,7 @@ class File
# end
def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
require 'tempfile' unless defined?(Tempfile)
+ require 'fileutils' unless defined?(FileUtils)
temp_file = Tempfile.new(basename(file_name), temp_dir)
yield temp_file
@@ -31,7 +32,7 @@ class File
end
# Overwrite original file with temp file
- rename(temp_file.path, file_name)
+ FileUtils.mv(temp_file.path, file_name)
# Set correct permissions on new file
chown(old_stat.uid, old_stat.gid, file_name)
diff --git a/activesupport/lib/active_support/core_ext/file/path.rb b/activesupport/lib/active_support/core_ext/file/path.rb
new file mode 100644
index 0000000000..b5feab80ae
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/file/path.rb
@@ -0,0 +1,5 @@
+class File
+ unless File.allocate.respond_to?(:to_path)
+ alias to_path path
+ end
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index 045a6944fa..e4d429fc2b 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -22,7 +22,7 @@ class Hash
# to +to_sym+.
def symbolize_keys!
keys.each do |key|
- self[(key.to_sym rescue key) || key] = delete(key)
+ self[(key.to_sym rescue key)] = delete(key) if key.respond_to?(:to_sym) && !key.is_a?(Fixnum)
end
self
end
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index db2dac1472..4f86d9d605 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -5,7 +5,7 @@ require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'active_support/core_ext/object/misc'
require 'active_support/core_ext/object/extending'
diff --git a/activesupport/lib/active_support/core_ext/object/metaclass.rb b/activesupport/lib/active_support/core_ext/object/metaclass.rb
deleted file mode 100644
index 93fb0ad594..0000000000
--- a/activesupport/lib/active_support/core_ext/object/metaclass.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class Object
- # Get object's meta (ghost, eigenclass, singleton) class
- def metaclass
- class << self
- self
- end
- end
-
- # If class_eval is called on an object, add those methods to its metaclass
- def class_eval(*args, &block)
- metaclass.class_eval(*args, &block)
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/object/singleton_class.rb b/activesupport/lib/active_support/core_ext/object/singleton_class.rb
new file mode 100644
index 0000000000..8dee54e71b
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/singleton_class.rb
@@ -0,0 +1,13 @@
+class Object
+ # Returns the object's singleton class.
+ def singleton_class
+ class << self
+ self
+ end
+ end unless respond_to?(:singleton_class)
+
+ # class_eval on an object acts like singleton_class_eval.
+ def class_eval(*args, &block)
+ singleton_class.class_eval(*args, &block)
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/proc.rb b/activesupport/lib/active_support/core_ext/proc.rb
index d50076a01e..71b413a88a 100644
--- a/activesupport/lib/active_support/core_ext/proc.rb
+++ b/activesupport/lib/active_support/core_ext/proc.rb
@@ -5,9 +5,9 @@ class Proc #:nodoc:
block, time = self, Time.now
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
- define_method(method_name, &block) # define_method("__bind_1230458026_720454", &block)
- method = instance_method(method_name) # method = instance_method("__bind_1230458026_720454")
- remove_method(method_name) # remove_method("__bind_1230458026_720454")
+ define_method(method_name, &block)
+ method = instance_method(method_name)
+ remove_method(method_name)
method
end.bind(object)
end
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 331416b3a9..52946f9037 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -1,4 +1,5 @@
require 'date'
+require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/core_ext/time/calculations'
class String
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index ea4ed61e42..fbb7b79fc6 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -1,8 +1,10 @@
+require 'active_support/inflector'
+
# String inflections define new methods on the String class to transform names for different purposes.
# For instance, you can figure out the name of a database from the name of a class.
#
# "ScaleScore".tableize # => "scale_scores"
-
+#
class String
# Returns the plural form of the word in the string.
#
diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb
index 06d3505c60..932117cc10 100644
--- a/activesupport/lib/active_support/core_ext/string/interpolation.rb
+++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb
@@ -1,91 +1 @@
-=begin
- heavily based on Masao Mutoh's gettext String interpolation extension
- http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
- Copyright (C) 2005-2010 Masao Mutoh
- You may redistribute it and/or modify it under the same license terms as Ruby.
-=end
-
-if RUBY_VERSION < '1.9' && !"".respond_to?(:interpolate_without_ruby_19_syntax)
-
- # KeyError is raised by String#% when the string contains a named placeholder
- # that is not contained in the given arguments hash. Ruby 1.9 includes and
- # raises this exception natively. We define it to mimic Ruby 1.9's behaviour
- # in Ruby 1.8.x
-
- class KeyError < IndexError
- def initialize(message = nil)
- super(message || "key not found")
- end
- end unless defined?(KeyError)
-
- # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
- #
- # String#% method which accept "named argument". The translator can know
- # the meaning of the msgids using "named argument" instead of %s/%d style.
-
- class String
- alias :interpolate_without_ruby_19_syntax :% # :nodoc:
-
- INTERPOLATION_PATTERN = Regexp.union(
- /%%/,
- /%\{(\w+)\}/, # matches placeholders like "%{foo}"
- /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
- )
-
- # % uses self (i.e. the String) as a format specification and returns the
- # result of applying it to the given arguments. In other words it interpolates
- # the given arguments to the string according to the formats the string
- # defines.
- #
- # There are three ways to use it:
- #
- # * Using a single argument or Array of arguments.
- #
- # This is the default behaviour of the String class. See Kernel#sprintf for
- # more details about the format string.
- #
- # Example:
- #
- # "%d %s" % [1, "message"]
- # # => "1 message"
- #
- # * Using a Hash as an argument and unformatted, named placeholders.
- #
- # When you pass a Hash as an argument and specify placeholders with %{foo}
- # it will interpret the hash values as named arguments.
- #
- # Example:
- #
- # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
- # # => "Masao Mutoh"
- #
- # * Using a Hash as an argument and formatted, named placeholders.
- #
- # When you pass a Hash as an argument and specify placeholders with %<foo>d
- # it will interpret the hash values as named arguments and format the value
- # according to the formatting instruction appended to the closing >.
- #
- # Example:
- #
- # "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
- # # => "10, 43.3"
- def %(args)
- if args.kind_of?(Hash)
- dup.gsub(INTERPOLATION_PATTERN) do |match|
- if match == '%%'
- '%'
- else
- key = ($1 || $2).to_sym
- raise KeyError unless args.has_key?(key)
- $3 ? sprintf("%#{$3}", args[key]) : args[key]
- end
- end
- elsif self =~ INTERPOLATION_PATTERN
- raise ArgumentError.new('one hash required')
- else
- result = gsub(/%([{<])/, '%%\1')
- result.send :'interpolate_without_ruby_19_syntax', args
- end
- end
- end
-end
+require 'i18n/core_ext/string/interpolate'
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 567ba00b0d..af46ae10d6 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -88,6 +88,14 @@ module ActiveSupport #:nodoc:
def to_s
self
end
+
+ def as_str
+ ''.replace(self)
+ end
+
+ def to_yaml(*args)
+ as_str.to_yaml(*args)
+ end
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 56de29b730..9c4412c28c 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -76,17 +76,19 @@ module ActiveSupport #:nodoc:
locked :concat, :each, :delete_if, :<<
def new_constants_for(frames)
- frames.map do |mod_name, prior_constants|
- mod = Inflector.constantize(mod_name)
+ constants = []
+ frames.each do |mod_name, prior_constants|
+ mod = Inflector.constantize(mod_name) if Dependencies.qualified_const_defined?(mod_name)
next unless mod.is_a?(Module)
new_constants = mod.local_constant_names - prior_constants
get(mod_name).concat(new_constants)
- new_constants.map do |suffix|
- ([mod_name, suffix] - ["Object"]).join("::")
+ new_constants.each do |suffix|
+ constants << ([mod_name, suffix] - ["Object"]).join("::")
end
- end.flatten
+ end
+ constants
end
# Add a set of modules to the watch stack, remembering the initial constants
@@ -115,8 +117,8 @@ module ActiveSupport #:nodoc:
def self.append_features(base)
base.class_eval do
# Emulate #exclude via an ivar
- return if @_const_missing
- @_const_missing = method(:const_missing)
+ return if defined?(@_const_missing) && @_const_missing
+ @_const_missing = instance_method(:const_missing)
remove_method(:const_missing)
end
super
@@ -177,6 +179,10 @@ module ActiveSupport #:nodoc:
end
def require_dependency(file_name, message = "No such file to load -- %s")
+ unless file_name.is_a?(String)
+ raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
+ end
+
Dependencies.depend_on(file_name, false, message)
end
@@ -435,7 +441,10 @@ module ActiveSupport #:nodoc:
qualified_name = qualified_name_for from_mod, const_name
path_suffix = qualified_name.underscore
+
+ trace = caller.reject {|l| l =~ %r{#{Regexp.escape(__FILE__)}}}
name_error = NameError.new("uninitialized constant #{qualified_name}")
+ name_error.set_backtrace(trace)
file_path = search_for_file(path_suffix)
diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb
index 44edb89ad5..f669f4a77e 100644
--- a/activesupport/lib/active_support/dependencies/autoload.rb
+++ b/activesupport/lib/active_support/dependencies/autoload.rb
@@ -1,7 +1,12 @@
require "active_support/inflector/methods"
+require "active_support/lazy_load_hooks"
module ActiveSupport
module Autoload
+ def self.extended(base)
+ base.extend(LazyLoadHooks)
+ end
+
@@autoloads = {}
@@under_path = nil
@@at_path = nil
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index cec8024b17..d0d8b577b3 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -12,15 +12,15 @@ module ActiveSupport
method_names.each do |method_name|
target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
target_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1)
- def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block)
- ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn(
- ::ActiveSupport::Deprecation.deprecated_method_warning( # ::ActiveSupport::Deprecation.deprecated_method_warning(
- :#{method_name}, # :generate_secret,
- #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"),
- caller # caller
- ) # )
- send(:#{target}_without_deprecation#{punctuation}, *args, &block) # send(:generate_secret_without_deprecation, *args, &block)
- end # end
+ def #{target}_with_deprecation#{punctuation}(*args, &block)
+ ::ActiveSupport::Deprecation.warn(
+ ::ActiveSupport::Deprecation.deprecated_method_warning(
+ :#{method_name},
+ #{options[method_name].inspect}),
+ caller
+ )
+ send(:#{target}_without_deprecation#{punctuation}, *args, &block)
+ end
end_eval
end
end
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index fcb05ad8d9..03c445ffbf 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -29,7 +29,8 @@ module ActiveSupport
private
def deprecation_message(callstack, message = nil)
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
- "DEPRECATION WARNING: #{message}. #{deprecation_caller_message(callstack)}"
+ message += '.' unless message =~ /\.$/
+ "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
end
def deprecation_caller_message(callstack)
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index 854c60eec1..034d7d8ddc 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -1,2 +1,3 @@
require 'i18n'
-I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" \ No newline at end of file
+I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
+ActiveSupport.run_base_hooks(:i18n) \ No newline at end of file
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 41277893e3..c7df62e915 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -109,7 +109,7 @@ module ActiveSupport
constant = Object
names.each do |name|
- constant = constant.const_get(name, false) || constant.const_missing(name)
+ constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
end
constant
end
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 30a9072ee1..236f2eb628 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -47,7 +47,7 @@ module ActiveSupport
# replace accented chars with their ascii equivalents
parameterized_string = transliterate(string)
# Turn unwanted chars into the separator
- parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep)
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
unless sep.nil? || sep.empty?
re_sep = Regexp.escape(sep)
# No more than one of the separator in a row.
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index e357b6837a..04ff316a44 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -7,7 +7,7 @@ module ActiveSupport
module JSON
# Listed in order of preference.
- DECODERS = %w(Yajl JSONGem Yaml)
+ DECODERS = %w(Yajl Yaml)
class << self
attr_reader :parse_error
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
new file mode 100644
index 0000000000..36acfda524
--- /dev/null
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -0,0 +1,25 @@
+module ActiveSupport
+ module LazyLoadHooks
+ def _setup_base_hooks
+ @base_hooks ||= Hash.new {|h,k| h[k] = [] }
+ @base ||= {}
+ end
+
+ def base_hook(name = nil, &block)
+ _setup_base_hooks
+
+ if base = @base[name]
+ base.instance_eval(&block)
+ else
+ @base_hooks[name] << block
+ end
+ end
+
+ def run_base_hooks(base, name = nil)
+ _setup_base_hooks
+
+ @base_hooks[name].each { |hook| base.instance_eval(&hook) } if @base_hooks
+ @base[name] = base
+ end
+ end
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
index f810f53029..ca1cfedae3 100644
--- a/activesupport/lib/active_support/memoizable.rb
+++ b/activesupport/lib/active_support/memoizable.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'active_support/core_ext/module/aliasing'
module ActiveSupport
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 3e96decb8c..3f1fe64e9b 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -9,7 +9,7 @@ module ActiveSupport
# end
#
# You can consume those events and the information they provide by registering
- # a subscriber. For instance, let's store all instrumented events in an array:
+ # a log subscriber. For instance, let's store all instrumented events in an array:
#
# @events = []
#
@@ -35,7 +35,7 @@ module ActiveSupport
# end
#
# Notifications ships with a queue implementation that consumes and publish events
- # to subscribers in a thread. You can use any queue implementation you want.
+ # to log subscribers in a thread. You can use any queue implementation you want.
#
module Notifications
autoload :Instrumenter, 'active_support/notifications/instrumenter'
@@ -44,7 +44,7 @@ module ActiveSupport
class << self
attr_writer :notifier
- delegate :publish, :subscribe, :to => :notifier
+ delegate :publish, :subscribe, :unsubscribe, :to => :notifier
delegate :instrument, :to => :instrumenter
def notifier
@@ -69,6 +69,10 @@ module ActiveSupport
@queue.bind(pattern).subscribe(&block)
end
+ def unsubscribe(subscriber)
+ @queue.unsubscribe(subscriber)
+ end
+
def wait
@queue.wait
end
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 0ec23da073..cd60054862 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -1,10 +1,11 @@
module ActiveSupport
module Notifications
# This is a default queue implementation that ships with Notifications. It
- # just pushes events to all registered subscribers.
+ # just pushes events to all registered log subscribers.
class Fanout
def initialize
@subscribers = []
+ @listeners_for = {}
end
def bind(pattern)
@@ -12,11 +13,22 @@ module ActiveSupport
end
def subscribe(pattern = nil, &block)
+ @listeners_for.clear
@subscribers << Subscriber.new(pattern, &block)
+ @subscribers.last
end
- def publish(*args)
- @subscribers.each { |s| s.publish(*args) }
+ def unsubscribe(subscriber)
+ @subscribers.delete(subscriber)
+ @listeners_for.clear
+ end
+
+ def publish(name, *args)
+ if listeners = @listeners_for[name]
+ listeners.each { |s| s.publish(name, *args) }
+ else
+ @listeners_for[name] = @subscribers.select { |s| s.publish(name, *args) }
+ end
end
# This is a sync queue, so there is not waiting.
@@ -48,7 +60,9 @@ module ActiveSupport
end
def publish(*args)
- push(*args) if matches?(args.first)
+ return unless matches?(args.first)
+ push(*args)
+ true
end
def drained?
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index 596a7b757d..61ccb79211 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -18,4 +18,10 @@ module ActiveSupport #:nodoc:
end
end
end
+
+ class InheritableOptions < OrderedOptions
+ def initialize(parent)
+ super() { |h,k| parent[k] }
+ end
+ end
end
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index c8d8b85000..d2c13e030d 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -33,14 +33,16 @@ module I18n
railtie_name :i18n
# Initialize I18n load paths to an array
- config.i18n.engines_load_path = []
+ config.i18n.railties_load_path = []
config.i18n.load_path = []
initializer "i18n.initialize" do
- require 'active_support/i18n'
-
- ActionDispatch::Callbacks.to_prepare do
+ ActiveSupport.base_hook(:i18n) do
I18n.reload!
+
+ ActionDispatch::Callbacks.to_prepare do
+ I18n.reload!
+ end
end
end
@@ -49,7 +51,7 @@ module I18n
config.after_initialize do |app|
app.config.i18n.each do |setting, value|
case setting
- when :engines_load_path
+ when :railties_load_path
app.config.i18n.load_path.unshift(*value)
when :load_path
I18n.load_path += value
diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb
index 1e49ccdade..f0db5b3021 100644
--- a/activesupport/lib/active_support/ruby/shim.rb
+++ b/activesupport/lib/active_support/ruby/shim.rb
@@ -17,3 +17,4 @@ require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/string/interpolation'
require 'active_support/core_ext/rexml'
require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/file/path'
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 6c6187be29..831204693f 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -2,8 +2,9 @@ module ActiveSupport
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb
index 11b05efac1..91ddef2619 100644
--- a/activesupport/lib/active_support/whiny_nil.rb
+++ b/activesupport/lib/active_support/whiny_nil.rb
@@ -25,17 +25,16 @@
# By default it is on in development and test modes, and it is off in production
# mode.
class NilClass
- WHINERS = [::Array]
- WHINERS << ::ActiveRecord::Base if defined? ::ActiveRecord
-
METHOD_CLASS_MAP = Hash.new
- WHINERS.each do |klass|
+ def self.add_whiner(klass)
methods = klass.public_instance_methods - public_instance_methods
class_name = klass.name
methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
end
+ add_whiner ::Array
+
# Raises a RuntimeError when you attempt to call +id+ on +nil+.
def id
raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 33be6f65bf..da338a2a26 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -1,5 +1,8 @@
require File.expand_path('../../../load_paths', __FILE__)
+lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
require 'test/unit'
require 'mocha'
diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb
index 2e978f01be..e74c64ba8d 100644
--- a/activesupport/test/callback_inheritance_test.rb
+++ b/activesupport/test/callback_inheritance_test.rb
@@ -1,5 +1,4 @@
require 'test/unit'
-$:.unshift "#{File.dirname(__FILE__)}/../lib"
require 'active_support'
class GrandParent
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 11494e951e..3fb940ad3c 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -1,6 +1,5 @@
# require 'abstract_unit'
require 'test/unit'
-$:.unshift "#{File.dirname(__FILE__)}/../lib"
require 'active_support'
module CallbacksTest
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index d4cd5ddbde..b374eca370 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -306,7 +306,7 @@ class ArrayUniqByTests < Test::Unit::TestCase
def test_uniq_by
assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? }
assert_equal [1,2], [1,2,3,4].uniq_by(&:even?)
- assert_equal (-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 }
+ assert_equal((-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 })
end
def test_uniq_by!
@@ -320,7 +320,7 @@ class ArrayUniqByTests < Test::Unit::TestCase
a = (-5..5).to_a
a.uniq_by! { |i| i**2 }
- assert_equal (-5..0).to_a, a
+ assert_equal((-5..0).to_a, a)
end
end
diff --git a/activesupport/test/core_ext/class/attribute_test.rb b/activesupport/test/core_ext/class/attribute_test.rb
index ef84b9f255..06b4cf075f 100644
--- a/activesupport/test/core_ext/class/attribute_test.rb
+++ b/activesupport/test/core_ext/class/attribute_test.rb
@@ -2,13 +2,6 @@ require 'abstract_unit'
require 'active_support/core_ext/class/attribute'
class ClassAttributeTest < ActiveSupport::TestCase
- class Base
- class_attribute :setting
- end
-
- class Subclass < Base
- end
-
def setup
@klass = Class.new { class_attribute :setting }
@sub = Class.new(@klass)
@@ -40,8 +33,30 @@ class ClassAttributeTest < ActiveSupport::TestCase
assert_equal true, @klass.setting?
end
- test 'no instance delegates' do
- assert_raise(NoMethodError) { @klass.new.setting }
- assert_raise(NoMethodError) { @klass.new.setting? }
+ test 'instance reader delegates to class' do
+ assert_nil @klass.new.setting
+
+ @klass.setting = 1
+ assert_equal 1, @klass.new.setting
+ end
+
+ test 'instance override' do
+ object = @klass.new
+ object.setting = 1
+ assert_nil @klass.setting
+ @klass.setting = 2
+ assert_equal 1, object.setting
+ end
+
+ test 'instance query' do
+ object = @klass.new
+ assert_equal false, object.setting?
+ object.setting = 1
+ assert_equal true, object.setting?
+ end
+
+ test 'disabling instance writer' do
+ object = Class.new { class_attribute :setting, :instance_writer => false }.new
+ assert_raise(NoMethodError) { object.setting = 'boom' }
end
end
diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb
index 011068ab74..636edb8d4b 100644
--- a/activesupport/test/core_ext/class/delegating_attributes_test.rb
+++ b/activesupport/test/core_ext/class/delegating_attributes_test.rb
@@ -84,6 +84,8 @@ class DelegatingAttributesTest < Test::Unit::TestCase
assert_equal "1", Child.some_attribute
assert_nil Mokopuna.some_attribute
+ ensure
+ Child.some_attribute=nil
end
end
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index 26be694176..e1258b872e 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -57,6 +57,10 @@ class AtomicWriteTest < Test::Unit::TestCase
File.unlink(file_name) rescue nil
end
+ def test_responds_to_to_path
+ assert_equal __FILE__, File.open(__FILE__, "r").to_path
+ end
+
private
def file_name
"atomic.file"
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 1fe75d5930..9edd7cc7c0 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -55,18 +55,6 @@ class Name
end
end
-$nowhere = <<-EOF
-class Name
- delegate :nowhere
-end
-EOF
-
-$noplace = <<-EOF
-class Name
- delegate :noplace, :tos => :hollywood
-end
-EOF
-
class ModuleTest < Test::Unit::TestCase
def setup
@david = Someone.new("David", Somewhere.new("Paulina", "Chicago"))
@@ -87,8 +75,12 @@ class ModuleTest < Test::Unit::TestCase
end
def test_missing_delegation_target
- assert_raise(ArgumentError) { eval($nowhere) }
- assert_raise(ArgumentError) { eval($noplace) }
+ assert_raise(ArgumentError) do
+ Name.send :delegate, :nowhere
+ end
+ assert_raise(ArgumentError) do
+ Name.send :delegate, :noplace, :tos => :hollywood
+ end
end
def test_delegation_prefix
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index f31e7774e9..437bb78a4e 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -89,7 +89,7 @@ class ClassExtTest < Test::Unit::TestCase
end
end
-class ObjectTests < Test::Unit::TestCase
+class ObjectTests < ActiveSupport::TestCase
class DuckTime
def acts_like_time?
true
@@ -119,12 +119,9 @@ class ObjectTests < Test::Unit::TestCase
assert !duck.acts_like?(:date)
end
- def test_metaclass
- string = "Hello"
- string.metaclass.instance_eval do
- define_method(:foo) { "bar" }
- end
- assert_equal "bar", string.foo
+ def test_singleton_class
+ o = Object.new
+ assert_equal class << o; self end, o.singleton_class
end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index d8145d467b..a50e259726 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -444,6 +444,10 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert_equal "hello".concat(13), string
assert string.html_safe?
end
+
+ test 'emits normal string yaml' do
+ assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml(:foo => 1)
+ end
end
class StringExcludeTest < ActiveSupport::TestCase
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index 2fa94b8e9c..ebd26d3fc6 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -158,7 +158,8 @@ module InflectorTestCases
"Allow_Under_Scores" => "allow_under_scores",
"Trailing bad characters!@#" => "trailing-bad-characters",
"!@#Leading bad characters" => "leading-bad-characters",
- "Squeeze separators" => "squeeze-separators"
+ "Squeeze separators" => "squeeze-separators",
+ "Test with + sign" => "test-with-sign"
}
StringToParameterizeWithNoSeparator = {
@@ -166,7 +167,8 @@ module InflectorTestCases
"Random text with *(bad)* characters" => "randomtextwithbadcharacters",
"Trailing bad characters!@#" => "trailingbadcharacters",
"!@#Leading bad characters" => "leadingbadcharacters",
- "Squeeze separators" => "squeezeseparators"
+ "Squeeze separators" => "squeezeseparators",
+ "Test with + sign" => "testwithsign"
}
StringToParameterizeWithUnderscore = {
@@ -174,7 +176,8 @@ module InflectorTestCases
"Random text with *(bad)* characters" => "random_text_with_bad_characters",
"Trailing bad characters!@#" => "trailing_bad_characters",
"!@#Leading bad characters" => "leading_bad_characters",
- "Squeeze separators" => "squeeze_separators"
+ "Squeeze separators" => "squeeze_separators",
+ "Test with + sign" => "test_with_sign"
}
# Ruby 1.9 doesn't do Unicode normalization yet.
diff --git a/activesupport/test/load_paths_test.rb b/activesupport/test/load_paths_test.rb
new file mode 100644
index 0000000000..9c83d6f061
--- /dev/null
+++ b/activesupport/test/load_paths_test.rb
@@ -0,0 +1,15 @@
+require 'abstract_unit'
+
+class LoadPathsTest < Test::Unit::TestCase
+ def test_uniq_load_paths
+ load_paths_count = $LOAD_PATH.inject({}) { |paths, path|
+ expanded_path = File.expand_path(path)
+ paths[expanded_path] ||= 0
+ paths[expanded_path] += 1
+ paths
+ }
+
+ # CI has a bunch of duplicate load paths
+ # assert_equal [], load_paths_count.select { |k, v| v > 1 }, $LOAD_PATH.inspect
+ end
+end
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 545811a376..67c3527e23 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -6,7 +6,7 @@ module Notifications
ActiveSupport::Notifications.notifier = nil
@notifier = ActiveSupport::Notifications.notifier
@events = []
- @notifier.subscribe { |*args| @events << event(*args) }
+ @subscription = @notifier.subscribe { |*args| @events << event(*args) }
end
private
@@ -19,6 +19,23 @@ module Notifications
end
end
+ class UnsubscribeTest < TestCase
+ def test_unsubscribing_removes_a_subscription
+ @notifier.publish :foo
+ @notifier.wait
+ assert_equal [[:foo]], @events
+ @notifier.unsubscribe(@subscription)
+ @notifier.publish :foo
+ @notifier.wait
+ assert_equal [[:foo]], @events
+ end
+
+ private
+ def event(*args)
+ args
+ end
+ end
+
class SyncPubSubTest < TestCase
def test_events_are_published_to_a_listener
@notifier.publish :foo
@@ -26,7 +43,29 @@ module Notifications
assert_equal [[:foo]], @events
end
- def test_subscriber_with_pattern
+ def test_publishing_multiple_times_works
+ @notifier.publish :foo
+ @notifier.publish :foo
+ @notifier.wait
+ assert_equal [[:foo], [:foo]], @events
+ end
+
+ def test_publishing_after_a_new_subscribe_works
+ @notifier.publish :foo
+ @notifier.publish :foo
+
+ @notifier.subscribe("not_existant") do |*args|
+ @events << ActiveSupport::Notifications::Event.new(*args)
+ end
+
+ @notifier.publish :foo
+ @notifier.publish :foo
+ @notifier.wait
+
+ assert_equal [[:foo]] * 4, @events
+ end
+
+ def test_log_subscriber_with_pattern
events = []
@notifier.subscribe('1') { |*args| events << args }
@@ -38,7 +77,7 @@ module Notifications
assert_equal [['1'], ['1.a']], events
end
- def test_subscriber_with_pattern_as_regexp
+ def test_log_subscriber_with_pattern_as_regexp
events = []
@notifier.subscribe(/\d/) { |*args| events << args }
@@ -50,7 +89,7 @@ module Notifications
assert_equal [['1'], ['a.1'], ['1.a']], events
end
- def test_multiple_subscribers
+ def test_multiple_log_subscribers
@another = []
@notifier.subscribe { |*args| @another << args }
@notifier.publish :foo
diff --git a/activesupport/test/whiny_nil_test.rb b/activesupport/test/whiny_nil_test.rb
index 1e4f8d854a..4b9f06dead 100644
--- a/activesupport/test/whiny_nil_test.rb
+++ b/activesupport/test/whiny_nil_test.rb
@@ -9,6 +9,8 @@ end
require 'abstract_unit'
require 'active_support/whiny_nil'
+NilClass.add_whiner ::ActiveRecord::Base
+
class WhinyNilTest < Test::Unit::TestCase
def test_unchanged
nil.method_thats_not_in_whiners
diff --git a/ci/ci_build.rb b/ci/ci_build.rb
index 0c7dd837b1..2a7bbc376e 100755
--- a/ci/ci_build.rb
+++ b/ci/ci_build.rb
@@ -19,7 +19,7 @@ puts "[CruiseControl] Rails build"
build_results = {}
# Install required version of bundler.
-bundler_install_cmd = "gem install bundler -v 0.9.3 --no-ri --no-rdoc"
+bundler_install_cmd = "sudo gem install bundler -v 0.9.10 --no-ri --no-rdoc"
puts "Running command: #{bundler_install_cmd}"
build_results[:install_bundler] = system bundler_install_cmd
diff --git a/load_paths.rb b/load_paths.rb
index d5f2ca0734..b87e0d7235 100644
--- a/load_paths.rb
+++ b/load_paths.rb
@@ -6,6 +6,11 @@ rescue LoadError
require 'bundler'
Bundler.setup
rescue LoadError
+ module Bundler
+ def self.require(*args, &block); end
+ def self.method_missing(*args, &block); end
+ end
+
%w(
actionmailer
actionpack
diff --git a/pushgems.rb b/pushgems.rb
deleted file mode 100755
index 784aa5de68..0000000000
--- a/pushgems.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env ruby
-
-unless ARGV.first == "no_build"
- build_number = Time.now.strftime("%Y%m%d%H%M%S").to_i
-end
-
-%w( activeresource actionmailer actionpack activerecord railties activesupport ).each do |pkg|
- puts "Pushing: #{pkg} (#{build_number})"
- if build_number
- `cd #{pkg} && rm -rf pkg && PKG_BUILD=#{build_number} rake pgem && cd ..`
- else
- `cd #{pkg} && rm -rf pkg && rake pgem && cd ..`
- end
-end \ No newline at end of file
diff --git a/rails.gemspec b/rails.gemspec
index 1460dd5688..8b9a374e25 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -1,10 +1,13 @@
+version = File.read(File.expand_path("../RAILS_VERSION",__FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'rails'
- s.version = '3.0.0.beta1'
- s.summary = 'Full-stack web-application framework.'
- s.description = 'Full-stack web-application framework.'
+ s.version = version
+ s.summary = 'Full-stack web application framework.'
+ s.description = 'Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.'
s.required_ruby_version = '>= 1.8.7'
+ s.required_rubygems_version = ">= 1.3.6"
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
@@ -14,11 +17,11 @@ Gem::Specification.new do |s|
s.files = []
s.require_path = []
- s.add_dependency('activesupport', '= 3.0.0.beta1')
- s.add_dependency('actionpack', '= 3.0.0.beta1')
- s.add_dependency('activerecord', '= 3.0.0.beta1')
- s.add_dependency('activeresource', '= 3.0.0.beta1')
- s.add_dependency('actionmailer', '= 3.0.0.beta1')
- s.add_dependency('railties', '= 3.0.0.beta1')
- s.add_dependency('bundler', '>= 0.9.3')
+ s.add_dependency('activesupport', version)
+ s.add_dependency('actionpack', version)
+ s.add_dependency('activerecord', version)
+ s.add_dependency('activeresource', version)
+ s.add_dependency('actionmailer', version)
+ s.add_dependency('railties', version)
+ s.add_dependency('bundler', '>= 0.9.8')
end
diff --git a/rails3b.gemspec b/rails3b.gemspec
index d67d20b884..613a248ef7 100644
--- a/rails3b.gemspec
+++ b/rails3b.gemspec
@@ -2,8 +2,7 @@ Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'rails3b'
s.version = '3.0.1'
- s.summary = 'Just the Rails 3 beta dependencies. Works around prerelease RubyGems bug.'
- s.description = 'My kingdom for working dependencies.'
+ s.summary = 'Just the Rails 3 beta dependencies. Works around prerelease RubyGems bug in versions before 1.3.6.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'Jeremy Kemper'
@@ -22,7 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency('tzinfo', '~> 0.3.16')
s.add_dependency('builder', '~> 2.1.2')
s.add_dependency('memcache-client', '~> 1.7.5')
- s.add_dependency('bundler', '>= 0.9.2')
+ s.add_dependency('bundler', '>= 0.9.8')
s.add_dependency('rake', '>= 0.8.3')
s.add_dependency('thor', '~> 0.13')
end
diff --git a/railties/Rakefile b/railties/Rakefile
index f32a794544..fe049d565f 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -8,20 +8,6 @@ require 'rake/gempackagetask'
require 'date'
require 'rbconfig'
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/lib"
-require 'rails/version'
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'railties'
-PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "rails"
-RUBY_FORGE_USER = "webster132"
-
task :default => :test
task :test => 'test:isolated'
diff --git a/railties/bin/rails b/railties/bin/rails
index 72c47b533f..173f122445 100755
--- a/railties/bin/rails
+++ b/railties/bin/rails
@@ -1,11 +1,30 @@
-if File.exists?(Dir.getwd + '/script/rails')
- exec(Dir.getwd + '/script/rails', *ARGV)
-else
- railties_path = File.expand_path('../../lib', __FILE__)
- $:.unshift(railties_path) if File.directory?(railties_path) && !$:.include?(railties_path)
+require 'rbconfig'
- require 'rails/ruby_version_check'
- Signal.trap("INT") { puts; exit }
+module Rails
+ module ScriptRailsLoader
+ RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
+ SCRIPT_RAILS = File.join('script', 'rails')
- require 'rails/commands/application'
-end \ No newline at end of file
+ def self.exec_script_rails!
+ cwd = Dir.pwd
+ exec RUBY, SCRIPT_RAILS, *ARGV if File.exists?(SCRIPT_RAILS)
+ Dir.chdir("..") do
+ # Recurse in a chdir block: if the search fails we want to be sure
+ # the application is generated in the original working directory.
+ exec_script_rails! unless cwd == Dir.pwd
+ end
+ rescue SystemCallError
+ # could not chdir, no problem just return
+ end
+ end
+end
+
+Rails::ScriptRailsLoader.exec_script_rails!
+
+railties_path = File.expand_path('../../lib', __FILE__)
+$:.unshift(railties_path) if File.directory?(railties_path) && !$:.include?(railties_path)
+
+require 'rails/ruby_version_check'
+Signal.trap("INT") { puts; exit }
+
+require 'rails/commands/application'
diff --git a/railties/builtin/routes.rb b/railties/builtin/routes.rb
index ef9d9e756d..bd58034d8f 100644
--- a/railties/builtin/routes.rb
+++ b/railties/builtin/routes.rb
@@ -1,3 +1,3 @@
-ActionController::Routing::Routes.draw do |map|
+Rails.application.routes.draw do |map|
match '/rails/info/properties' => "rails/info#properties"
-end \ No newline at end of file
+end
diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile
index 55fc610a53..9c6e7863f4 100644
--- a/railties/guides/source/3_0_release_notes.textile
+++ b/railties/guides/source/3_0_release_notes.textile
@@ -443,7 +443,7 @@ As well as the following deprecations:
* I18n error messages for ActiveRecord should be changed from :en.activerecord.errors.template to <tt>:en.errors.template</tt>.
* <tt>model.errors.on</tt> is deprecated in favour of <tt>model.errors[]</tt>
* validates_presence_of => validates... :presence => true
-* <tt>ActiveRecord::Base.colorize_logging</tt> and <tt>config.active_record.colorize_logging</tt> are deprecated in favour of <tt>Rails::Subscriber.colorize_logging</tt> or <tt>config.colorize_logging</tt>
+* <tt>ActiveRecord::Base.colorize_logging</tt> and <tt>config.active_record.colorize_logging</tt> are deprecated in favour of <tt>Rails::LogSubscriber.colorize_logging</tt> or <tt>config.colorize_logging</tt>
NOTE: While an implementation of State Machine has been in Active Record edge for some months now, it has been removed from the Rails 3.0 release.
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 0e6f981168..5418ad7dd4 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -159,7 +159,7 @@ WARNING: Threadsafe operation in incompatible with the normal workings of develo
* +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']+.
-* +config.action_controller.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class.
+* +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class.
The caching code adds two additional settings:
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index ecf68b56f9..b62ff8cb38 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -92,7 +92,7 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves
That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _(highlight)don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb:
<ruby>
-config.action_controller.session = {
+config.action_dispatch.session = {
:key => '_app_session',
:secret => '0x0dkfj3927dkc7djdh36rkckdfzsg...'
}
diff --git a/railties/lib/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/generators/erb/scaffold/templates/_form.html.erb
index 9c19a09616..01ec58c615 100644
--- a/railties/lib/generators/erb/scaffold/templates/_form.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/_form.html.erb
@@ -1,4 +1,4 @@
-<%% form_for(@<%= singular_name %>) do |f| %>
+<%%= form_for(@<%= singular_name %>) do |f| %>
<%%= f.error_messages %>
<% for attribute in attributes -%>
diff --git a/railties/lib/generators/erb/scaffold/templates/layout.html.erb b/railties/lib/generators/erb/scaffold/templates/layout.html.erb
index 420d17f33c..3f64be0c45 100644
--- a/railties/lib/generators/erb/scaffold/templates/layout.html.erb
+++ b/railties/lib/generators/erb/scaffold/templates/layout.html.erb
@@ -9,6 +9,7 @@
<body>
<p class="notice"><%%= notice %></p>
+<p class="alert"><%%= alert %></p>
<%%= yield %>
diff --git a/railties/lib/generators/rails/app/app_generator.rb b/railties/lib/generators/rails/app/app_generator.rb
index c439ed89f5..92e0d37436 100644
--- a/railties/lib/generators/rails/app/app_generator.rb
+++ b/railties/lib/generators/rails/app/app_generator.rb
@@ -7,6 +7,10 @@ module Rails::Generators
# can change in Ruby 1.8.7 when we FileUtils.cd.
RAILS_DEV_PATH = File.expand_path("../../../../..", File.dirname(__FILE__))
+ RESERVED_NAMES = %w[generate console server dbconsole
+ application destroy benchmarker profiler
+ plugin runner test]
+
class AppGenerator < Base
DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db )
@@ -134,8 +138,11 @@ module Rails::Generators
end
def create_prototype_files
- return if options[:skip_prototype]
- directory "public/javascripts"
+ unless options[:skip_prototype]
+ directory "public/javascripts"
+ else
+ empty_directory_with_gitkeep "public/javascripts"
+ end
end
def create_script_files
@@ -209,9 +216,10 @@ module Rails::Generators
end
def valid_app_const?
- case app_const
- when /^\d/
+ if app_const =~ /^\d/
raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
+ elsif RESERVED_NAMES.include?(app_name)
+ raise Error, "Invalid application name #{app_name}. Please give a name which does not match one of the reserved rails words."
end
end
diff --git a/railties/lib/generators/rails/app/templates/Gemfile b/railties/lib/generators/rails/app/templates/Gemfile
index f4bce8646d..0dd10f3f2d 100644
--- a/railties/lib/generators/rails/app/templates/Gemfile
+++ b/railties/lib/generators/rails/app/templates/Gemfile
@@ -1,4 +1,4 @@
-source 'http://gemcutter.org'
+source 'http://rubygems.org'
<%- if options.dev? -%>
gem 'rails', :path => '<%= Rails::Generators::RAILS_DEV_PATH %>'
@@ -15,15 +15,15 @@ gem 'rails', '<%= Rails::VERSION::STRING %>'
gem '<%= gem_for_database %>'<% if require_for_database %>, :require => '<%= require_for_database %>'<% end %>
<% end -%>
-# Use mongrel as the web server
-# gem 'mongrel'
+# Use unicorn as the web server
+# gem 'unicorn'
# Deploy with Capistrano
# gem 'capistrano'
# Bundle the extra gems:
# gem 'bj'
-# gem 'hpricot', '0.6'
+# gem 'nokogiri', '1.4.1'
# gem 'sqlite3-ruby', :require => 'sqlite3'
# gem 'aws-s3', :require => 'aws/s3'
diff --git a/railties/lib/generators/rails/app/templates/config/application.rb b/railties/lib/generators/rails/app/templates/config/application.rb
index 7c555c2542..dc20ffb2fa 100644
--- a/railties/lib/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/generators/rails/app/templates/config/application.rb
@@ -35,7 +35,7 @@ module <%= app_const_base %>
# 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}')]
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Configure generators values. Many other options are available, be sure to check the documentation.
diff --git a/railties/lib/generators/rails/app/templates/config/boot.rb b/railties/lib/generators/rails/app/templates/config/boot.rb
index 29c9d506e5..3cb561d41f 100644
--- a/railties/lib/generators/rails/app/templates/config/boot.rb
+++ b/railties/lib/generators/rails/app/templates/config/boot.rb
@@ -5,13 +5,4 @@ rescue LoadError
require 'rubygems'
require 'bundler'
Bundler.setup
-
- # To use 2.x style vendor/rails and RubyGems
- #
- # vendor_rails = File.expand_path('../../vendor/rails', __FILE__)
- # if File.exist?(vendor_rails)
- # Dir["#{vendor_rails}/*/lib"].each { |path| $:.unshift(path) }
- # end
- #
- # require 'rubygems'
end
diff --git a/railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt b/railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt
index 451dbe1d1c..be627fbbcc 100644
--- a/railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt
@@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
-ActionController::Base.cookie_verifier_secret = '<%= app_secret %>'
+Rails.application.config.cookie_secret = '<%= app_secret %>'
diff --git a/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
index baff704d3e..9e32fb930e 100644
--- a/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
+++ b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt
@@ -1,15 +1,10 @@
# Be sure to restart your server when you modify this file.
-# Your secret key for verifying cookie session data integrity.
-# If you change this key, all old sessions will become invalid!
-# Make sure the secret is at least 30 characters and all random,
-# no regular words or you'll be exposed to dictionary attacks.
-ActionController::Base.session = {
- :key => '_<%= app_name %>_session',
- :secret => '<%= app_secret %>'
+Rails.application.config.session_store :cookie_store, {
+ :key => '_<%= app_name %>_session',
}
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rake db:sessions:create")
-# ActionController::Base.session_store = :active_record_store
+# Rails.application.config.session_store :active_record_store
diff --git a/railties/lib/generators/rails/app/templates/public/index.html b/railties/lib/generators/rails/app/templates/public/index.html
index ef916f9c5a..836da1b689 100644
--- a/railties/lib/generators/rails/app/templates/public/index.html
+++ b/railties/lib/generators/rails/app/templates/public/index.html
@@ -181,27 +181,27 @@
}
</style>
- <script type="text/javascript" src="javascripts/prototype.js"></script>
- <script type="text/javascript" src="javascripts/effects.js"></script>
<script type="text/javascript">
function about() {
- if (Element.empty('about-content')) {
- new Ajax.Updater('about-content', 'rails/info/properties', {
- method: 'get',
- onFailure: function() {Element.classNames('about-content').add('failure')},
- onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
- });
- } else {
- new Effect[Element.visible('about-content') ?
- 'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
- }
+ info = document.getElementById('about-content');
+ if (window.XMLHttpRequest)
+ { xhr = new XMLHttpRequest(); }
+ else
+ { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
+ xhr.open("GET","rails/info/properties",false);
+ xhr.send("");
+ info.innerHTML = xhr.responseText;
+ info.style.display = 'block'
+ }
+
+ function prepend() {
+ search = document.getElementById('search-text');
+ text = search.value;
+ search.value = 'site:rubyonrails.org ' + text;
}
window.onload = function() {
- $('search-text').value = '';
- $('search').onsubmit = function() {
- $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
- }
+ document.getElementById('search-text').value = '';
}
</script>
</head>
@@ -210,7 +210,7 @@
<div id="sidebar">
<ul id="sidebar-items">
<li>
- <form id="search" action="http://www.google.com/search" method="get">
+ <form id="search" action="http://www.google.com/search" method="get" onSubmit="prepend();">
<input type="hidden" name="hl" value="en" />
<input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
<input type="submit" value="Search" /> the Rails site
diff --git a/railties/lib/generators/rails/app/templates/script/rails b/railties/lib/generators/rails/app/templates/script/rails
index b01d1ee183..199fe1a6d3 100644
--- a/railties/lib/generators/rails/app/templates/script/rails
+++ b/railties/lib/generators/rails/app/templates/script/rails
@@ -1,8 +1,5 @@
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
-ENV_PATH = File.expand_path('../../config/environment', __FILE__)
-BOOT_PATH = File.expand_path('../../config/boot', __FILE__)
-APP_PATH = File.expand_path('../../config/application', __FILE__)
-
-require BOOT_PATH
+ENV_PATH = File.expand_path('../../config/environment', __FILE__)
+require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
diff --git a/railties/lib/generators/rails/stylesheets/templates/scaffold.css b/railties/lib/generators/rails/stylesheets/templates/scaffold.css
index de6669ad9e..ea3dc9b8b5 100644
--- a/railties/lib/generators/rails/stylesheets/templates/scaffold.css
+++ b/railties/lib/generators/rails/stylesheets/templates/scaffold.css
@@ -24,6 +24,10 @@ div.field, div.actions {
color: green;
}
+.alert {
+ color: red;
+}
+
.fieldWithErrors {
padding: 2px;
background-color: red;
diff --git a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb
index e1aeb2db90..a2b1f1ed05 100644
--- a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb
+++ b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb
@@ -7,7 +7,6 @@ class <%= class_name %>Test < ActionMailer::TestCase
@expected.to = "to@example.org"
@expected.from = "from@example.com"
@expected.body = read_fixture("<%= action %>")
- @expected.date = Time.now
assert_equal @expected, <%= class_name %>.<%= action %>
end
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index b7a39fd5a7..3d3151bd8f 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -7,7 +7,7 @@ require 'active_support/core_ext/logger'
require 'rails/application'
require 'rails/version'
require 'rails/deprecation'
-require 'rails/subscriber'
+require 'rails/log_subscriber'
require 'rails/ruby_version_check'
require 'active_support/railtie'
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index f445ef4831..49388c5060 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -52,7 +52,10 @@ module Rails
class << self
private :new
- alias :configure :class_eval
+
+ def configure(&block)
+ class_eval(&block)
+ end
def instance
if self == Rails::Application
@@ -85,17 +88,13 @@ module Rails
end
def routes
- ::ActionController::Routing::Routes
+ @routes ||= ActionDispatch::Routing::RouteSet.new
end
def railties
@railties ||= Railties.new(config)
end
- def metal_loader
- @metal_laoder ||= MetalLoader.new
- end
-
def routes_reloader
@routes_reloader ||= RoutesReloader.new
end
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index b20e53f2de..06243f2e5e 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -1,3 +1,5 @@
+require "active_support/notifications"
+
module Rails
class Application
module Bootstrap
@@ -49,20 +51,6 @@ module Rails
end
end
- # Initialize rails subscriber on top of notifications.
- initializer :initialize_subscriber do
- require 'active_support/notifications'
-
- if config.colorize_logging == false
- Rails::Subscriber.colorize_logging = false
- config.generators.colorize_logging = false
- end
-
- ActiveSupport::Notifications.subscribe do |*args|
- Rails::Subscriber.dispatch(args)
- end
- end
-
initializer :set_clear_dependencies_hook do
unless config.cache_classes
ActionDispatch::Callbacks.after do
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index d6ad045294..d3a0ecb243 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -5,7 +5,7 @@ module Rails
class Configuration < ::Rails::Engine::Configuration
include ::Rails::Configuration::Deprecated
- attr_accessor :allow_concurrency, :cache_classes, :cache_store, :colorize_logging,
+ attr_accessor :allow_concurrency, :cache_classes, :cache_store,
:consider_all_requests_local, :dependency_loading,
:filter_parameters, :log_level, :logger, :metals,
:plugins, :preload_frameworks, :reload_engines, :reload_plugins,
@@ -14,7 +14,6 @@ module Rails
def initialize(*)
super
@allow_concurrency = false
- @colorize_logging = true
@filter_parameters = []
@dependency_loading = true
@serve_static_assets = true
@@ -22,6 +21,10 @@ module Rails
@consider_all_requests_local = true
end
+ def middleware
+ @@default_middleware_stack ||= default_middleware
+ end
+
def paths
@paths ||= begin
paths = super
@@ -81,6 +84,16 @@ module Rails
def log_level
@log_level ||= Rails.env.production? ? :info : :debug
end
+
+ def colorize_logging
+ @colorize_logging
+ end
+
+ def colorize_logging=(val)
+ @colorize_logging = val
+ Rails::LogSubscriber.colorize_logging = val
+ self.generators.colorize_logging = val
+ end
end
end
end \ No newline at end of file
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index b722679ec2..cb38d5a5db 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -31,7 +31,6 @@ module Rails
app
end
- # Fires the user-supplied after_initialize block (config.after_initialize)
initializer :after_initialize do
config.after_initialize_blocks.each do |block|
block.call(self)
diff --git a/railties/lib/rails/application/metal_loader.rb b/railties/lib/rails/application/metal_loader.rb
index c0f2e4f948..2a43fa7892 100644
--- a/railties/lib/rails/application/metal_loader.rb
+++ b/railties/lib/rails/application/metal_loader.rb
@@ -34,7 +34,7 @@ module Rails
Dir.glob("#{path}/**/*.rb").sort.each do |metal_path|
metal = metal_path.sub(matcher, '\1').to_sym
next unless list.include?(metal) || list.include?(:all)
- require_dependency metal
+ require_dependency metal.to_s
metals << metal
end
end
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index fde6211c5d..a5154f4bba 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -27,7 +27,7 @@ module Rails
routes.clear!
paths.each { |path| load(path) }
- routes.finalize!
+ ActionController.base_hook { routes.finalize! }
nil
ensure
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 6972e25b29..d4558be00f 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -30,19 +30,17 @@ when 'g', 'generate'
require 'rails/commands/generate'
when 'c', 'console'
require 'rails/commands/console'
- require APP_PATH
+ require ENV_PATH
Rails::Console.start(Rails::Application)
when 's', 'server'
require 'rails/commands/server'
- # Initialize the server first, so environment options are set
server = Rails::Server.new
- require APP_PATH
-
+ require ENV_PATH
Dir.chdir(Rails::Application.root)
server.start
when 'db', 'dbconsole'
require 'rails/commands/dbconsole'
- require APP_PATH
+ require ENV_PATH
Rails::DBConsole.start(Rails::Application)
when 'application'
@@ -57,7 +55,7 @@ when 'profiler'
require ENV_PATH
require 'rails/commands/performance/profiler'
when 'plugin'
- require APP_PATH
+ require ENV_PATH
require 'rails/commands/plugin'
when 'runner'
require 'rails/commands/runner'
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 910b26f886..73ae9bcb16 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -4,92 +4,6 @@ require 'rails/rack'
module Rails
module Configuration
- # Holds coonfiguration shared between Railtie, Engine and Application.
- module Shared
- def middleware
- @@default_middleware_stack ||= ActionDispatch::MiddlewareStack.new.tap do |middleware|
- middleware.use('::ActionDispatch::Static', lambda { Rails.public_path }, :if => lambda { Rails.application.config.serve_static_assets })
- middleware.use('::Rack::Lock', :if => lambda { !Rails.application.config.allow_concurrency })
- middleware.use('::Rack::Runtime')
- middleware.use('::Rails::Rack::Logger')
- middleware.use('::ActionDispatch::ShowExceptions', lambda { Rails.application.config.consider_all_requests_local })
- middleware.use('::ActionDispatch::Callbacks', lambda { !Rails.application.config.cache_classes })
- middleware.use('::ActionDispatch::Cookies')
- middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options })
- middleware.use('::ActionDispatch::Flash', :if => lambda { ActionController::Base.session_store })
- middleware.use(lambda { Rails.application.metal_loader.build_middleware(Rails.application.config.metals) }, :if => lambda { Rails.application.metal_loader.metals.any? })
- middleware.use('ActionDispatch::ParamsParser')
- middleware.use('::Rack::MethodOverride')
- middleware.use('::ActionDispatch::Head')
- end
- end
-
- # Holds generators configuration:
- #
- # config.generators do |g|
- # g.orm :datamapper, :migration => true
- # g.template_engine :haml
- # g.test_framework :rspec
- # end
- #
- # If you want to disable color in console, do:
- #
- # config.generators.colorize_logging = false
- #
- def generators
- @@generators ||= Rails::Configuration::Generators.new
- if block_given?
- yield @@generators
- else
- @@generators
- end
- end
-
- def after_initialize_blocks
- @@after_initialize_blocks ||= []
- end
-
- def after_initialize(&blk)
- after_initialize_blocks << blk if blk
- end
-
- def to_prepare_blocks
- @@to_prepare_blocks ||= []
- end
-
- def to_prepare(&blk)
- to_prepare_blocks << blk if blk
- end
-
- def respond_to?(name)
- super || name.to_s =~ config_key_regexp
- end
-
- private
-
- def method_missing(name, *args, &blk)
- if name.to_s =~ config_key_regexp
- return $2 == '=' ? options[$1] = args.first : options[$1]
- end
- super
- end
-
- def config_key_regexp
- bits = config_keys.map { |n| Regexp.escape(n.to_s) }.join('|')
- /^(#{bits})(?:=)?$/
- end
-
- def config_keys
- (Railtie.railtie_names + Engine.engine_names).map { |n| n.to_s }.uniq
- end
-
- def options
- @@options ||= Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new }
- end
- end
-
- # Generators configuration which uses method missing to wrap it in a nifty DSL.
- # It also allows you to set generators fallbacks and aliases.
class Generators #:nodoc:
attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging
@@ -123,7 +37,6 @@ module Rails
end
end
- # Holds configs deprecated in 3.0. Will be removed on 3.1.
module Deprecated
def frameworks(*args)
raise "config.frameworks in no longer supported. See the generated " \
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index 7e8fd027e6..4a7701081b 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -17,8 +17,8 @@ end
# create a new session. If a block is given, the new session will be yielded
# to the block before being returned.
def new_session
- app = ActionController::Dispatcher.new
- session = ActionController::Integration::Session.new(app)
+ app = Rails.application
+ session = ActionDispatch::Integration::Session.new(app)
yield session if block_given?
session
end
@@ -26,7 +26,8 @@ end
# reloads the environment
def reload!(print=true)
puts "Reloading..." if print
- ActionDispatch::Callbacks.new(lambda {}, false)
+ # This triggers the to_prepare callbacks
+ ActionDispatch::Callbacks.new(Proc.new {}, false).call({})
true
end
diff --git a/railties/lib/rails/console/helpers.rb b/railties/lib/rails/console/helpers.rb
index 039db667c4..212fc6125a 100644
--- a/railties/lib/rails/console/helpers.rb
+++ b/railties/lib/rails/console/helpers.rb
@@ -2,4 +2,6 @@ def helper
@helper ||= ApplicationController.helpers
end
-@controller = ApplicationController.new
+def controller
+ @controller ||= ApplicationController.new
+end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 226d1aaa88..7ee0f31359 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -1,5 +1,6 @@
-require 'active_support/core_ext/module/delegation'
require 'rails/railtie'
+require 'active_support/core_ext/module/delegation'
+require 'pathname'
module Rails
# Rails::Engine allows you to wrap a specific Rails application and share it accross
@@ -98,8 +99,9 @@ module Rails
def inherited(base)
unless abstract_railtie?(base)
base.called_from = begin
- call_stack = caller.map { |p| p.split(':').first }
- File.dirname(call_stack.detect { |p| p !~ %r[railties[\w\-]*/lib/rails|rack[\w\-]*/lib/rack] })
+ # Remove the line number from backtraces making sure we don't leave anything behind
+ call_stack = caller.map { |p| p.split(':')[0..-2].join(':') }
+ File.dirname(call_stack.detect { |p| p !~ %r[railties[\w\-\.]*/lib/rails|rack[\w\-\.]*/lib/rack] })
end
end
@@ -122,10 +124,10 @@ module Rails
end
end
- delegate :middleware, :paths, :root, :to => :config
+ delegate :middleware, :paths, :metal_loader, :root, :to => :config
def load_tasks
- super
+ super
config.paths.lib.tasks.to_a.sort.each { |ext| load(ext) }
end
@@ -159,10 +161,11 @@ module Rails
end
end
+ # DEPRECATED: Remove in 3.1
initializer :add_routing_namespaces do |app|
paths.app.controllers.to_a.each do |load_path|
load_path = File.expand_path(load_path)
- Dir["#{load_path}/*/*_controller.rb"].collect do |path|
+ Dir["#{load_path}/*/**/*_controller.rb"].collect do |path|
namespace = File.dirname(path).sub(/#{load_path}\/?/, '')
app.routes.controller_namespaces << namespace unless namespace.empty?
end
@@ -172,13 +175,13 @@ module Rails
# I18n load paths are a special case since the ones added
# later have higher priority.
initializer :add_locales do
- config.i18n.engines_load_path.concat(paths.config.locales.to_a)
+ config.i18n.railties_load_path.concat(paths.config.locales.to_a)
end
initializer :add_view_paths do
views = paths.app.views.to_a
- ActionController::Base.view_paths.unshift(*views) if defined?(ActionController)
- ActionMailer::Base.view_paths.unshift(*views) if defined?(ActionMailer)
+ ActionController.base_hook { prepend_view_path(views) } if defined?(ActionController)
+ ActionMailer.base_hook { prepend_view_path(views) } if defined?(ActionMailer)
end
initializer :add_metals do |app|
@@ -214,4 +217,4 @@ module Rails
app.config.reload_engines
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 93b882f874..b8f1f1009c 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -7,6 +7,7 @@ module Rails
attr_writer :eager_load_paths, :load_once_paths, :load_paths
def initialize(root=nil)
+ super()
@root = root
end
@@ -17,8 +18,9 @@ module Rails
paths.app.controllers "app/controllers", :eager_load => true
paths.app.helpers "app/helpers", :eager_load => true
paths.app.models "app/models", :eager_load => true
- paths.app.metals "app/metal"
- paths.app.views "app/views"
+ paths.app.mailers "app/mailers", :eager_load => true
+ paths.app.metals "app/metal", :eager_load => true
+ paths.app.views "app/views", :eager_load => true
paths.lib "lib", :load_path => true
paths.lib.tasks "lib/tasks", :glob => "**/*.rake"
paths.lib.templates "lib/templates"
@@ -26,6 +28,9 @@ module Rails
paths.config.initializers "config/initializers", :glob => "**/*.rb"
paths.config.locales "config/locales", :glob => "*.{rb,yml}"
paths.config.routes "config/routes.rb"
+ paths.public "public"
+ paths.public.javascripts "public/javascripts"
+ paths.public.stylesheets "public/stylesheets"
paths
end
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index c01018aab2..3c902ce0d4 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -3,17 +3,23 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.inc
require 'active_support'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/inflections'
require 'rails/generators/base'
-require 'rails/generators/named_base'
module Rails
module Generators
+ autoload :Actions, 'rails/generators/actions'
+ autoload :ActiveModel, 'rails/generators/active_model'
+ autoload :Migration, 'rails/generators/migration'
+ autoload :NamedBase, 'rails/generators/named_base'
+ autoload :ResourceHelpers, 'rails/generators/resource_helpers'
+ autoload :TestCase, 'rails/generators/test_case'
+
DEFAULT_ALIASES = {
:rails => {
:actions => '-a',
@@ -291,4 +297,4 @@ end
# If the application was already defined, configure generators,
# otherwise you have to configure it by hand.
-Rails::Generators.configure! if Rails.respond_to?(:application) && Rails.application \ No newline at end of file
+Rails::Generators.configure! if Rails.respond_to?(:application) && Rails.application
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index d41da773c6..7dec4d446a 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -89,12 +89,12 @@ module Rails
#
# ==== Example
#
- # source "http://gems.github.com/"
+ # add_source "http://gems.github.com/"
def add_source(source, options={})
log :source, source
in_root do
- prepend_file "Gemfile", "source #{source.inspect}", :verbose => false
+ prepend_file "Gemfile", "source #{source.inspect}\n", :verbose => false
end
end
@@ -280,6 +280,16 @@ module Rails
end
end
+ # Reads the given file at the source root and prints it in the console.
+ #
+ # === Example
+ #
+ # readme "README"
+ #
+ def readme(path)
+ say File.read(find_in_source_paths(path))
+ end
+
protected
# Define log for backwards compatibility. If just one argument is sent,
diff --git a/railties/lib/rails/subscriber.rb b/railties/lib/rails/log_subscriber.rb
index 9bf22f27a4..42697d2e32 100644
--- a/railties/lib/rails/subscriber.rb
+++ b/railties/lib/rails/log_subscriber.rb
@@ -2,15 +2,15 @@ require 'active_support/core_ext/class/inheritable_attributes'
require 'active_support/notifications'
module Rails
- # Rails::Subscriber is an object set to consume ActiveSupport::Notifications
- # on initialization with the sole purpose of logging. The subscriber dispatches
- # notifications to a registered object based on it's given namespace.
+ # Rails::LogSubscriber is an object set to consume ActiveSupport::Notifications
+ # on initialization with solely purpose of logging. The log subscriber dispatches
+ # notifications to a regirested object based on its given namespace.
#
- # An example would be an Active Record subscriber responsible for logging queries:
+ # An example would be ActiveRecord log subscriber responsible for logging queries:
#
# module ActiveRecord
# class Railtie
- # class Subscriber < Rails::Subscriber
+ # class LogSubscriber < Rails::LogSubscriber
# def sql(event)
# "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
# end
@@ -18,21 +18,21 @@ module Rails
# end
# end
#
- # Which would be registed as:
+ # It's finally registed as:
#
- # Rails::Subscriber.add :active_record, ActiveRecord::Railtie::Subscriber.new
+ # Rails::LogSubscriber.add :active_record, ActiveRecord::Railtie::LogSubscriber.new
#
- # So whenever an +active_record.sql+ notification arrives to Rails::Subscriber,
+ # So whenever a "active_record.sql" notification arrive to Rails::LogSubscriber,
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
# the sql method.
#
- # This avoids spanning several subscribers just for logging purposes
- # (which slows down the main thread). It also provides a centralized
+ # This is useful because it avoids spanning several log subscribers just for logging
+ # purposes(which slows down the main thread). Besides of providing a centralized
# facility on top of Rails.logger.
- #
- # Subscriber also has some helpers to deal with logging and automatically flushes
- # all logs when the request finishes.
- class Subscriber
+ #
+ # Log subscriber also has some helpers to deal with logging and automatically flushes
+ # all logs when the request finishes (via action_dispatch.callback notification).
+ class LogSubscriber
mattr_accessor :colorize_logging
self.colorize_logging = true
@@ -50,30 +50,29 @@ module Rails
CYAN = "\e[36m"
WHITE = "\e[37m"
- def self.add(namespace, subscriber)
- subscribers[namespace.to_sym] = subscriber
- end
-
- def self.subscribers
- @subscribers ||= {}
- end
+ def self.add(namespace, log_subscriber, notifier = ActiveSupport::Notifications)
+ log_subscribers << log_subscriber
- def self.dispatch(args)
- namespace, name = args[0].split(".")
- subscriber = subscribers[namespace.to_sym]
+ log_subscriber.public_methods(false).each do |event|
+ notifier.subscribe("#{namespace}.#{event}") do |*args|
+ next if log_subscriber.logger.nil?
- if subscriber.respond_to?(name) && subscriber.logger
- begin
- subscriber.send(name, ActiveSupport::Notifications::Event.new(*args))
- rescue Exception => e
- Rails.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}"
+ begin
+ log_subscriber.send(event, ActiveSupport::Notifications::Event.new(*args))
+ rescue Exception => e
+ Rails.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}"
+ end
end
end
end
- # Flush all subscribers' logger.
+ def self.log_subscribers
+ @log_subscribers ||= []
+ end
+
+ # Flush all log_subscribers' logger.
def self.flush_all!
- loggers = subscribers.values.map(&:logger)
+ loggers = log_subscribers.map(&:logger)
loggers.uniq!
loggers.each { |l| l.flush if l.respond_to?(:flush) }
end
@@ -88,6 +87,7 @@ module Rails
%w(info debug warn error fatal unknown).each do |level|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{level}(*args, &block)
+ return unless logger
logger.#{level}(*args, &block)
end
METHOD
@@ -97,6 +97,7 @@ module Rails
# option is set to true, it also adds bold to the string. This is based
# on Highline implementation and it automatically appends CLEAR to the end
# of the returned String.
+ #
def color(text, color, bold=false)
return text unless colorize_logging
color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
diff --git a/railties/lib/rails/subscriber/test_helper.rb b/railties/lib/rails/log_subscriber/test_helper.rb
index 39b4117372..02f5079462 100644
--- a/railties/lib/rails/subscriber/test_helper.rb
+++ b/railties/lib/rails/log_subscriber/test_helper.rb
@@ -1,13 +1,13 @@
-require 'rails/subscriber'
+require 'rails/log_subscriber'
module Rails
- class Subscriber
- # Provides some helpers to deal with testing subscribers by setting up
+ class LogSubscriber
+ # Provides some helpers to deal with testing log subscribers by setting up
# notifications. Take for instance ActiveRecord subscriber tests:
#
- # class SyncSubscriberTest < ActiveSupport::TestCase
- # include Rails::Subscriber::TestHelper
- # Rails::Subscriber.add(:active_record, ActiveRecord::Railties::Subscriber.new)
+ # class SyncLogSubscriberTest < ActiveSupport::TestCase
+ # include Rails::LogSubscriber::TestHelper
+ # Rails::LogSubscriber.add(:active_record, ActiveRecord::Railties::LogSubscriber.new)
#
# def test_basic_query_logging
# Developer.all
@@ -17,18 +17,18 @@ module Rails
# assert_match /SELECT \* FROM "developers"/, @logger.logged(:debug).last
# end
#
- # class SyncSubscriberTest < ActiveSupport::TestCase
- # include Rails::Subscriber::SyncTestHelper
- # include SubscriberTest
+ # class SyncLogSubscriberTest < ActiveSupport::TestCase
+ # include Rails::LogSubscriber::SyncTestHelper
+ # include LogSubscriberTest
# end
#
- # class AsyncSubscriberTest < ActiveSupport::TestCase
- # include Rails::Subscriber::AsyncTestHelper
- # include SubscriberTest
+ # class AsyncLogSubscriberTest < ActiveSupport::TestCase
+ # include Rails::LogSubscriber::AsyncTestHelper
+ # include LogSubscriberTest
# end
# end
#
- # All you need to do is to ensure that your subscriber is added to Rails::Subscriber,
+ # All you need to do is to ensure that your log subscriber is added to Rails::Subscriber,
# as in the second line of the code above. The test helpers is reponsible for setting
# up the queue, subscriptions and turning colors in logs off.
#
@@ -42,8 +42,7 @@ module Rails
@logger = MockLogger.new
@notifier = ActiveSupport::Notifications::Notifier.new(queue)
- Rails::Subscriber.colorize_logging = false
- @notifier.subscribe { |*args| Rails::Subscriber.dispatch(args) }
+ Rails::LogSubscriber.colorize_logging = false
set_logger(@logger)
ActiveSupport::Notifications.notifier = @notifier
@@ -80,7 +79,7 @@ module Rails
@notifier.wait
end
- # Overwrite if you use another logger in your subscriber:
+ # Overwrite if you use another logger in your log subscriber:
#
# def logger
# ActiveRecord::Base.logger = @logger
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 2a746e0fa6..0997be1b6f 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -1,4 +1,5 @@
require 'rails/engine'
+require 'active_support/core_ext/array/conversions'
module Rails
# Rails::Plugin is nothing more than a Rails::Engine, but since it's loaded too late
@@ -40,12 +41,14 @@ module Rails
def load_tasks
super
- extra_tasks = Dir["#{root}/{tasks,rails/tasks}/**/*.rake"]
+ load_deprecated_tasks
+ end
- unless extra_tasks.empty?
- ActiveSupport::Deprecation.warn "Having rake tasks in PLUGIN_PATH/tasks or " <<
- "PLUGIN_PATH/rails/tasks is deprecated. Use to PLUGIN_PATH/lib/tasks instead"
- extra_tasks.sort.each { |ext| load(ext) }
+ def load_deprecated_tasks
+ tasks = Dir["#{root}/{tasks,rails/tasks}/**/*.rake"].sort
+ if tasks.any?
+ ActiveSupport::Deprecation.warn "Rake tasks in #{tasks.to_sentence} are deprecated. Use lib/tasks instead"
+ tasks.each { |ext| load(ext) }
end
end
@@ -59,9 +62,14 @@ module Rails
end
initializer :load_init_rb, :before => :load_application_initializers do |app|
- file = Dir["#{root}/{rails/init,init}.rb"].first
- config = app.config
- eval(File.read(file), binding, file) if file && File.file?(file)
+ files = %w(rails/init.rb init.rb).map { |path| File.expand_path path, root }
+ if initrb = files.find { |path| File.file? path }
+ if initrb == files.first
+ ActiveSupport::Deprecation.warn "Use toplevel init.rb; rails/init.rb is deprecated: #{initrb}"
+ end
+ config = app.config
+ eval(File.read(initrb), binding, initrb)
+ end
end
initializer :sanity_check_railties_collision do
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index de21fb4f10..dd8b342f59 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -1,9 +1,9 @@
-require 'rails/subscriber'
+require 'rails/log_subscriber'
module Rails
module Rack
# Log the request started and flush all loggers after it.
- class Logger < Rails::Subscriber
+ class Logger < Rails::LogSubscriber
def initialize(app)
@app = app
end
@@ -19,14 +19,14 @@ module Rails
def before_dispatch(env)
request = ActionDispatch::Request.new(env)
- path = request.request_uri.inspect rescue "unknown"
+ path = request.fullpath.inspect rescue "unknown"
info "\n\nStarted #{request.method.to_s.upcase} #{path} " <<
"for #{request.remote_ip} at #{Time.now.to_s(:db)}"
end
def after_dispatch(env)
- Rails::Subscriber.flush_all!
+ Rails::LogSubscriber.flush_all!
end
end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index b817f6b71e..96a07844e5 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -1,5 +1,6 @@
require 'rails/initializable'
require 'rails/configuration'
+require 'active_support/inflector'
module Rails
# Railtie is the core of the Rails Framework and provides several hooks to extend
@@ -193,17 +194,16 @@ module Rails
end
def railtie_name(railtie_name = nil)
- @railtie_name ||= name.demodulize.underscore
@railtie_name = railtie_name if railtie_name
- @railtie_name
+ @railtie_name ||= default_name
end
def railtie_names
subclasses.map { |p| p.railtie_name }
end
- def subscriber(subscriber)
- Rails::Subscriber.add(railtie_name, subscriber)
+ def log_subscriber(log_subscriber)
+ Rails::LogSubscriber.add(railtie_name, log_subscriber)
end
def rake_tasks(&blk)
@@ -223,6 +223,10 @@ module Rails
def abstract_railtie?(base)
ABSTRACT_RAILTIES.include?(base.name)
end
+
+ def default_name
+ ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
+ end
end
def rake_tasks
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index bfb43f7041..828ccec3d0 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -3,7 +3,123 @@ require 'rails/configuration'
module Rails
class Railtie
class Configuration
- include Rails::Configuration::Shared
+ attr_accessor :cookie_secret
+
+ def initialize
+ @session_store = :cookie_store
+ @session_options = {}
+ end
+
+ # Holds generators configuration:
+ #
+ # config.generators do |g|
+ # g.orm :datamapper, :migration => true
+ # g.template_engine :haml
+ # g.test_framework :rspec
+ # end
+ #
+ # If you want to disable color in console, do:
+ #
+ # config.generators.colorize_logging = false
+ #
+ def generators
+ @@generators ||= Rails::Configuration::Generators.new
+ if block_given?
+ yield @@generators
+ else
+ @@generators
+ end
+ end
+
+ def after_initialize_blocks
+ @@after_initialize_blocks ||= []
+ end
+
+ def after_initialize(&blk)
+ after_initialize_blocks << blk if blk
+ end
+
+ def to_prepare_blocks
+ @@to_prepare_blocks ||= []
+ end
+
+ def to_prepare(&blk)
+ to_prepare_blocks << blk if blk
+ end
+
+ def respond_to?(name)
+ super || name.to_s =~ config_key_regexp
+ end
+
+ def metal_loader
+ @metal_loader ||= Rails::Application::MetalLoader.new
+ end
+
+ def session_store(*args)
+ if args.empty?
+ case @session_store
+ when :disabled
+ nil
+ when :active_record_store
+ ActiveRecord::SessionStore
+ when Symbol
+ ActionDispatch::Session.const_get(@session_store.to_s.camelize)
+ else
+ @session_store
+ end
+ else
+ @session_store = args.shift
+ @session_options = args.shift || {}
+ end
+ end
+
+ private
+
+ def method_missing(name, *args, &blk)
+ if name.to_s =~ config_key_regexp
+ return $2 == '=' ? options[$1] = args.first : options[$1]
+ end
+ super
+ end
+
+ def session_options
+ return @session_options unless @session_store == :cookie_store
+ @session_options.merge(:secret => @cookie_secret)
+ end
+
+ def config_key_regexp
+ bits = config_keys.map { |n| Regexp.escape(n.to_s) }.join('|')
+ /^(#{bits})(?:=)?$/
+ end
+
+ def config_keys
+ (Railtie.railtie_names + Engine.engine_names).map { |n| n.to_s }.uniq
+ end
+
+ def options
+ @@options ||= Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new }
+ end
+
+ def default_middleware
+ require 'action_dispatch'
+ ActionDispatch::MiddlewareStack.new.tap do |middleware|
+ middleware.use('::ActionDispatch::Static', lambda { Rails.public_path }, :if => lambda { serve_static_assets })
+ middleware.use('::Rack::Lock', :if => lambda { !allow_concurrency })
+ middleware.use('::Rack::Runtime')
+ middleware.use('::Rails::Rack::Logger')
+ middleware.use('::ActionDispatch::ShowExceptions', lambda { consider_all_requests_local })
+ middleware.use("::ActionDispatch::RemoteIp", lambda { action_dispatch.ip_spoofing_check }, lambda { action_dispatch.trusted_proxies })
+ middleware.use('::Rack::Sendfile', lambda { action_dispatch.x_sendfile_header })
+ middleware.use('::ActionDispatch::Callbacks', lambda { !cache_classes })
+ middleware.use('::ActionDispatch::Cookies')
+ middleware.use(lambda { session_store }, lambda { session_options })
+ middleware.use('::ActionDispatch::Flash', :if => lambda { session_store })
+ middleware.use(lambda { metal_loader.build_middleware(metals) }, :if => lambda { metal_loader.metals.any? })
+ middleware.use('ActionDispatch::ParamsParser')
+ middleware.use('::Rack::MethodOverride')
+ middleware.use('::ActionDispatch::Head')
+ end
+ end
end
end
end \ No newline at end of file
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index f7cc6ff4be..abf9b33ae5 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -11,44 +11,54 @@ namespace :doc do
rdoc.rdoc_files.include('lib/**/*.rb')
}
- desc 'Generate documentation for the Rails framework. Specify path with PATH="/path/to/rails"'
- Rake::RDocTask.new("rails") { |rdoc|
- path = ENV['RAILS_PATH'] || 'vendor/gems/gems'
- version = '-3.0.0.beta1' unless ENV['RAILS_PATH']
- rdoc.rdoc_dir = 'doc/api'
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
- rdoc.title = "Rails Framework Documentation"
- rdoc.options << '--line-numbers' << '--inline-source'
- rdoc.rdoc_files.include('README')
-
- %w(README CHANGELOG lib/action_mailer/base.rb).each do |file|
- rdoc.rdoc_files.include("#{path}/actionmailer#{version}/#{file}")
+ desc 'Generate documentation for the Rails framework. Specify path with RAILS_PATH="/path/to/rails"'
+ path = ENV['RAILS_PATH']
+ unless path && File.directory?(path)
+ task :rails do
+ if path
+ $stderr.puts "Skipping doc:rails, missing Rails directory at #{path}"
+ else
+ $stderr.puts "Skipping doc:rails, RAILS_PATH environment variable is not set"
+ end
end
+ else
+ Rake::RDocTask.new("rails") { |rdoc|
+ version = "-#{Rails::VERSION::STRING}" unless ENV['RAILS_PATH']
+ rdoc.rdoc_dir = 'doc/api'
+ rdoc.template = "#{ENV['template']}.rb" if ENV['template']
+ rdoc.title = "Rails Framework Documentation"
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+
+ %w(README CHANGELOG lib/action_mailer/base.rb).each do |file|
+ rdoc.rdoc_files.include("#{path}/actionmailer#{version}/#{file}")
+ end
- %w(README CHANGELOG lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file|
- rdoc.rdoc_files.include("#{path}/actionpack#{version}/#{file}")
- end
+ %w(README CHANGELOG lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file|
+ rdoc.rdoc_files.include("#{path}/actionpack#{version}/#{file}")
+ end
- %w(README CHANGELOG lib/active_model/**/*.rb).each do |file|
- rdoc.rdoc_files.include("#{path}/activemodel#{version}/#{file}")
- end
+ %w(README CHANGELOG lib/active_model/**/*.rb).each do |file|
+ rdoc.rdoc_files.include("#{path}/activemodel#{version}/#{file}")
+ end
- %w(README CHANGELOG lib/active_record/**/*.rb).each do |file|
- rdoc.rdoc_files.include("#{path}/activerecord#{version}/#{file}")
- end
+ %w(README CHANGELOG lib/active_record/**/*.rb).each do |file|
+ rdoc.rdoc_files.include("#{path}/activerecord#{version}/#{file}")
+ end
- %w(README CHANGELOG lib/active_resource.rb lib/active_resource/*).each do |file|
- rdoc.rdoc_files.include("#{path}/activeresource#{version}/#{file}")
- end
+ %w(README CHANGELOG lib/active_resource.rb lib/active_resource/*).each do |file|
+ rdoc.rdoc_files.include("#{path}/activeresource#{version}/#{file}")
+ end
- %w(README CHANGELOG lib/active_support/**/*.rb).each do |file|
- rdoc.rdoc_files.include("#{path}/activesupport#{version}/#{file}")
- end
+ %w(README CHANGELOG lib/active_support/**/*.rb).each do |file|
+ rdoc.rdoc_files.include("#{path}/activesupport#{version}/#{file}")
+ end
- %w(README CHANGELOG MIT-LICENSE lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file|
- rdoc.rdoc_files.include("#{path}/railties#{version}/#{file}")
- end
- }
+ %w(README CHANGELOG MIT-LICENSE lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file|
+ rdoc.rdoc_files.include("#{path}/railties#{version}/#{file}")
+ end
+ }
+ end
plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) }
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index 65d3c48f2d..dbe2ac54ed 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -32,12 +32,18 @@ namespace :rails do
namespace :update do
def invoke_from_app_generator(method)
- require 'rails/generators'
- require 'generators/rails/app/app_generator'
+ app_generator.invoke(method)
+ end
- generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true },
- :destination_root => Rails.root
- generator.invoke(method)
+ def app_generator
+ @app_generator ||= begin
+ require 'rails/generators'
+ require 'generators/rails/app/app_generator'
+ gen = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true },
+ :destination_root => Rails.root
+ gen.send(:valid_app_const?)
+ gen
+ end
end
desc "Update config/boot.rb from your current rails install"
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index ac0f440896..42e01d5e51 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -1,9 +1,9 @@
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task :routes => :environment do
Rails::Application.reload_routes!
- all_routes = ENV['CONTROLLER'] ? ActionController::Routing::Routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : ActionController::Routing::Routes.routes
+ all_routes = ENV['CONTROLLER'] ? Rails.application.routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : Rails.application.routes.routes
routes = all_routes.collect do |route|
- name = ActionController::Routing::Routes.named_routes.routes.index(route).to_s
+ name = Rails.application.routes.named_routes.routes.index(route).to_s
reqs = route.requirements.empty? ? "" : route.requirements.inspect
{:name => name, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
end
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index f9aa018cab..2ed5353755 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -1,6 +1,6 @@
# Make double-sure the RAILS_ENV is set to test,
# so fixtures are loaded to the right database
-exit("Abort testing: Your Rails environment is not running in test mode!") unless Rails.env.test?
+abort("Abort testing: Your Rails environment is not running in test mode!") unless Rails.env.test?
require 'test/unit'
require 'active_support/core_ext/kernel/requires'
@@ -24,6 +24,16 @@ if defined?(ActiveRecord)
end
end
+class ActionController::TestCase
+ setup do
+ @router = Rails.application.routes
+ end
+end
+
+class ActionDispatch::IntegrationTest
+ include Rails.application.routes.url_helpers
+end
+
begin
require_library_or_gem 'ruby-debug'
Debugger.start
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index d0c7cb45db..1dd8fa0ec7 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -2,8 +2,9 @@ module Rails
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
end
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 65d2dde89f..b9d2739539 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -1,9 +1,11 @@
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
+
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'railties'
- s.version = '3.0.0.beta1'
- s.summary = 'Controls boot-up, rake tasks and generators for the Rails framework.'
- s.description = 'Controls boot-up, rake tasks and generators for the Rails framework.'
+ s.version = version
+ s.summary = 'Tools for creating, working with, and running Rails applications.'
+ s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.'
s.required_ruby_version = '>= 1.8.7'
s.author = 'David Heinemeier Hansson'
@@ -21,7 +23,7 @@ Gem::Specification.new do |s|
s.has_rdoc = false
s.add_dependency('rake', '>= 0.8.3')
- s.add_dependency('thor', '~> 0.13')
- s.add_dependency('activesupport', '= 3.0.0.beta1')
- s.add_dependency('actionpack', '= 3.0.0.beta1')
+ s.add_dependency('thor', '~> 0.13.4')
+ s.add_dependency('activesupport', version)
+ s.add_dependency('actionpack', version)
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 56f45582c8..54cd751f4e 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -171,5 +171,62 @@ module ApplicationTests
get "/"
assert $prepared
end
+
+ test "config.action_dispatch.x_sendfile_header defaults to X-Sendfile" do
+ require "rails"
+ require "action_controller/railtie"
+
+ class MyApp < Rails::Application
+ config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4"
+ config.session_store :cookie_store, :key => "_myapp_session"
+ end
+
+ MyApp.initialize!
+
+ class ::OmgController < ActionController::Base
+ def index
+ send_file __FILE__
+ end
+ end
+
+ MyApp.routes.draw do
+ match "/" => "omg#index"
+ end
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Sendfile"]
+ end
+
+ test "config.action_dispatch.x_sendfile_header is sent to Rack::Sendfile" do
+ require "rails"
+ require "action_controller/railtie"
+
+ class MyApp < Rails::Application
+ config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4"
+ config.session_store :cookie_store, :key => "_myapp_session"
+ config.action_dispatch.x_sendfile_header = 'X-Lighttpd-Send-File'
+ end
+
+ MyApp.initialize!
+
+ class ::OmgController < ActionController::Base
+ def index
+ send_file __FILE__
+ end
+ end
+
+ MyApp.routes.draw do
+ match "/" => "omg#index"
+ end
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"]
+ end
end
end
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 22ab60f4a0..8ff69f0208 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -6,7 +6,9 @@ class ConsoleTest < Test::Unit::TestCase
def setup
build_app
boot_rails
+ end
+ def load_environment
# Load steps taken from rails/commands/console.rb
require "#{rails_root}/config/environment"
require 'rails/console/app'
@@ -14,18 +16,21 @@ class ConsoleTest < Test::Unit::TestCase
end
def test_app_method_should_return_integration_session
+ load_environment
console_session = app
assert_not_nil console_session
assert_instance_of ActionController::Integration::Session, console_session
end
def test_new_session_should_return_integration_session
+ load_environment
session = new_session
assert_not_nil session
assert_instance_of ActionController::Integration::Session, session
end
def test_reload_should_fire_preparation_callbacks
+ load_environment
a = b = c = nil
# TODO: These should be defined on the initializer
@@ -34,16 +39,37 @@ class ConsoleTest < Test::Unit::TestCase
ActionDispatch::Callbacks.to_prepare { c = 3 }
# Hide Reloading... output
- silence_stream(STDOUT) do
- reload!
- end
+ silence_stream(STDOUT) { reload! }
assert_equal 1, a
assert_equal 2, b
assert_equal 3, c
end
+ def test_reload_should_reload_constants
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ attr_accessor :name
+ end
+ MODEL
+
+ load_environment
+ assert User.new.respond_to?(:name)
+ assert !User.new.respond_to?(:age)
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ attr_accessor :name, :age
+ end
+ MODEL
+
+ assert !User.new.respond_to?(:age)
+ silence_stream(STDOUT) { reload! }
+ assert User.new.respond_to?(:age)
+ end
+
def test_access_to_helpers
+ load_environment
assert_not_nil helper
assert_instance_of ActionView::Base, helper
assert_equal 'Once upon a time in a world...',
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 1e7b9c9997..8e57022e5b 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -32,6 +32,17 @@ module ApplicationTests
ActionMailer::Base.view_paths.include?(File.expand_path("app/views", app_path))
end
+ test "allows me to configure default url options for ActionMailer" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails::Application.configure do
+ config.action_mailer.default_url_options = { :host => "test.rails" }
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert "test.rails", ActionMailer::Base.default_url_options[:host]
+ end
+
# AS
test "if there's no config.active_support.bare, all of ActiveSupport is required" do
use_frameworks []
@@ -54,7 +65,7 @@ module ApplicationTests
test "database middleware doesn't initialize when session store is not active_record" do
add_to_config <<-RUBY
config.root = "#{app_path}"
- config.action_controller.session_store = :cookie_store
+ config.session_store :cookie_store, { :key => "blahblahblah" }
RUBY
require "#{app_path}/config/environment"
@@ -62,7 +73,7 @@ module ApplicationTests
end
test "database middleware initializes when session store is active record" do
- add_to_config "config.action_controller.session_store = :active_record_store"
+ add_to_config "config.session_store :active_record_store"
require "#{app_path}/config/environment"
@@ -80,7 +91,7 @@ module ApplicationTests
test "database middleware doesn't initialize when activerecord is not in frameworks" do
use_frameworks []
require "#{app_path}/config/environment"
- assert_nil defined?(ActiveRecord)
+ assert_nil defined?(ActiveRecord::Base)
end
end
end
diff --git a/railties/test/application/initializers/initializers_test.rb b/railties/test/application/initializers/initializers_test.rb
index 0c3de7ce33..2e6a707175 100644
--- a/railties/test/application/initializers/initializers_test.rb
+++ b/railties/test/application/initializers/initializers_test.rb
@@ -51,5 +51,31 @@ module ApplicationTests
assert $activerecord_configurations
assert $activerecord_configurations['development']
end
+
+ test "after_initialize happens after to_prepare in development" do
+ $order = []
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.after_initialize { $order << :after_initialize }
+ config.to_prepare { $order << :to_prepare }
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert [:to_prepare, :after_initialize], $order
+ end
+
+ test "after_initialize happens after to_prepare in production" do
+ $order = []
+ add_to_config <<-RUBY
+ config.cache_classes = true
+ config.after_initialize { $order << :after_initialize }
+ config.to_prepare { $order << :to_prepare }
+ RUBY
+
+ require "#{app_path}/config/application"
+ Rails.env.replace "production"
+ require "#{app_path}/config/environment"
+ assert [:to_prepare, :after_initialize], $order
+ end
end
end
diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb
index 061bb34c19..b99cf5bb4f 100644
--- a/railties/test/application/initializers/notifications_test.rb
+++ b/railties/test/application/initializers/notifications_test.rb
@@ -28,7 +28,7 @@ module ApplicationTests
ActiveSupport::Notifications.notifier.wait
end
- test "rails subscribers are added" do
+ test "rails log_subscribers are added" do
add_to_config <<-RUBY
config.colorize_logging = false
RUBY
diff --git a/railties/test/application/metal_test.rb b/railties/test/application/metal_test.rb
index 225bede117..1ec62282c8 100644
--- a/railties/test/application/metal_test.rb
+++ b/railties/test/application/metal_test.rb
@@ -28,7 +28,7 @@ module ApplicationTests
end
RUBY
- get "/"
+ get "/not/slash"
assert_equal 200, last_response.status
assert_equal "FooMetal", last_response.body
end
@@ -50,7 +50,7 @@ module ApplicationTests
end
RUBY
- get "/"
+ get "/not/slash"
assert_equal 200, last_response.status
assert_equal "Metal B", last_response.body
end
diff --git a/railties/test/application/middleware_stack_defaults_test.rb b/railties/test/application/middleware_stack_defaults_test.rb
new file mode 100644
index 0000000000..284f7e2e5b
--- /dev/null
+++ b/railties/test/application/middleware_stack_defaults_test.rb
@@ -0,0 +1,54 @@
+require 'isolation/abstract_unit'
+
+class MiddlewareStackDefaultsTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ boot_rails
+ require "rails"
+ require "action_controller/railtie"
+
+ Object.const_set(:MyApplication, Class.new(Rails::Application))
+ MyApplication.class_eval do
+ config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4"
+ config.session_store :cookie_store, :key => "_myapp_session"
+ end
+ end
+
+ def remote_ip(env = {})
+ remote_ip = nil
+ env = Rack::MockRequest.env_for("/").merge(env).merge('action_dispatch.show_exceptions' => false)
+
+ endpoint = Proc.new do |e|
+ remote_ip = ActionDispatch::Request.new(e).remote_ip
+ [200, {}, ["Hello"]]
+ end
+
+ out = MyApplication.middleware.build(endpoint).call(env)
+ remote_ip
+ end
+
+ test "remote_ip works" do
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1")
+ end
+
+ test "checks IP spoofing by default" do
+ assert_raises(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "can disable IP spoofing check" do
+ MyApplication.config.action_dispatch.ip_spoofing_check = false
+
+ assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ assert_equal "1.1.1.2", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "the user can set trusted proxies" do
+ MyApplication.config.action_dispatch.trusted_proxies = /^4\.2\.42\.42$/
+
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "4.2.42.42,1.1.1.1")
+ end
+end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index ce9cd510a3..9a359d20b1 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -19,6 +19,8 @@ module ApplicationTests
"Rack::Runtime",
"Rails::Rack::Logger",
"ActionDispatch::ShowExceptions",
+ "ActionDispatch::RemoteIp",
+ "Rack::Sendfile",
"ActionDispatch::Callbacks",
"ActionDispatch::Cookies",
"ActionDispatch::Session::CookieStore",
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
index ac0aa27c64..511b8b629a 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -11,8 +11,8 @@ module ApplicationTests
app_file "config/environments/development.rb", ""
add_to_config <<-RUBY
config.root = "#{app_path}"
- config.after_initialize do
- ActionController::Base.session_store = nil
+ config.after_initialize do |app|
+ app.config.session_store nil
end
RUBY
use_frameworks [:action_controller, :action_view, :action_mailer, :active_record]
@@ -56,9 +56,10 @@ module ApplicationTests
end
test "booting up Rails yields a list of paths that are eager" do
- assert @paths.app.eager_load?
- assert @paths.app.controllers.eager_load?
- assert @paths.app.helpers.eager_load?
+ eager_load = @paths.eager_load
+ assert eager_load.include?(root("app/controllers"))
+ assert eager_load.include?(root("app/helpers"))
+ assert eager_load.include?(root("app/models"))
end
test "environments has a glob equal to the current environment" do
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index b93e349a46..dcac1a87d9 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -120,7 +120,8 @@ module ApplicationTests
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do |map|
- match ':controller(/:action)'
+ match 'admin/foo', :to => 'admin/foo#index'
+ match 'foo', :to => 'foo#index'
end
RUBY
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
new file mode 100644
index 0000000000..04f5454e09
--- /dev/null
+++ b/railties/test/application/url_generation_test.rb
@@ -0,0 +1,43 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class UrlGenerationTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def app
+ Rails.application
+ end
+
+ test "it works" do
+ boot_rails
+ require "rails"
+ require "action_controller/railtie"
+
+ class MyApp < Rails::Application
+ config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4"
+ config.session_store :cookie_store, :key => "_myapp_session"
+ end
+
+ MyApp.initialize!
+
+ class ::ApplicationController < ActionController::Base
+ end
+
+ class ::OmgController < ::ApplicationController
+ def index
+ render :text => omg_path
+ end
+ end
+
+ MyApp.routes.draw do
+ match "/" => "omg#index", :as => :omg
+ end
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/"
+ assert_equal "/", last_response.body
+ end
+ end
+end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 5929db6318..3585e6e7c0 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -210,6 +210,12 @@ class ActionsTest < Rails::Generators::TestCase
assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/
end
+ def test_readme
+ run_generator
+ Rails::Generators::AppGenerator.expects(:source_root).returns(destination_root)
+ assert_match(/Welcome to Rails/, action(:readme, "README"))
+ end
+
protected
def action(*args, &block)
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 0a746b200f..412034029e 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -50,6 +50,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
).each{ |path| assert_file path }
end
+ def test_name_collision_raises_an_error
+ content = capture(:stderr){ run_generator [File.join(destination_root, "generate")] }
+ assert_equal "Invalid application name generate. Please give a name which does not match one of the reserved rails words.\n", content
+ end
+
def test_invalid_database_option_raises_an_error
content = capture(:stderr){ run_generator([destination_root, "-d", "unknown"]) }
assert_match /Invalid value for \-\-database option/, content
@@ -102,6 +107,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_prototype_and_test_unit_are_skipped_if_required
run_generator [destination_root, "--skip-prototype", "--skip-testunit"]
assert_no_file "public/javascripts/prototype.js"
+ assert_file "public/javascripts"
assert_no_file "test"
end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 33cc27bd84..dd17f8f756 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -145,16 +145,17 @@ class GeneratorsTest < Rails::Generators::TestCase
end
def test_developer_options_are_overwriten_by_user_options
- Rails::Generators.options[:new_generator] = { :generate => false }
+ Rails::Generators.options[:with_options] = { :generate => false }
- klass = Class.new(Rails::Generators::Base) do
- def self.name() 'NewGenerator' end
- class_option :generate, :default => true
- end
+ self.class.class_eval <<-end_eval
+ class WithOptionsGenerator < Rails::Generators::Base
+ class_option :generate, :default => true
+ end
+ end_eval
- assert_equal false, klass.class_options[:generate].default
+ assert_equal false, WithOptionsGenerator.class_options[:generate].default
ensure
- Rails::Generators.subclasses.delete(klass)
+ Rails::Generators.subclasses.delete(WithOptionsGenerator)
end
def test_load_generators_from_railties
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 364dbd8e55..8f2f15b49e 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -100,7 +100,7 @@ module TestHelpers
end
end
- add_to_config 'config.action_controller.session = { :key => "_myapp_session", :secret => "bac838a849c1d5c4de2e6a50af826079" }'
+ add_to_config 'config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"'
end
class Bukkit
diff --git a/railties/test/log_subscriber_test.rb b/railties/test/log_subscriber_test.rb
new file mode 100644
index 0000000000..49288cfaa8
--- /dev/null
+++ b/railties/test/log_subscriber_test.rb
@@ -0,0 +1,123 @@
+require 'abstract_unit'
+require 'rails/log_subscriber/test_helper'
+
+class MyLogSubscriber < Rails::LogSubscriber
+ attr_reader :event
+
+ def some_event(event)
+ @event = event
+ info event.name
+ end
+
+ def foo(event)
+ debug "debug"
+ info "info"
+ warn "warn"
+ end
+
+ def bar(event)
+ info "#{color("cool", :red)}, #{color("isn't it?", :blue, true)}"
+ end
+
+ def puke(event)
+ raise "puke"
+ end
+end
+
+class SyncLogSubscriberTest < ActiveSupport::TestCase
+ include Rails::LogSubscriber::TestHelper
+
+ def setup
+ super
+ @log_subscriber = MyLogSubscriber.new
+ end
+
+ def teardown
+ super
+ Rails::LogSubscriber.log_subscribers.clear
+ end
+
+ def instrument(*args, &block)
+ ActiveSupport::Notifications.instrument(*args, &block)
+ end
+
+ def test_proxies_method_to_rails_logger
+ @log_subscriber.foo(nil)
+ assert_equal %w(debug), @logger.logged(:debug)
+ assert_equal %w(info), @logger.logged(:info)
+ assert_equal %w(warn), @logger.logged(:warn)
+ end
+
+ def test_set_color_for_messages
+ Rails::LogSubscriber.colorize_logging = true
+ @log_subscriber.bar(nil)
+ assert_equal "\e[31mcool\e[0m, \e[1m\e[34misn't it?\e[0m", @logger.logged(:info).last
+ end
+
+ def test_does_not_set_color_if_colorize_logging_is_set_to_false
+ @log_subscriber.bar(nil)
+ assert_equal "cool, isn't it?", @logger.logged(:info).last
+ end
+
+ def test_event_is_sent_to_the_registered_class
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ instrument "my_log_subscriber.some_event"
+ wait
+ assert_equal %w(my_log_subscriber.some_event), @logger.logged(:info)
+ end
+
+ def test_event_is_an_active_support_notifications_event
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ instrument "my_log_subscriber.some_event"
+ wait
+ assert_kind_of ActiveSupport::Notifications::Event, @log_subscriber.event
+ end
+
+ def test_does_not_send_the_event_if_it_doesnt_match_the_class
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ instrument "my_log_subscriber.unknown_event"
+ wait
+ # If we get here, it means that NoMethodError was not raised.
+ end
+
+ def test_does_not_send_the_event_if_logger_is_nil
+ Rails.logger = nil
+ @log_subscriber.expects(:some_event).never
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ instrument "my_log_subscriber.some_event"
+ wait
+ end
+
+ def test_does_not_fail_with_non_namespaced_events
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ instrument "whatever"
+ wait
+ end
+
+ def test_flushes_loggers
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ Rails::LogSubscriber.flush_all!
+ assert_equal 1, @logger.flush_count
+ end
+
+ def test_flushes_the_same_logger_just_once
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ Rails::LogSubscriber.add :another, @log_subscriber
+ Rails::LogSubscriber.flush_all!
+ wait
+ assert_equal 1, @logger.flush_count
+ end
+
+ def test_logging_does_not_die_on_failures
+ Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber
+ instrument "my_log_subscriber.puke"
+ instrument "my_log_subscriber.some_event"
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_equal 'my_log_subscriber.some_event', @logger.logged(:info).last
+
+ assert_equal 1, @logger.logged(:error).size
+ assert_equal 'Could not log "my_log_subscriber.puke" event. RuntimeError: puke', @logger.logged(:error).last
+ end
+end \ No newline at end of file
diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb
index 4163fb2c6d..d904d7b461 100644
--- a/railties/test/rails_info_controller_test.rb
+++ b/railties/test/rails_info_controller_test.rb
@@ -14,10 +14,13 @@ class InfoControllerTest < ActionController::TestCase
tests Rails::InfoController
def setup
- ActionController::Routing::Routes.draw do |map|
- match ':controller/:action'
+ Rails.application.routes.draw do |map|
+ match '/rails/info/properties' => "rails/info#properties"
end
@controller.stubs(:consider_all_requests_local? => false, :local_request? => true)
+ @router = Rails.application.routes
+
+ Rails::InfoController.send(:include, @router.url_helpers)
end
test "info controller does not allow remote requests" do
diff --git a/railties/test/railties/plugin_test.rb b/railties/test/railties/plugin_test.rb
index 09b859dcdd..0f5f29468c 100644
--- a/railties/test/railties/plugin_test.rb
+++ b/railties/test/railties/plugin_test.rb
@@ -94,6 +94,15 @@ module RailtiesTest
assert rescued, "Expected boot rails to fail"
end
+ test "loads deprecated rails/init.rb" do
+ @plugin.write "rails/init.rb", <<-RUBY
+ $loaded = true
+ RUBY
+
+ boot_rails
+ assert $loaded
+ end
+
test "deprecated tasks are also loaded" do
$executed = false
@plugin.write "tasks/foo.rake", <<-RUBY
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index b723e08281..9fefb285b4 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -51,12 +51,12 @@ module RailtiesTest
assert_equal "bar", Bar.config.foo.bar
end
- test "railtie can add subscribers" do
+ test "railtie can add log subscribers" do
begin
- class Foo < Rails::Railtie ; subscriber(Rails::Subscriber.new) ; end
- assert_kind_of Rails::Subscriber, Rails::Subscriber.subscribers[:foo]
+ class Foo < Rails::Railtie ; log_subscriber(Rails::LogSubscriber.new) ; end
+ assert_kind_of Rails::LogSubscriber, Rails::LogSubscriber.log_subscribers[0]
ensure
- Rails::Subscriber.subscribers.clear
+ Rails::LogSubscriber.log_subscribers.clear
end
end
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index d51a0d153c..83d25be5db 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -133,7 +133,7 @@ module RailtiesTest
end
end
- ActionController::Routing::Routes.draw do
+ Rails.application.routes.draw do
match "/sprokkit", :to => Sprokkit
end
RUBY
@@ -170,7 +170,7 @@ module RailtiesTest
RUBY
@plugin.write "config/routes.rb", <<-RUBY
- ActionController::Routing::Routes.draw do |map|
+ Rails.application.routes.draw do |map|
match 'foo', :to => 'bar#index'
match 'bar', :to => 'bar#index'
end
@@ -254,22 +254,24 @@ YAML
require 'rack/test'
extend Rack::Test::Methods
- get "/"
+ get "/not/slash"
assert_equal 200, last_response.status
assert_equal "FooMetal", last_response.body
end
def test_namespaced_controllers_with_namespaced_routes
@plugin.write "config/routes.rb", <<-RUBY
- ActionController::Routing::Routes.draw do
+ Rails.application.routes.draw do
namespace :admin do
- match "index", :to => "admin/foo#index"
+ namespace :foo do
+ match "bar", :to => "admin/foo/bar#index"
+ end
end
end
RUBY
- @plugin.write "app/controllers/admin/foo_controller.rb", <<-RUBY
- class Admin::FooController < ApplicationController
+ @plugin.write "app/controllers/admin/foo/bar_controller.rb", <<-RUBY
+ class Admin::Foo::BarController < ApplicationController
def index
render :text => "Rendered from namespace"
end
@@ -280,7 +282,7 @@ YAML
require 'rack/test'
extend Rack::Test::Methods
- get "/admin/index"
+ get "/admin/foo/bar"
assert_equal 200, last_response.status
assert_equal "Rendered from namespace", last_response.body
end
@@ -312,4 +314,4 @@ YAML
boot_rails
end
end
-end \ No newline at end of file
+end
diff --git a/railties/test/subscriber_test.rb b/railties/test/subscriber_test.rb
deleted file mode 100644
index f6c895093f..0000000000
--- a/railties/test/subscriber_test.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-require 'abstract_unit'
-require 'rails/subscriber/test_helper'
-
-class MySubscriber < Rails::Subscriber
- attr_reader :event
-
- def some_event(event)
- @event = event
- info event.name
- end
-
- def foo(event)
- debug "debug"
- info "info"
- warn "warn"
- end
-
- def bar(event)
- info "#{color("cool", :red)}, #{color("isn't it?", :blue, true)}"
- end
-
- def puke(event)
- raise "puke"
- end
-end
-
-class SyncSubscriberTest < ActiveSupport::TestCase
- include Rails::Subscriber::TestHelper
-
- def setup
- super
- @subscriber = MySubscriber.new
- Rails::Subscriber.instance_variable_set(:@log_tailer, nil)
- end
-
- def teardown
- super
- Rails::Subscriber.subscribers.clear
- Rails::Subscriber.instance_variable_set(:@log_tailer, nil)
- end
-
- def instrument(*args, &block)
- ActiveSupport::Notifications.instrument(*args, &block)
- end
-
- def test_proxies_method_to_rails_logger
- @subscriber.foo(nil)
- assert_equal %w(debug), @logger.logged(:debug)
- assert_equal %w(info), @logger.logged(:info)
- assert_equal %w(warn), @logger.logged(:warn)
- end
-
- def test_set_color_for_messages
- Rails::Subscriber.colorize_logging = true
- @subscriber.bar(nil)
- assert_equal "\e[31mcool\e[0m, \e[1m\e[34misn't it?\e[0m", @logger.logged(:info).last
- end
-
- def test_does_not_set_color_if_colorize_logging_is_set_to_false
- @subscriber.bar(nil)
- assert_equal "cool, isn't it?", @logger.logged(:info).last
- end
-
- def test_event_is_sent_to_the_registered_class
- Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "my_subscriber.some_event"
- wait
- assert_equal %w(my_subscriber.some_event), @logger.logged(:info)
- end
-
- def test_event_is_an_active_support_notifications_event
- Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "my_subscriber.some_event"
- wait
- assert_kind_of ActiveSupport::Notifications::Event, @subscriber.event
- end
-
- def test_does_not_send_the_event_if_it_doesnt_match_the_class
- Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "my_subscriber.unknown_event"
- wait
- # If we get here, it means that NoMethodError was raised.
- end
-
- def test_does_not_send_the_event_if_logger_is_nil
- Rails.logger = nil
- @subscriber.expects(:some_event).never
- Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "my_subscriber.some_event"
- wait
- end
-
- def test_flushes_loggers
- Rails::Subscriber.add :my_subscriber, @subscriber
- Rails::Subscriber.flush_all!
- assert_equal 1, @logger.flush_count
- end
-
- def test_flushes_the_same_logger_just_once
- Rails::Subscriber.add :my_subscriber, @subscriber
- Rails::Subscriber.add :another, @subscriber
- Rails::Subscriber.flush_all!
- wait
- assert_equal 1, @logger.flush_count
- end
-
- def test_logging_does_not_die_on_failures
- Rails::Subscriber.add :my_subscriber, @subscriber
- instrument "my_subscriber.puke"
- instrument "my_subscriber.some_event"
- wait
-
- assert_equal 1, @logger.logged(:info).size
- assert_equal 'my_subscriber.some_event', @logger.logged(:info).last
-
- assert_equal 1, @logger.logged(:error).size
- assert_equal 'Could not log "my_subscriber.puke" event. RuntimeError: puke', @logger.logged(:error).last
- end
-end \ No newline at end of file
diff --git a/release.rb b/release.rb
deleted file mode 100755
index e8e6c01c35..0000000000
--- a/release.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env ruby
-
-VERSION = ARGV.first
-PACKAGES = %w(activesupport activerecord actionpack actionmailer activeresource)
-
-# Copy source
-`mkdir release`
-(PACKAGES + %w(railties)).each do |p|
- `cp -R #{p} release/#{p}`
-end
-
-# Create Rails packages
-`cd release/railties && rake template=jamis package`
-
-# Upload documentation
-`cd release/rails/doc/api && scp -r * davidhh@wrath.rubyonrails.com:public_html/api`
-
-# Upload packages
-(PACKAGES + %w(railties)).each do |p|
- `cd release/#{p} && echo "Releasing #{p}" && rake release`
-end
-
-# Upload rails tgz/zip
-`rubyforge add_release rails rails 'REL #{VERSION}' release/rails-#{VERSION}.tgz`
-`rubyforge add_release rails rails 'REL #{VERSION}' release/rails-#{VERSION}.zip` \ No newline at end of file
diff --git a/version.rb b/version.rb
new file mode 100644
index 0000000000..1dd8fa0ec7
--- /dev/null
+++ b/version.rb
@@ -0,0 +1,10 @@
+module Rails
+ module VERSION #:nodoc:
+ MAJOR = 3
+ MINOR = 0
+ TINY = 0
+ BUILD = "beta1"
+
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ end
+end