aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Peek <josh@joshpeek.com>2009-05-29 16:06:21 -0500
committerJoshua Peek <josh@joshpeek.com>2009-05-29 16:06:21 -0500
commit69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3 (patch)
tree044c2131cc87d21ee54027511aae2b7f2d2ae26a
parent5f3f100ce2d689480da85abc88e5e940cf90189e (diff)
parent5ec2c7dc29b36d85b2658465b8a979deb0529d7e (diff)
downloadrails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.tar.gz
rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.tar.bz2
rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.zip
Merge branch 'master' into active_model
Conflicts: activemodel/lib/active_model/core.rb activemodel/test/cases/state_machine/event_test.rb activemodel/test/cases/state_machine/state_transition_test.rb activerecord/lib/active_record/validations.rb activerecord/test/cases/validations/i18n_validation_test.rb activeresource/lib/active_resource.rb activeresource/test/abstract_unit.rb
-rw-r--r--Rakefile8
-rw-r--r--actionmailer/Rakefile6
-rw-r--r--actionmailer/lib/action_mailer/base.rb58
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb1
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail.rb1
-rw-r--r--actionmailer/test/adv_attr_test.rb (renamed from actionpack/test/adv_attr_test.rb)6
-rw-r--r--actionmailer/test/fixtures/auto_layout_mailer/multipart.html.erb (renamed from actionmailer/test/fixtures/auto_layout_mailer/multipart.text.html.erb)0
-rw-r--r--actionmailer/test/fixtures/auto_layout_mailer/multipart.text.erb (renamed from actionmailer/test/fixtures/auto_layout_mailer/multipart.text.plain.erb)0
-rw-r--r--actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb2
-rw-r--r--actionmailer/test/fixtures/test_mailer/_subtemplate.text.erb (renamed from actionmailer/test/fixtures/test_mailer/_subtemplate.text.plain.erb)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/custom_templating_extension.html.haml (renamed from actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.html.haml)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.haml (renamed from actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.html.erb (renamed from actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.html.erb~ (renamed from actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.erb (renamed from actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.yaml.erb (renamed from actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/included_subtemplate.text.erb (renamed from actionmailer/test/fixtures/test_mailer/included_subtemplate.text.plain.erb)0
-rw-r--r--actionmailer/test/fixtures/test_mailer/rxml_template.builder2
-rw-r--r--actionmailer/test/mail_service_test.rb25
-rw-r--r--actionpack/CHANGELOG10
-rw-r--r--actionpack/Rakefile44
-rw-r--r--actionpack/examples/minimal.rb58
-rw-r--r--actionpack/lib/action_controller.rb15
-rw-r--r--actionpack/lib/action_controller/abstract.rb6
-rw-r--r--actionpack/lib/action_controller/abstract/base.rb124
-rw-r--r--actionpack/lib/action_controller/abstract/benchmarker.rb28
-rw-r--r--actionpack/lib/action_controller/abstract/callbacks.rb45
-rw-r--r--actionpack/lib/action_controller/abstract/exceptions.rb4
-rw-r--r--actionpack/lib/action_controller/abstract/helpers.rb51
-rw-r--r--actionpack/lib/action_controller/abstract/layouts.rb87
-rw-r--r--actionpack/lib/action_controller/abstract/logger.rb42
-rw-r--r--actionpack/lib/action_controller/abstract/renderer.rb69
-rw-r--r--actionpack/lib/action_controller/base/base.rb145
-rw-r--r--actionpack/lib/action_controller/base/chained/benchmarking.rb4
-rw-r--r--actionpack/lib/action_controller/base/chained/filters.rb9
-rw-r--r--actionpack/lib/action_controller/base/chained/flash.rb73
-rw-r--r--actionpack/lib/action_controller/base/cookies.rb2
-rw-r--r--actionpack/lib/action_controller/base/filter_parameter_logging.rb97
-rw-r--r--actionpack/lib/action_controller/base/helpers.rb22
-rw-r--r--actionpack/lib/action_controller/base/http_authentication.rb9
-rw-r--r--actionpack/lib/action_controller/base/layout.rb6
-rw-r--r--actionpack/lib/action_controller/base/mime_responds.rb212
-rw-r--r--actionpack/lib/action_controller/base/redirect.rb8
-rw-r--r--actionpack/lib/action_controller/base/render.rb19
-rw-r--r--actionpack/lib/action_controller/base/request_forgery_protection.rb24
-rw-r--r--actionpack/lib/action_controller/base/rescue.rb50
-rw-r--r--actionpack/lib/action_controller/base/streaming.rb8
-rw-r--r--actionpack/lib/action_controller/base/verification.rb9
-rw-r--r--actionpack/lib/action_controller/caching.rb30
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb30
-rw-r--r--actionpack/lib/action_controller/dispatch/dispatcher.rb109
-rw-r--r--actionpack/lib/action_controller/dispatch/middlewares.rb11
-rw-r--r--actionpack/lib/action_controller/dispatch/rescue.rb185
-rw-r--r--actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb10
-rw-r--r--actionpack/lib/action_controller/new_base.rb52
-rw-r--r--actionpack/lib/action_controller/new_base/base.rb225
-rw-r--r--actionpack/lib/action_controller/new_base/compatibility.rb138
-rw-r--r--actionpack/lib/action_controller/new_base/conditional_get.rb133
-rw-r--r--actionpack/lib/action_controller/new_base/helpers.rb129
-rw-r--r--actionpack/lib/action_controller/new_base/hide_actions.rb54
-rw-r--r--actionpack/lib/action_controller/new_base/http.rb91
-rw-r--r--actionpack/lib/action_controller/new_base/layouts.rb45
-rw-r--r--actionpack/lib/action_controller/new_base/rack_convenience.rb33
-rw-r--r--actionpack/lib/action_controller/new_base/redirector.rb19
-rw-r--r--actionpack/lib/action_controller/new_base/render_options.rb103
-rw-r--r--actionpack/lib/action_controller/new_base/renderer.rb108
-rw-r--r--actionpack/lib/action_controller/new_base/rescuable.rb52
-rw-r--r--actionpack/lib/action_controller/new_base/session.rb15
-rw-r--r--actionpack/lib/action_controller/new_base/testing.rb39
-rw-r--r--actionpack/lib/action_controller/new_base/url_for.rb15
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb2
-rw-r--r--actionpack/lib/action_controller/routing.rb4
-rw-r--r--actionpack/lib/action_controller/routing/builder.rb2
-rw-r--r--actionpack/lib/action_controller/routing/route.rb8
-rw-r--r--actionpack/lib/action_controller/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb2
-rw-r--r--actionpack/lib/action_controller/testing/assertions/response.rb150
-rw-r--r--actionpack/lib/action_controller/testing/integration.rb540
-rw-r--r--actionpack/lib/action_controller/testing/process.rb416
-rw-r--r--actionpack/lib/action_controller/testing/process2.rb74
-rw-r--r--actionpack/lib/action_controller/testing/test_case.rb15
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb2
-rw-r--r--actionpack/lib/action_dispatch.rb35
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb3
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb2
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb77
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb78
-rw-r--r--actionpack/lib/action_dispatch/http/status_codes.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb40
-rw-r--r--actionpack/lib/action_dispatch/middleware/failsafe.rb52
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb14
-rw-r--r--actionpack/lib/action_dispatch/middleware/reloader.rb14
-rw-r--r--actionpack/lib/action_dispatch/middleware/rescue.rb14
-rw-r--r--actionpack/lib/action_dispatch/middleware/rewindable_input.rb19
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb41
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb141
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb)6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb)0
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb)4
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb)0
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb)0
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb)6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb (renamed from actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb)0
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/dom.rb (renamed from actionpack/lib/action_controller/testing/assertions/dom.rb)22
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/model.rb (renamed from actionpack/lib/action_controller/testing/assertions/model.rb)6
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb145
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb (renamed from actionpack/lib/action_controller/testing/assertions/routing.rb)44
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb (renamed from actionpack/lib/action_controller/testing/assertions/selector.rb)2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/tag.rb (renamed from actionpack/lib/action_controller/testing/assertions/tag.rb)18
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb83
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb131
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.0/rack/reloader.rb64
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack.rb)7
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/adapter/camping.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/adapter/camping.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/abstract/handler.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/request.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/abstract/request.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/basic.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/basic.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/md5.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/md5.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/nonce.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/params.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/params.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/request.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/request.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/openid.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/openid.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/builder.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/builder.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/cascade.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/cascade.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/chunked.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/chunked.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/commonlogger.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/commonlogger.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/conditionalget.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/conditionalget.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_length.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/content_length.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_type.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/content_type.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/deflater.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/deflater.rb)71
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/directory.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/directory.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/file.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/file.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler.rb)23
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/cgi.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/cgi.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/evented_mongrel.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/fastcgi.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/fastcgi.rb)47
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/lsws.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/lsws.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/mongrel.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/mongrel.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/scgi.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/scgi.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/thin.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/thin.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/webrick.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/webrick.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/head.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/head.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lint.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lint.rb)127
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lobster.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lobster.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lock.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lock.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/methodoverride.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/methodoverride.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mime.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/mime.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mock.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/mock.rb)30
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/recursive.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/recursive.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb106
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/request.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/request.rb)39
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/response.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/response.rb)6
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb98
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/abstract/id.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/cookie.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/cookie.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/memcache.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/memcache.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/pool.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/pool.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showexceptions.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/showexceptions.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showstatus.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/showstatus.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/static.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/static.rb)0
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/urlmap.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/urlmap.rb)2
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/utils.rb (renamed from actionpack/lib/action_dispatch/vendor/rack-1.0/rack/utils.rb)158
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb50
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb239
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb169
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb45
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb27
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb36
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb75
-rw-r--r--actionpack/lib/action_view.rb13
-rw-r--r--actionpack/lib/action_view/base.rb33
-rw-r--r--actionpack/lib/action_view/helpers/active_record_helper.rb1
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb20
-rw-r--r--actionpack/lib/action_view/paths.rb20
-rw-r--r--actionpack/lib/action_view/render/partials.rb37
-rw-r--r--actionpack/lib/action_view/render/rendering.rb22
-rw-r--r--actionpack/lib/action_view/template/error.rb4
-rw-r--r--actionpack/lib/action_view/template/handler.rb6
-rw-r--r--actionpack/lib/action_view/template/handlers.rb8
-rw-r--r--actionpack/lib/action_view/template/handlers/builder.rb2
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb9
-rw-r--r--actionpack/lib/action_view/template/handlers/rjs.rb6
-rw-r--r--actionpack/lib/action_view/template/path.rb173
-rw-r--r--actionpack/lib/action_view/template/template.rb227
-rw-r--r--actionpack/lib/action_view/template/text.rb16
-rw-r--r--actionpack/lib/action_view/test_case.rb14
-rw-r--r--actionpack/test/abstract_controller/abstract_controller_test.rb57
-rw-r--r--actionpack/test/abstract_controller/callbacks_test.rb39
-rw-r--r--actionpack/test/abstract_controller/helper_test.rb6
-rw-r--r--actionpack/test/abstract_controller/layouts_test.rb31
-rw-r--r--actionpack/test/abstract_controller/test_helper.rb12
-rw-r--r--actionpack/test/abstract_unit.rb8
-rw-r--r--actionpack/test/activerecord/active_record_store_test.rb29
-rw-r--r--actionpack/test/activerecord/polymorphic_routes_test.rb1
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb79
-rw-r--r--actionpack/test/controller/addresses_render_test.rb1
-rw-r--r--actionpack/test/controller/base_test.rb12
-rw-r--r--actionpack/test/controller/caching_test.rb44
-rw-r--r--actionpack/test/controller/capture_test.rb1
-rw-r--r--actionpack/test/controller/content_type_test.rb13
-rw-r--r--actionpack/test/controller/cookie_test.rb47
-rw-r--r--actionpack/test/controller/deprecation/deprecated_base_methods_test.rb6
-rw-r--r--actionpack/test/controller/dispatcher_test.rb47
-rw-r--r--actionpack/test/controller/filter_params_test.rb45
-rw-r--r--actionpack/test/controller/filters_test.rb251
-rw-r--r--actionpack/test/controller/flash_test.rb71
-rw-r--r--actionpack/test/controller/helper_test.rb44
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb36
-rw-r--r--actionpack/test/controller/integration_test.rb45
-rw-r--r--actionpack/test/controller/layout_test.rb64
-rw-r--r--actionpack/test/controller/logging_test.rb9
-rw-r--r--actionpack/test/controller/mime_responds_test.rb48
-rw-r--r--actionpack/test/controller/redirect_test.rb5
-rw-r--r--actionpack/test/controller/render_js_test.rb39
-rw-r--r--actionpack/test/controller/render_json_test.rb80
-rw-r--r--actionpack/test/controller/render_other_test.rb238
-rw-r--r--actionpack/test/controller/render_test.rb422
-rw-r--r--actionpack/test/controller/render_xml_test.rb81
-rw-r--r--actionpack/test/controller/request/test_request_test.rb23
-rw-r--r--actionpack/test/controller/rescue_test.rb360
-rw-r--r--actionpack/test/controller/resources_test.rb8
-rw-r--r--actionpack/test/controller/routing_test.rb14
-rw-r--r--actionpack/test/controller/selector_test.rb1
-rw-r--r--actionpack/test/controller/send_file_test.rb41
-rw-r--r--actionpack/test/controller/test_test.rb41
-rw-r--r--actionpack/test/controller/verification_test.rb16
-rw-r--r--actionpack/test/controller/view_paths_test.rb51
-rw-r--r--actionpack/test/controller/webservice_test.rb4
-rw-r--r--actionpack/test/dispatch/rack_test.rb90
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb64
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb38
-rw-r--r--actionpack/test/dispatch/request_test.rb2
-rw-r--r--actionpack/test/dispatch/response_test.rb130
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb6
-rw-r--r--actionpack/test/dispatch/session/mem_cache_store_test.rb8
-rw-r--r--actionpack/test/dispatch/session/test_session_test.rb24
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb108
-rw-r--r--actionpack/test/dispatch/test_request_test.rb45
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab2
-rw-r--r--actionpack/test/fixtures/layouts/standard.html.erb (renamed from actionpack/test/fixtures/layouts/standard.erb)0
-rw-r--r--actionpack/test/fixtures/test/greeting.html.erb (renamed from actionpack/test/fixtures/test/greeting.erb)0
-rw-r--r--actionpack/test/fixtures/test/render_file_with_locals_and_default.erb1
-rw-r--r--actionpack/test/fixtures/test/utf8.html.erb5
-rw-r--r--actionpack/test/html-scanner/cdata_node_test.rb (renamed from actionpack/test/controller/html-scanner/cdata_node_test.rb)0
-rw-r--r--actionpack/test/html-scanner/document_test.rb (renamed from actionpack/test/controller/html-scanner/document_test.rb)0
-rw-r--r--actionpack/test/html-scanner/node_test.rb (renamed from actionpack/test/controller/html-scanner/node_test.rb)0
-rw-r--r--actionpack/test/html-scanner/sanitizer_test.rb (renamed from actionpack/test/controller/html-scanner/sanitizer_test.rb)0
-rw-r--r--actionpack/test/html-scanner/tag_node_test.rb (renamed from actionpack/test/controller/html-scanner/tag_node_test.rb)0
-rw-r--r--actionpack/test/html-scanner/text_node_test.rb (renamed from actionpack/test/controller/html-scanner/text_node_test.rb)0
-rw-r--r--actionpack/test/html-scanner/tokenizer_test.rb (renamed from actionpack/test/controller/html-scanner/tokenizer_test.rb)0
-rw-r--r--actionpack/test/lib/active_record_unit.rb (renamed from actionpack/test/active_record_unit.rb)6
-rw-r--r--actionpack/test/lib/controller/fake_controllers.rb (renamed from actionpack/test/controller/fake_controllers.rb)0
-rw-r--r--actionpack/test/lib/controller/fake_models.rb (renamed from actionpack/test/controller/fake_models.rb)0
-rw-r--r--actionpack/test/lib/fixture_template.rb115
-rw-r--r--actionpack/test/lib/testing_sandbox.rb (renamed from actionpack/test/testing_sandbox.rb)0
-rw-r--r--actionpack/test/new_base/abstract_unit.rb166
-rw-r--r--actionpack/test/new_base/base_test.rb103
-rw-r--r--actionpack/test/new_base/content_type_test.rb111
-rw-r--r--actionpack/test/new_base/etag_test.rb46
-rw-r--r--actionpack/test/new_base/redirect_test.rb1
-rw-r--r--actionpack/test/new_base/render_action_test.rb417
-rw-r--r--actionpack/test/new_base/render_file_test.rb110
-rw-r--r--actionpack/test/new_base/render_implicit_action_test.rb30
-rw-r--r--actionpack/test/new_base/render_layout_test.rb106
-rw-r--r--actionpack/test/new_base/render_partial_test.rb27
-rw-r--r--actionpack/test/new_base/render_template_test.rb211
-rw-r--r--actionpack/test/new_base/render_test.rb85
-rw-r--r--actionpack/test/new_base/render_text_test.rb197
-rw-r--r--actionpack/test/new_base/render_xml_test.rb11
-rw-r--r--actionpack/test/new_base/test_helper.rb126
-rw-r--r--actionpack/test/template/body_parts_test.rb11
-rw-r--r--actionpack/test/template/compiled_templates_test.rb36
-rw-r--r--actionpack/test/template/form_options_helper_test.rb22
-rw-r--r--actionpack/test/template/javascript_helper_test.rb4
-rw-r--r--actionpack/test/template/number_helper_test.rb4
-rw-r--r--actionpack/test/template/output_buffer_test.rb67
-rw-r--r--actionpack/test/template/prototype_helper_test.rb20
-rw-r--r--actionpack/test/template/render_test.rb36
-rw-r--r--actionpack/test/template/text_helper_test.rb10
-rwxr-xr-xactivemodel/Rakefile6
-rw-r--r--activemodel/lib/active_model.rb14
-rw-r--r--activemodel/lib/active_model/errors.rb3
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb28
-rw-r--r--activerecord/CHANGELOG2
-rw-r--r--activerecord/Rakefile26
-rw-r--r--activerecord/examples/simple.rb14
-rw-r--r--activerecord/lib/active_record.rb13
-rw-r--r--activerecord/lib/active_record/aggregations.rb4
-rw-r--r--activerecord/lib/active_record/association_preload.rb8
-rwxr-xr-xactiverecord/lib/active_record/associations.rb52
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb17
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb14
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb17
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb16
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb30
-rw-r--r--activerecord/lib/active_record/autosave_association.rb27
-rwxr-xr-xactiverecord/lib/active_record/base.rb42
-rw-r--r--activerecord/lib/active_record/batches.rb4
-rw-r--r--activerecord/lib/active_record/calculations.rb5
-rw-r--r--activerecord/lib/active_record/callbacks.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb4
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb1
-rw-r--r--activerecord/lib/active_record/dirty.rb25
-rw-r--r--activerecord/lib/active_record/fixtures.rb48
-rw-r--r--activerecord/lib/active_record/i18n_interpolation_deprecation.rb26
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb16
-rw-r--r--activerecord/lib/active_record/migration.rb10
-rw-r--r--activerecord/lib/active_record/named_scope.rb27
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb23
-rw-r--r--activerecord/lib/active_record/observer.rb4
-rw-r--r--activerecord/lib/active_record/reflection.rb25
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/serialization.rb25
-rw-r--r--activerecord/lib/active_record/serializers/json_serializer.rb22
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb5
-rw-r--r--activerecord/lib/active_record/session_store.rb10
-rw-r--r--activerecord/lib/active_record/test_case.rb2
-rw-r--r--activerecord/lib/active_record/timestamp.rb12
-rw-r--r--activerecord/lib/active_record/transactions.rb12
-rw-r--r--activerecord/lib/active_record/validations.rb22
-rw-r--r--activerecord/test/cases/aaa_create_tables_test.rb24
-rw-r--r--activerecord/test/cases/aggregations_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb24
-rw-r--r--activerecord/test/cases/associations/eager_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb39
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb56
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb9
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb313
-rw-r--r--activerecord/test/cases/autosave_association_test.rb29
-rwxr-xr-xactiverecord/test/cases/base_test.rb7
-rw-r--r--activerecord/test/cases/calculations_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb23
-rw-r--r--activerecord/test/cases/fixtures_test.rb5
-rw-r--r--activerecord/test/cases/helper.rb23
-rw-r--r--activerecord/test/cases/json_serialization_test.rb112
-rw-r--r--activerecord/test/cases/method_scoping_test.rb18
-rw-r--r--activerecord/test/cases/named_scope_test.rb41
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb19
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb29
-rw-r--r--activerecord/test/cases/repair_helper.rb6
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb15
-rw-r--r--activerecord/test/fixtures/faces.yml7
-rw-r--r--activerecord/test/fixtures/interests.yml29
-rw-r--r--activerecord/test/fixtures/men.yml5
-rw-r--r--activerecord/test/fixtures/people.yml5
-rw-r--r--activerecord/test/fixtures/zines.yml5
-rw-r--r--activerecord/test/models/company_in_module.rb2
-rw-r--r--activerecord/test/models/developer.rb10
-rw-r--r--activerecord/test/models/face.rb5
-rw-r--r--activerecord/test/models/interest.rb4
-rw-r--r--activerecord/test/models/man.rb7
-rw-r--r--activerecord/test/models/person.rb1
-rw-r--r--activerecord/test/models/pirate.rb2
-rw-r--r--activerecord/test/models/zine.rb3
-rw-r--r--activerecord/test/schema/schema.rb27
-rw-r--r--activerecord/test/schema/schema2.rb6
-rw-r--r--activeresource/Rakefile10
-rw-r--r--activeresource/examples/simple.rb16
-rw-r--r--activeresource/lib/active_resource.rb17
-rw-r--r--activeresource/lib/active_resource/base.rb29
-rw-r--r--activeresource/lib/active_resource/connection.rb60
-rw-r--r--activeresource/lib/active_resource/custom_methods.rb4
-rw-r--r--activeresource/lib/active_resource/exceptions.rb55
-rw-r--r--activeresource/lib/active_resource/formats.rb8
-rw-r--r--activeresource/lib/active_resource/formats/json_format.rb2
-rw-r--r--activeresource/lib/active_resource/formats/xml_format.rb2
-rw-r--r--activeresource/lib/active_resource/http_mock.rb7
-rw-r--r--activeresource/lib/active_resource/validations.rb7
-rw-r--r--activeresource/test/abstract_unit.rb12
-rw-r--r--activeresource/test/base/custom_methods_test.rb1
-rw-r--r--activeresource/test/base/load_test.rb2
-rw-r--r--activeresource/test/base_test.rb3
-rw-r--r--activesupport/CHANGELOG21
-rw-r--r--activesupport/Rakefile6
-rw-r--r--activesupport/lib/active_support.rb38
-rw-r--r--activesupport/lib/active_support/all.rb3
-rw-r--r--activesupport/lib/active_support/autoload.rb26
-rw-r--r--activesupport/lib/active_support/cache.rb1
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb22
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/concern.rb22
-rw-r--r--activesupport/lib/active_support/core.rb7
-rw-r--r--activesupport/lib/active_support/core/all.rb4
-rw-r--r--activesupport/lib/active_support/core/time.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/class/removal.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/integer/even_odd.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/requires.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_internal.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/module/model_naming.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/setup.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/time.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/conversions.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/object/misc.rb83
-rw-r--r--activesupport/lib/active_support/core_ext/object/returning.rb42
-rw-r--r--activesupport/lib/active_support/core_ext/object/tap.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/range/blockless_step.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/string/iterators.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/string/multibyte.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/symbol.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/symbol/to_proc.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb6
-rw-r--r--activesupport/lib/active_support/dependencies.rb9
-rw-r--r--activesupport/lib/active_support/dependency_module.rb17
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb1
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb6
-rw-r--r--activesupport/lib/active_support/duration.rb1
-rw-r--r--activesupport/lib/active_support/inflector.rb3
-rw-r--r--activesupport/lib/active_support/json.rb72
-rw-r--r--activesupport/lib/active_support/json/backends/jsongem.rb41
-rw-r--r--activesupport/lib/active_support/json/backends/yaml.rb88
-rw-r--r--activesupport/lib/active_support/json/decoding.rb84
-rw-r--r--activesupport/lib/active_support/json/encoders/date.rb37
-rw-r--r--activesupport/lib/active_support/json/encoders/date_time.rb37
-rw-r--r--activesupport/lib/active_support/json/encoders/enumerable.rb21
-rw-r--r--activesupport/lib/active_support/json/encoders/false_class.rb7
-rw-r--r--activesupport/lib/active_support/json/encoders/hash.rb87
-rw-r--r--activesupport/lib/active_support/json/encoders/nil_class.rb7
-rw-r--r--activesupport/lib/active_support/json/encoders/numeric.rb7
-rw-r--r--activesupport/lib/active_support/json/encoders/object.rb9
-rw-r--r--activesupport/lib/active_support/json/encoders/regexp.rb7
-rw-r--r--activesupport/lib/active_support/json/encoders/string.rb38
-rw-r--r--activesupport/lib/active_support/json/encoders/symbol.rb7
-rw-r--r--activesupport/lib/active_support/json/encoders/time.rb39
-rw-r--r--activesupport/lib/active_support/json/encoders/true_class.rb7
-rw-r--r--activesupport/lib/active_support/json/encoding.rb10
-rw-r--r--activesupport/lib/active_support/json/variable.rb7
-rw-r--r--activesupport/lib/active_support/memoizable.rb3
-rw-r--r--activesupport/lib/active_support/multibyte.rb2
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb1
-rw-r--r--activesupport/lib/active_support/new_callbacks.rb56
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb14
-rw-r--r--activesupport/lib/active_support/ruby/shim.rb24
-rw-r--r--activesupport/lib/active_support/test_case.rb5
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb2
-rw-r--r--activesupport/lib/active_support/testing/deprecation.rb18
-rw-r--r--activesupport/lib/active_support/testing/pending.rb5
-rw-r--r--activesupport/lib/active_support/time.rb14
-rw-r--r--activesupport/lib/active_support/time/autoload.rb (renamed from activesupport/lib/active_support/core/time/autoload.rb)0
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb40
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb166
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb16
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb16
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb16
-rw-r--r--activesupport/test/buffered_logger_test.rb14
-rw-r--r--activesupport/test/caching_test.rb38
-rw-r--r--activesupport/test/concern_test.rb88
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb3
-rw-r--r--activesupport/test/core_ext/blank_test.rb1
-rw-r--r--activesupport/test/core_ext/class/attribute_accessor_test.rb1
-rw-r--r--activesupport/test/core_ext/class/class_inheritable_attributes_test.rb1
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb1
-rw-r--r--activesupport/test/core_ext/duplicable_test.rb2
-rw-r--r--activesupport/test/core_ext/duration_test.rb21
-rw-r--r--activesupport/test/core_ext/exception_test.rb1
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb18
-rw-r--r--activesupport/test/core_ext/module/attr_accessor_with_default_test.rb1
-rw-r--r--activesupport/test/core_ext/module/attr_internal_test.rb7
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_test.rb1
-rw-r--r--activesupport/test/core_ext/module/attribute_aliasing_test.rb1
-rw-r--r--activesupport/test/core_ext/module/model_naming_test.rb1
-rw-r--r--activesupport/test/core_ext/name_error_test.rb1
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb5
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/proc_test.rb1
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb1
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb1
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb9
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb7
-rw-r--r--activesupport/test/dependencies_test.rb3
-rw-r--r--activesupport/test/i18n_test.rb2
-rw-r--r--activesupport/test/inflector_test.rb2
-rw-r--r--activesupport/test/json/decoding_test.rb82
-rw-r--r--activesupport/test/json/encoding_test.rb38
-rw-r--r--activesupport/test/new_callbacks_test.rb112
-rw-r--r--activesupport/test/ordered_hash_test.rb10
-rw-r--r--activesupport/test/test_test.rb1
-rw-r--r--activesupport/test/time_zone_test.rb1
-rw-r--r--activesupport/test/xml_mini/jdom_engine_test.rb153
-rw-r--r--activesupport/test/xml_mini/nokogiri_engine_test.rb14
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb14
-rw-r--r--ci/ci_setup_notes.txt2
-rw-r--r--ci/cruise_config.rb2
-rw-r--r--ci/geminstaller.yml4
-rw-r--r--railties/CHANGELOG7
-rw-r--r--railties/Rakefile19
-rw-r--r--railties/builtin/rails_info/rails/info.rb10
-rw-r--r--railties/configs/routes.rb2
-rw-r--r--railties/configs/seeds.rb7
-rw-r--r--railties/lib/commands/performance/profiler.rb2
-rw-r--r--railties/lib/commands/server.rb9
-rw-r--r--railties/lib/console_app.rb5
-rw-r--r--railties/lib/initializer.rb51
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb3
-rw-r--r--railties/lib/rails/gem_dependency.rb21
-rw-r--r--railties/lib/rails/plugin.rb9
-rw-r--r--railties/lib/rails/plugin/loader.rb12
-rw-r--r--railties/lib/rails/plugin/locator.rb2
-rw-r--r--railties/lib/rails/rack/metal.rb4
-rw-r--r--railties/lib/rails_generator.rb13
-rw-r--r--railties/lib/rails_generator/generators/applications/app/app_generator.rb5
-rw-r--r--railties/lib/rails_generator/generators/applications/app/template_runner.rb2
-rw-r--r--railties/lib/rails_generator/generators/components/model/templates/model.rb2
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/USAGE13
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb32
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb3
-rw-r--r--railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb8
-rw-r--r--railties/lib/rails_generator/scripts.rb2
-rw-r--r--railties/lib/tasks/databases.rake13
-rw-r--r--railties/lib/tasks/gems.rake23
-rw-r--r--railties/lib/tasks/misc.rake6
-rw-r--r--railties/test/boot_test.rb1
-rw-r--r--railties/test/gem_dependency_test.rb45
-rw-r--r--railties/test/generators/rails_model_subclass_generator_test.rb15
-rw-r--r--railties/test/initializer_test.rb23
-rw-r--r--railties/test/plugin_loader_test.rb4
-rw-r--r--railties/test/plugin_locator_test.rb6
-rw-r--r--railties/test/plugin_test_helper.rb6
-rw-r--r--railties/test/rails_info_test.rb2
-rw-r--r--railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification29
-rw-r--r--railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification41
-rw-r--r--railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile0
-rw-r--r--railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification41
-rw-r--r--railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification49
-rw-r--r--railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb1
-rwxr-xr-xtools/profile75
570 files changed, 11404 insertions, 5763 deletions
diff --git a/Rakefile b/Rakefile
index 8f4c891074..fbb7f2213c 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,6 +1,5 @@
require 'rake'
require 'rake/rdoctask'
-require 'rake/contrib/sshpublisher'
env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
@@ -13,12 +12,14 @@ end
desc 'Run all tests by default'
task :default => :test
-%w(test rdoc pgem package release).each do |task_name|
+%w(test isolated_test rdoc pgem package release).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})
+ system(%(cd #{project} && #{env} #{$0} #{task_name})) || errors << project
end
+ fail("Errors in #{errors.join(', ')}") unless errors.empty?
end
end
@@ -74,6 +75,7 @@ end
desc "Publish API docs for Rails as a whole and for each component"
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)
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index f06f18ff76..24c25abc8b 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -28,6 +28,12 @@ Rake::TestTask.new { |t|
t.warning = false
}
+task :isolated_test do
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ Dir.glob("test/*_test.rb").all? do |file|
+ system(ruby, '-Ilib:test', file)
+ end or raise "Failures"
+end
# Generate the RDoc documentation
Rake::RDocTask.new { |rdoc|
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 9eee5783a0..af2cc2ee24 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -465,48 +465,48 @@ module ActionMailer #:nodoc:
def create!(method_name, *parameters) #:nodoc:
initialize_defaults(method_name)
__send__(method_name, *parameters)
-
+
# If an explicit, textual body has not been set, we check assumptions.
unless String === @body
# First, we look to see if there are any likely templates that match,
# which include the content-type in their file name (i.e.,
# "the_template_file.text.html.erb", etc.). Only do this if parts
# have not already been specified manually.
- if @parts.empty?
- Dir.glob("#{template_path}/#{@template}.*").each do |path|
- template = template_root.find_by_parts("#{mailer_name}/#{File.basename(path)}")
-
- # Skip unless template has a multipart format
- next unless template && template.multipart?
-
+ # if @parts.empty?
+ template_root.find_all_by_parts(@template, {}, template_path).each do |template|
@parts << Part.new(
- :content_type => template.content_type,
+ :content_type => template.mime_type ? template.mime_type.to_s : "text/plain",
:disposition => "inline",
:charset => charset,
:body => render_template(template, @body)
)
end
- unless @parts.empty?
+
+ if @parts.size > 1
@content_type = "multipart/alternative" if @content_type !~ /^multipart/
@parts = sort_parts(@parts, @implicit_parts_order)
end
- end
-
+ # end
+
# Then, if there were such templates, we check to see if we ought to
# also render a "normal" template (without the content type). If a
# normal template exists (or if there were no implicit parts) we render
# it.
- template_exists = @parts.empty?
- template_exists ||= template_root.find_by_parts("#{mailer_name}/#{@template}")
- @body = render_message(@template, @body) if template_exists
+ # ====
+ # TODO: Revisit this
+ # template_exists = @parts.empty?
+ # template_exists ||= template_root.find_by_parts("#{mailer_name}/#{@template}")
+ # @body = render_message(@template, @body) if template_exists
# Finally, if there are other message parts and a textual body exists,
# we shift it onto the front of the parts and set the body to nil (so
# that create_mail doesn't try to render it in addition to the parts).
- if !@parts.empty? && String === @body
- @parts.unshift Part.new(:charset => charset, :body => @body)
- @body = nil
- end
+ # ====
+ # TODO: Revisit this
+ # if !@parts.empty? && String === @body
+ # @parts.unshift Part.new(:charset => charset, :body => @body)
+ # @body = nil
+ # end
end
# If this is a multipart e-mail add the mime_version if it is not
@@ -555,12 +555,13 @@ module ActionMailer #:nodoc:
end
def render_template(template, body)
- if template.respond_to?(:content_type)
- @current_template_content_type = template.content_type
+ if template.respond_to?(:mime_type)
+ @current_template_content_type = template.mime_type && template.mime_type.to_sym.to_s
end
@template = initialize_template_class(body)
- layout = _pick_layout(layout, true) unless template.exempt_from_layout?
+ layout = _pick_layout(layout, true) unless
+ ActionController::Base.exempt_from_layout.include?(template.handler)
@template._render_template_with_layout(template, layout, {})
ensure
@current_template_content_type = nil
@@ -580,11 +581,11 @@ module ActionMailer #:nodoc:
if file
prefix = mailer_name unless file =~ /\//
- template = view_paths.find_by_parts(file, formats, prefix)
+ template = view_paths.find_by_parts(file, {:formats => formats}, prefix)
end
layout = _pick_layout(layout,
- !template || !template.exempt_from_layout?)
+ !template || ActionController::Base.exempt_from_layout.include?(template.handler))
if template
@template._render_template_with_layout(template, layout, opts)
@@ -611,7 +612,7 @@ module ActionMailer #:nodoc:
end
def template_path
- "#{template_root}/#{mailer_name}"
+ "#{mailer_name}"
end
def initialize_template_class(assigns)
@@ -622,7 +623,7 @@ module ActionMailer #:nodoc:
def sort_parts(parts, order = [])
order = order.collect { |s| s.downcase }
-
+
parts = parts.sort do |a, b|
a_ct = a.content_type.downcase
b_ct = b.content_type.downcase
@@ -663,10 +664,13 @@ module ActionMailer #:nodoc:
headers.each { |k, v| m[k] = v }
real_content_type, ctype_attrs = parse_content_type
-
+
if @parts.empty?
m.set_content_type(real_content_type, nil, ctype_attrs)
m.body = normalize_new_lines(body)
+ elsif @parts.size == 1 && @parts.first.parts.empty?
+ m.set_content_type(real_content_type, nil, ctype_attrs)
+ m.body = normalize_new_lines(@parts.first.body)
else
if String === body
part = TMail::Mail.new
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
index c3a8803dc4..23a3f75de3 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb
@@ -518,6 +518,7 @@ module TMail
def parse_body( f = nil )
return if @body_parsed
+
if f
parse_body_0 f
else
diff --git a/actionmailer/lib/action_mailer/vendor/tmail.rb b/actionmailer/lib/action_mailer/vendor/tmail.rb
index 51d36cdd0d..60555605f6 100644
--- a/actionmailer/lib/action_mailer/vendor/tmail.rb
+++ b/actionmailer/lib/action_mailer/vendor/tmail.rb
@@ -12,6 +12,7 @@ end
require 'tmail'
+require 'active_support/core_ext/kernel/reporting'
silence_warnings do
TMail::Encoder.const_set("MAX_LINE_LEN", 200)
end
diff --git a/actionpack/test/adv_attr_test.rb b/actionmailer/test/adv_attr_test.rb
index fdda4ad92d..fd909a5627 100644
--- a/actionpack/test/adv_attr_test.rb
+++ b/actionmailer/test/adv_attr_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/abstract_unit'
+require 'abstract_unit'
require 'action_mailer/adv_attr_accessor'
class AdvAttrTest < Test::Unit::TestCase
@@ -15,6 +15,4 @@ class AdvAttrTest < Test::Unit::TestCase
assert_raise(ArgumentError) {bob.name 'x', 'y'}
end
-
-
-end \ No newline at end of file
+end
diff --git a/actionmailer/test/fixtures/auto_layout_mailer/multipart.text.html.erb b/actionmailer/test/fixtures/auto_layout_mailer/multipart.html.erb
index 6d73f199c4..6d73f199c4 100644
--- a/actionmailer/test/fixtures/auto_layout_mailer/multipart.text.html.erb
+++ b/actionmailer/test/fixtures/auto_layout_mailer/multipart.html.erb
diff --git a/actionmailer/test/fixtures/auto_layout_mailer/multipart.text.plain.erb b/actionmailer/test/fixtures/auto_layout_mailer/multipart.text.erb
index f4b91e4031..f4b91e4031 100644
--- a/actionmailer/test/fixtures/auto_layout_mailer/multipart.text.plain.erb
+++ b/actionmailer/test/fixtures/auto_layout_mailer/multipart.text.erb
diff --git a/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb b/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb
index 897a5065cf..2d0cd5c124 100644
--- a/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb
+++ b/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb
@@ -1 +1 @@
-Have a lovely picture, from me. Enjoy! \ No newline at end of file
+Have some dots. Enjoy! \ No newline at end of file
diff --git a/actionmailer/test/fixtures/test_mailer/_subtemplate.text.plain.erb b/actionmailer/test/fixtures/test_mailer/_subtemplate.text.erb
index 3b4ba35f20..3b4ba35f20 100644
--- a/actionmailer/test/fixtures/test_mailer/_subtemplate.text.plain.erb
+++ b/actionmailer/test/fixtures/test_mailer/_subtemplate.text.erb
diff --git a/actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.html.haml b/actionmailer/test/fixtures/test_mailer/custom_templating_extension.html.haml
index 847d065c37..847d065c37 100644
--- a/actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.html.haml
+++ b/actionmailer/test/fixtures/test_mailer/custom_templating_extension.html.haml
diff --git a/actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml b/actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.haml
index 847d065c37..847d065c37 100644
--- a/actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml
+++ b/actionmailer/test/fixtures/test_mailer/custom_templating_extension.text.haml
diff --git a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.html.erb
index 946d99ede5..946d99ede5 100644
--- a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb
+++ b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.html.erb
diff --git a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.html.erb~
index 946d99ede5..946d99ede5 100644
--- a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~
+++ b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.html.erb~
diff --git a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.erb
index a6c8d54cf9..a6c8d54cf9 100644
--- a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb
+++ b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.erb
diff --git a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.yaml.erb
index c14348c770..c14348c770 100644
--- a/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb
+++ b/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.yaml.erb
diff --git a/actionmailer/test/fixtures/test_mailer/included_subtemplate.text.plain.erb b/actionmailer/test/fixtures/test_mailer/included_subtemplate.text.erb
index a93c30ea1a..a93c30ea1a 100644
--- a/actionmailer/test/fixtures/test_mailer/included_subtemplate.text.plain.erb
+++ b/actionmailer/test/fixtures/test_mailer/included_subtemplate.text.erb
diff --git a/actionmailer/test/fixtures/test_mailer/rxml_template.builder b/actionmailer/test/fixtures/test_mailer/rxml_template.builder
deleted file mode 100644
index d566bd8d7c..0000000000
--- a/actionmailer/test/fixtures/test_mailer/rxml_template.builder
+++ /dev/null
@@ -1,2 +0,0 @@
-xml.instruct!
-xml.test \ No newline at end of file
diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb
index b7d5fd5dd3..30d1b836c4 100644
--- a/actionmailer/test/mail_service_test.rb
+++ b/actionmailer/test/mail_service_test.rb
@@ -6,10 +6,10 @@ class FunkyPathMailer < ActionMailer::Base
def multipart_with_template_path_with_dots(recipient)
recipients recipient
- subject "Have a lovely picture"
+ subject "This path has dots"
from "Chad Fowler <chad@chadfowler.com>"
- attachment :content_type => "image/jpeg",
- :body => "not really a jpeg, we're only testing, after all"
+ attachment :content_type => "text/plain",
+ :body => "dots dots dots..."
end
end
@@ -338,6 +338,7 @@ class ActionMailerTest < Test::Unit::TestCase
def test_nested_parts_with_body
created = nil
+ TestMailer.create_nested_multipart_with_body(@recipient)
assert_nothing_raised { created = TestMailer.create_nested_multipart_with_body(@recipient)}
assert_equal 1,created.parts.size
assert_equal 2,created.parts.first.parts.size
@@ -351,8 +352,8 @@ class ActionMailerTest < Test::Unit::TestCase
def test_attachment_with_custom_header
created = nil
- assert_nothing_raised { created = TestMailer.create_attachment_with_custom_header(@recipient)}
- assert_equal "<test@test.com>", created.parts[1].header['content-id'].to_s
+ assert_nothing_raised { created = TestMailer.create_attachment_with_custom_header(@recipient) }
+ assert created.parts.any? { |p| p.header['content-id'].to_s == "<test@test.com>" }
end
def test_signed_up
@@ -824,7 +825,7 @@ EOF
assert_equal 3, mail.parts.length
assert_equal "1.0", mail.mime_version
assert_equal "multipart/alternative", mail.content_type
- assert_equal "text/yaml", mail.parts[0].content_type
+ assert_equal "application/x-yaml", mail.parts[0].content_type
assert_equal "utf-8", mail.parts[0].sub_header("content-type", "charset")
assert_equal "text/plain", mail.parts[1].content_type
assert_equal "utf-8", mail.parts[1].sub_header("content-type", "charset")
@@ -835,11 +836,11 @@ EOF
def test_implicitly_multipart_messages_with_custom_order
assert ActionView::Template.template_handler_extensions.include?("bak"), "bak extension was not registered"
- mail = TestMailer.create_implicitly_multipart_example(@recipient, nil, ["text/yaml", "text/plain"])
+ mail = TestMailer.create_implicitly_multipart_example(@recipient, nil, ["application/x-yaml", "text/plain"])
assert_equal 3, mail.parts.length
assert_equal "text/html", mail.parts[0].content_type
assert_equal "text/plain", mail.parts[1].content_type
- assert_equal "text/yaml", mail.parts[2].content_type
+ assert_equal "application/x-yaml", mail.parts[2].content_type
end
def test_implicitly_multipart_messages_with_charset
@@ -938,8 +939,8 @@ EOF
def test_multipart_with_template_path_with_dots
mail = FunkyPathMailer.create_multipart_with_template_path_with_dots(@recipient)
assert_equal 2, mail.parts.length
- assert_equal 'text/plain', mail.parts[0].content_type
- assert_equal 'utf-8', mail.parts[0].charset
+ assert "text/plain", mail.parts[1].content_type
+ assert "utf-8", mail.parts[1].charset
end
def test_custom_content_type_attributes
@@ -994,13 +995,13 @@ end
class InheritableTemplateRootTest < Test::Unit::TestCase
def test_attr
- expected = "#{File.dirname(__FILE__)}/fixtures/path.with.dots"
+ expected = File.expand_path("#{File.dirname(__FILE__)}/fixtures/path.with.dots")
assert_equal expected, FunkyPathMailer.template_root.to_s
sub = Class.new(FunkyPathMailer)
sub.template_root = 'test/path'
- assert_equal 'test/path', sub.template_root.to_s
+ assert_equal File.expand_path('test/path'), sub.template_root.to_s
assert_equal expected, FunkyPathMailer.template_root.to_s
end
end
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 204f5ae272..e758983d7a 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,15 @@
*Edge*
+* Ruby 1.9: ERB template encoding using a magic comment at the top of the file. [Jeremy Kemper]
+ <%# encoding: utf-8 %>
+
+* Change integration test helpers to accept Rack environment instead of just HTTP Headers [Pratik Naik]
+
+ Before : get '/path', {}, 'Accept' => 'text/javascript'
+ After : get '/path', {}, 'HTTP_ACCEPT' => 'text/javascript'
+
+* Instead of checking Rails.env.test? in Failsafe middleware, check env["rails.raise_exceptions"] [Bryan Helmkamp]
+
* Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes]
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 300c2ebf81..5f5614e58f 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require 'rake/contrib/sshpublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@@ -23,26 +22,59 @@ task :default => [ :test ]
# Run the unit tests
desc "Run all unit tests"
-task :test => [:test_action_pack, :test_active_record_integration]
+task :test => [:test_action_pack, :test_active_record_integration, :test_new_base, :test_new_base_on_old_tests]
+test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"]
Rake::TestTask.new(:test_action_pack) do |t|
- t.libs << "test"
+ t.libs.concat test_lib_dirs
# make sure we include the tests in alphabetical order as on some systems
# this will not happen automatically and the tests (as a whole) will error
- t.test_files = Dir.glob( "test/[cdft]*/**/*_test.rb" ).sort
+ t.test_files = Dir.glob( "test/{controller,dispatch,template,html-scanner}/**/*_test.rb" ).sort
t.verbose = true
#t.warning = true
end
+task :isolated_test do
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ Dir.glob("test/{controller,dispatch,template}/**/*_test.rb").all? do |file|
+ system(ruby, "-Ilib:#{test_lib_dirs * ':'}", file)
+ end or raise "Failures"
+end
desc 'ActiveRecord Integration Tests'
Rake::TestTask.new(:test_active_record_integration) do |t|
- t.libs << "test"
+ t.libs.concat test_lib_dirs
t.test_files = Dir.glob("test/activerecord/*_test.rb")
t.verbose = true
end
+desc 'New Controller Tests'
+Rake::TestTask.new(:test_new_base) do |t|
+ t.libs << "test/new_base" << "test/lib"
+ t.test_files = Dir.glob("test/{abstract_controller,new_base}/*_test.rb")
+ t.verbose = true
+end
+
+desc 'Old Controller Tests on New Base'
+Rake::TestTask.new(:test_new_base_on_old_tests) do |t|
+ t.libs << "test/new_base" << "test/lib"
+
+ t.verbose = true
+ # ==== Not ported
+ # * filters
+
+ t.test_files = Dir.glob( "test/{dispatch,template}/**/*_test.rb" ).sort + %w(
+ action_pack_assertions addresses_render assert_select
+ base benchmark caching capture content_type cookie dispatcher
+ filter_params flash helper http_basic_authentication
+ http_digest_authentication integration layout logging mime_responds
+ record_identifier redirect render render_js render_json
+ render_other render_xml request_forgery_protection rescue
+ resources routing selector send_file test url_rewriter
+ verification view_paths webservice
+ ).map { |name| "test/controller/#{name}_test.rb" }
+end
# Genereate the RDoc documentation
@@ -136,12 +168,14 @@ task :update_js => [ :update_scriptaculous ]
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
diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb
new file mode 100644
index 0000000000..9eb92cd8e7
--- /dev/null
+++ b/actionpack/examples/minimal.rb
@@ -0,0 +1,58 @@
+# Pass NEW=1 to run with the new Base
+ENV['RAILS_ENV'] ||= 'production'
+ENV['NO_RELOAD'] ||= '1'
+
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'action_controller'
+require 'action_controller/new_base' if ENV['NEW']
+require 'benchmark'
+
+class Runner
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env['n'].to_i.times { @app.call(env) }
+ @app.call(env).tap { |response| report(env, response) }
+ end
+
+ def report(env, response)
+ out = env['rack.errors']
+ out.puts response[0], response[1].to_yaml, '---'
+ response[2].each { |part| out.puts part }
+ out.puts '---'
+ end
+
+ def self.run(app, n, label = nil)
+ puts '=' * label.size, label, '=' * label.size if label
+ env = { 'n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout }
+ t = Benchmark.realtime { new(app).call(env) }
+ puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n]
+ puts
+ end
+end
+
+
+N = (ENV['N'] || 1000).to_i
+
+class BasePostController < ActionController::Base
+ def index
+ render :text => ''
+ end
+end
+
+OK = [200, {}, []]
+MetalPostController = lambda { OK }
+
+if ActionController.const_defined?(:Http)
+ class HttpPostController < ActionController::Http
+ def index
+ self.response_body = ''
+ end
+ end
+end
+
+Runner.run(MetalPostController, N, 'metal')
+Runner.run(HttpPostController.action(:index), N, 'http') if defined? HttpPostController
+Runner.run(BasePostController.action(:index), N, 'base')
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 100a0be1db..dd22bfd617 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -21,15 +21,9 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift activesupport_path if File.directory?(activesupport_path)
+require 'active_support'
require File.join(File.dirname(__FILE__), "action_pack")
@@ -59,7 +53,7 @@ module ActionController
autoload :Redirector, 'action_controller/base/redirect'
autoload :Renderer, 'action_controller/base/render'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
- autoload :Rescue, 'action_controller/dispatch/rescue'
+ autoload :Rescue, 'action_controller/base/rescue'
autoload :Resources, 'action_controller/routing/resources'
autoload :Responder, 'action_controller/base/responder'
autoload :Routing, 'action_controller/routing'
@@ -72,6 +66,7 @@ module ActionController
autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
autoload :Verification, 'action_controller/base/verification'
+ autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging'
module Assertions
autoload :DomAssertions, 'action_controller/testing/assertions/dom'
diff --git a/actionpack/lib/action_controller/abstract.rb b/actionpack/lib/action_controller/abstract.rb
index 3f5c4a185f..f46b91627f 100644
--- a/actionpack/lib/action_controller/abstract.rb
+++ b/actionpack/lib/action_controller/abstract.rb
@@ -1,5 +1,9 @@
+require "active_support/core_ext/module/attr_internal"
+require "active_support/core_ext/module/delegation"
+
module AbstractController
autoload :Base, "action_controller/abstract/base"
+ autoload :Benchmarker, "action_controller/abstract/benchmarker"
autoload :Callbacks, "action_controller/abstract/callbacks"
autoload :Helpers, "action_controller/abstract/helpers"
autoload :Layouts, "action_controller/abstract/layouts"
@@ -7,4 +11,4 @@ module AbstractController
autoload :Renderer, "action_controller/abstract/renderer"
# === Exceptions
autoload :ActionNotFound, "action_controller/abstract/exceptions"
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb
index ade7719cc0..87083a4d79 100644
--- a/actionpack/lib/action_controller/abstract/base.rb
+++ b/actionpack/lib/action_controller/abstract/base.rb
@@ -1,41 +1,115 @@
+require 'active_support/core_ext/module/attr_internal'
+
module AbstractController
+ class Error < StandardError; end
+
+ class DoubleRenderError < Error
+ DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
class Base
-
attr_internal :response_body
attr_internal :response_obj
attr_internal :action_name
-
- def self.process(action)
- new.process(action)
- end
-
- def self.inherited(klass)
+
+ class << self
+ attr_reader :abstract
+
+ def abstract!
+ @abstract = true
+ end
+
+ alias_method :abstract?, :abstract
+
+ def inherited(klass)
+ ::AbstractController::Base.subclasses << klass.to_s
+ super
+ end
+
+ def subclasses
+ @subclasses ||= []
+ end
+
+ def internal_methods
+ controller = self
+ controller = controller.superclass until controller.abstract?
+ controller.public_instance_methods(true)
+ end
+
+ def process(action)
+ new.process(action.to_s)
+ end
+
+ def hidden_actions
+ []
+ end
+
+ def action_methods
+ @action_methods ||=
+ # All public instance methods of this class, including ancestors
+ public_instance_methods(true).map { |m| m.to_s }.to_set -
+ # Except for public instance methods of Base and its ancestors
+ internal_methods.map { |m| m.to_s } +
+ # Be sure to include shadowed public instance methods of this class
+ public_instance_methods(false).map { |m| m.to_s } -
+ # And always exclude explicitly hidden actions
+ hidden_actions
+ end
end
-
+
+ abstract!
+
def initialize
self.response_obj = {}
end
-
- def process(action_name)
- unless respond_to_action?(action_name)
- raise ActionNotFound, "The action '#{action_name}' could not be found"
+
+ def process(action)
+ @_action_name = action_name = action.to_s
+
+ unless action_name = method_for_action(action_name)
+ raise ActionNotFound, "The action '#{action}' could not be found"
end
-
- @_action_name = action_name
- process_action
- self.response_obj[:body] = self.response_body
+
+ process_action(action_name)
self
end
-
+
private
-
- def process_action
- respond_to?(action_name) ? send(action_name) : send(:action_missing, action_name)
+ def action_methods
+ self.class.action_methods
+ end
+
+ def action_method?(action)
+ action_methods.include?(action)
end
-
- def respond_to_action?(action_name)
- respond_to?(action_name) || respond_to?(:action_missing, true)
+
+ # It is possible for respond_to?(action_name) to be false and
+ # respond_to?(:action_missing) to be false if respond_to_action?
+ # is overridden in a subclass. For instance, ActionController::Base
+ # overrides it to include the case where a template matching the
+ # action_name is found.
+ def process_action(method_name)
+ send_action(method_name)
+ end
+
+ alias send_action send
+
+ def _handle_action_missing
+ action_missing(@_action_name)
+ end
+
+ # Override this to change the conditions that will raise an
+ # ActionNotFound error. If you accept a difference case,
+ # you must handle it by also overriding process_action and
+ # handling the case.
+ def method_for_action(action_name)
+ if action_method?(action_name) then action_name
+ elsif respond_to?(:action_missing, true) then "_handle_action_missing"
+ end
end
-
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/abstract/benchmarker.rb b/actionpack/lib/action_controller/abstract/benchmarker.rb
new file mode 100644
index 0000000000..6999329144
--- /dev/null
+++ b/actionpack/lib/action_controller/abstract/benchmarker.rb
@@ -0,0 +1,28 @@
+module AbstractController
+ module Benchmarker
+ extend ActiveSupport::Concern
+
+ depends_on Logger
+
+ module ClassMethods
+ def benchmark(title, log_level = ::Logger::DEBUG, use_silence = true)
+ if logger && logger.level >= log_level
+ result = nil
+ ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
+ logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)")
+ result
+ else
+ yield
+ end
+ end
+
+ # Silences the logger for the duration of the block.
+ def silence
+ old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger
+ yield
+ ensure
+ logger.level = old_logger_level if logger
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb
index c8b509081c..6c67315c58 100644
--- a/actionpack/lib/action_controller/abstract/callbacks.rb
+++ b/actionpack/lib/action_controller/abstract/callbacks.rb
@@ -1,28 +1,31 @@
module AbstractController
module Callbacks
- setup do
- include ActiveSupport::NewCallbacks
- define_callbacks :process_action
+ extend ActiveSupport::Concern
+
+ depends_on ActiveSupport::NewCallbacks
+
+ included do
+ define_callbacks :process_action, "response_body"
end
-
- def process_action
- _run_process_action_callbacks(action_name) do
+
+ def process_action(method_name)
+ _run_process_action_callbacks(method_name) do
super
end
end
-
+
module ClassMethods
def _normalize_callback_options(options)
if only = options[:only]
- only = Array(only).map {|o| "action_name == :#{o}"}.join(" || ")
+ only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
options[:per_key] = {:if => only}
end
if except = options[:except]
- except = Array(except).map {|e| "action_name == :#{e}"}.join(" || ")
+ except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
options[:per_key] = {:unless => except}
end
end
-
+
[:before, :after, :around].each do |filter|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{filter}_filter(*names, &blk)
@@ -33,8 +36,28 @@ module AbstractController
process_action_callback(:#{filter}, name, options)
end
end
+
+ def prepend_#{filter}_filter(*names, &blk)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+ _normalize_callback_options(options)
+ names.push(blk) if block_given?
+ names.each do |name|
+ process_action_callback(:#{filter}, name, options.merge(:prepend => true))
+ end
+ end
+
+ def skip_#{filter}_filter(*names, &blk)
+ options = names.last.is_a?(Hash) ? names.pop : {}
+ _normalize_callback_options(options)
+ names.push(blk) if block_given?
+ names.each do |name|
+ skip_process_action_callback(:#{filter}, name, options)
+ end
+ end
+
+ alias_method :append_#{filter}_filter, :#{filter}_filter
RUBY_EVAL
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/abstract/exceptions.rb b/actionpack/lib/action_controller/abstract/exceptions.rb
index ec4680629b..2f6c55f068 100644
--- a/actionpack/lib/action_controller/abstract/exceptions.rb
+++ b/actionpack/lib/action_controller/abstract/exceptions.rb
@@ -1,3 +1,3 @@
module AbstractController
- class ActionNotFound < StandardError ; end
-end \ No newline at end of file
+ class ActionNotFound < StandardError; end
+end
diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb
index 1f0b38417b..43832f1e2d 100644
--- a/actionpack/lib/action_controller/abstract/helpers.rb
+++ b/actionpack/lib/action_controller/abstract/helpers.rb
@@ -1,19 +1,14 @@
module AbstractController
module Helpers
+ extend ActiveSupport::Concern
+
depends_on Renderer
-
- setup do
+
+ included do
extlib_inheritable_accessor :master_helper_module
self.master_helper_module = Module.new
end
-
- # def self.included(klass)
- # klass.class_eval do
- # extlib_inheritable_accessor :master_helper_module
- # self.master_helper_module = Module.new
- # end
- # end
-
+
def _action_view
@_action_view ||= begin
av = super
@@ -21,19 +16,38 @@ module AbstractController
av
end
end
-
+
module ClassMethods
def inherited(klass)
klass.master_helper_module = Module.new
klass.master_helper_module.__send__ :include, master_helper_module
-
+
super
end
-
+
+ # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
+ # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
+ # available to the templates.
def add_template_helper(mod)
master_helper_module.module_eval { include mod }
end
-
+
+ # Declare a controller method as a helper. For example, the following
+ # makes the +current_user+ controller method available to the view:
+ # class ApplicationController < ActionController::Base
+ # helper_method :current_user, :logged_in?
+ #
+ # def current_user
+ # @current_user ||= User.find_by_id(session[:user])
+ # end
+ #
+ # def logged_in?
+ # current_user != nil
+ # end
+ # end
+ #
+ # In a view:
+ # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
def helper_method(*meths)
meths.flatten.each do |meth|
master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
@@ -43,17 +57,16 @@ module AbstractController
ruby_eval
end
end
-
- def helper(*args, &blk)
+
+ def helper(*args, &block)
args.flatten.each do |arg|
case arg
when Module
add_template_helper(arg)
end
end
- master_helper_module.module_eval(&blk) if block_given?
+ master_helper_module.module_eval(&block) if block_given?
end
end
-
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb
index 478b301a26..b3f21f7b22 100644
--- a/actionpack/lib/action_controller/abstract/layouts.rb
+++ b/actionpack/lib/action_controller/abstract/layouts.rb
@@ -1,25 +1,34 @@
module AbstractController
module Layouts
-
+ extend ActiveSupport::Concern
+
depends_on Renderer
-
+
+ included do
+ extlib_inheritable_accessor :_layout_conditions
+ self._layout_conditions = {}
+ end
+
module ClassMethods
- def layout(layout)
+ def layout(layout, conditions = {})
unless [String, Symbol, FalseClass, NilClass].include?(layout.class)
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
end
-
+
+ conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
+ self._layout_conditions = conditions
+
@_layout = layout || false # Converts nil to false
_write_layout_method
end
-
+
def _implied_layout_name
name.underscore
end
-
+
# Takes the specified layout and creates a _layout method to be called
# by _default_layout
- #
+ #
# If the specified layout is a:
# String:: return the string
# Symbol:: call the method specified by the symbol
@@ -30,15 +39,15 @@ module AbstractController
def _write_layout_method
case @_layout
when String
- self.class_eval %{def _layout() #{@_layout.inspect} end}
+ self.class_eval %{def _layout(details) #{@_layout.inspect} end}
when Symbol
- self.class_eval %{def _layout() #{@_layout} end}
+ self.class_eval %{def _layout(details) #{@_layout} end}
when false
- self.class_eval %{def _layout() end}
+ self.class_eval %{def _layout(details) end}
else
self.class_eval %{
- def _layout
- if view_paths.find_by_parts?("#{_implied_layout_name}", formats, "layouts")
+ def _layout(details)
+ if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts")
"#{_implied_layout_name}"
else
super
@@ -48,35 +57,51 @@ module AbstractController
end
end
end
-
- def _render_template(template, options)
- _action_view._render_template_with_layout(template, options[:_layout])
- end
-
+
private
-
- def _layout() end # This will be overwritten
-
- def _layout_for_name(name)
+ # This will be overwritten
+ def _layout(details)
+ end
+
+ # :api: plugin
+ # ====
+ # Override this to mutate the inbound layout name
+ def _layout_for_name(name, details = {:formats => formats})
unless [String, FalseClass, NilClass].include?(name.class)
raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}"
end
-
- name && view_paths.find_by_parts(name, formats, "layouts")
+
+ name && view_paths.find_by_parts(name, details, _layout_prefix(name))
end
-
- def _default_layout(require_layout = false)
- if require_layout && !_layout
- raise ArgumentError,
+
+ # TODO: Decide if this is the best hook point for the feature
+ def _layout_prefix(name)
+ "layouts"
+ end
+
+ def _default_layout(require_layout = false, details = {:formats => formats})
+ if require_layout && _action_has_layout? && !_layout(details)
+ raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
end
-
+
begin
- layout = _layout_for_name(_layout)
+ _layout_for_name(_layout(details), details) if _action_has_layout?
rescue NameError => e
- raise NoMethodError,
+ raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
end
+
+ def _action_has_layout?
+ conditions = _layout_conditions
+ if only = conditions[:only]
+ only.include?(action_name)
+ elsif except = conditions[:except]
+ !except.include?(action_name)
+ else
+ true
+ end
+ end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb
index 4117369bd4..d6fa843485 100644
--- a/actionpack/lib/action_controller/abstract/logger.rb
+++ b/actionpack/lib/action_controller/abstract/logger.rb
@@ -1,7 +1,45 @@
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/logger'
+
module AbstractController
module Logger
- setup do
+ extend ActiveSupport::Concern
+
+ class DelayedLog
+ def initialize(&blk)
+ @blk = blk
+ end
+
+ def to_s
+ @blk.call
+ end
+ alias to_str to_s
+ end
+
+ included do
cattr_accessor :logger
end
+
+ def process(action)
+ ret = super
+
+ if logger
+ log = DelayedLog.new do
+ "\n\nProcessing #{self.class.name}\##{action_name} " \
+ "to #{request.formats} " \
+ "(for #{request_origin}) [#{request.method.to_s.upcase}]"
+ end
+
+ logger.info(log)
+ end
+
+ ret
+ end
+
+ def request_origin
+ # this *needs* to be cached!
+ # otherwise you'd get different results if calling it more than once
+ @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
+ end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb
index a86eef889e..cd3e87d861 100644
--- a/actionpack/lib/action_controller/abstract/renderer.rb
+++ b/actionpack/lib/action_controller/abstract/renderer.rb
@@ -2,52 +2,63 @@ require "action_controller/abstract/logger"
module AbstractController
module Renderer
+ extend ActiveSupport::Concern
+
depends_on AbstractController::Logger
-
- setup do
+
+ included do
attr_internal :formats
-
+
extlib_inheritable_accessor :_view_paths
-
+
self._view_paths ||= ActionView::PathSet.new
end
-
+
def _action_view
- @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self)
+ @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self)
end
-
- def render(options = {})
- self.response_body = render_to_body(options)
+
+ def render(*args)
+ if response_body
+ raise AbstractController::DoubleRenderError, "OMG"
+ end
+
+ self.response_body = render_to_body(*args)
end
-
+
# Raw rendering of a template to a Rack-compatible body.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
- #
+ #
# :api: plugin
def render_to_body(options = {})
- name = options[:_template_name] || action_name
-
- template = options[:_template] || view_paths.find_by_parts(name.to_s, formats, options[:_prefix])
- _render_template(template, options)
+ # TODO: Refactor so we can just use the normal template logic for this
+ if options[:_partial_object]
+ _action_view._render_partial_from_controller(options)
+ else
+ _determine_template(options)
+ _render_template(options)
+ end
end
# Raw rendering of a template to a string.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
- #
+ #
# :api: plugin
def render_to_string(options = {})
AbstractController::Renderer.body_to_s(render_to_body(options))
end
- def _render_template(template, options)
- _action_view._render_template_with_layout(template)
+ def _render_template(options)
+ _action_view._render_template_from_controller(options[:_template], options[:_layout], options, options[:_partial])
+ end
+
+ def view_paths()
+ _view_paths
end
-
- def view_paths() _view_paths end
# Return a string representation of a Rack-compatible response body.
def self.body_to_s(body)
@@ -61,16 +72,28 @@ module AbstractController
end
end
+ private
+ def _determine_template(options)
+ name = (options[:_template_name] || action_name).to_s
+
+ options[:_template] ||= view_paths.find_by_parts(
+ name, { :formats => formats }, options[:_prefix], options[:_partial]
+ )
+ end
+
module ClassMethods
-
def append_view_path(path)
self.view_paths << path
end
-
+
+ def prepend_view_path(path)
+ self.view_paths.unshift(path)
+ end
+
def view_paths
self._view_paths
end
-
+
def view_paths=(paths)
self._view_paths = paths.is_a?(ActionView::PathSet) ?
paths : ActionView::Base.process_view_paths(paths)
diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base/base.rb
index 3000b3d12f..67369eb122 100644
--- a/actionpack/lib/action_controller/base/base.rb
+++ b/actionpack/lib/action_controller/base/base.rb
@@ -1,5 +1,7 @@
require 'action_controller/deprecated'
require 'set'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/module/attr_internal'
module ActionController #:nodoc:
class ActionControllerError < StandardError #:nodoc:
@@ -30,10 +32,6 @@ module ActionController #:nodoc:
def allowed_methods_header
allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
end
-
- def handle_response!(response)
- response.headers['Allow'] ||= allowed_methods_header
- end
end
class NotImplemented < MethodNotAllowed #:nodoc:
@@ -238,13 +236,12 @@ module ActionController #:nodoc:
cattr_reader :protected_instance_variables
# Controller specific instance variables which will not be accessible inside views.
@@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
- @action_name @before_filter_chain_aborted @action_cache_path @_session @_headers @_params
+ @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params
@_flash @_response)
# 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"
- @@asset_host = ""
cattr_accessor :asset_host
# All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
@@ -356,7 +353,9 @@ module ActionController #:nodoc:
# Holds a hash of objects in the session. Accessed like <tt>session[:person]</tt> to get the object tied to the "person"
# key. The session will hold any type of object as values, but the key should be a string or symbol.
- attr_internal :session
+ def session
+ request.session
+ end
# Holds a hash of header names and values. Accessed like <tt>headers["Cache-Control"]</tt> to get the value of the Cache-Control
# directive. Values should always be specified as strings.
@@ -365,19 +364,24 @@ module ActionController #:nodoc:
# Returns the name of the action this controller is processing.
attr_accessor :action_name
- class << self
- def call(env)
- # HACK: For global rescue to have access to the original request and response
- request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env)
- response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new
- process(request, response)
- end
+ attr_reader :template
- # Factory for the standard create, process loop where the controller is discarded after processing.
- def process(request, response) #:nodoc:
- new.process(request, response)
- end
+ def action(name, env)
+ request = ActionDispatch::Request.new(env)
+ response = ActionDispatch::Response.new
+ self.action_name = name && name.to_s
+ process(request, response).to_a
+ end
+
+ class << self
+ def action(name = nil)
+ @actions ||= {}
+ @actions[name] ||= proc do |env|
+ new.action(name, env)
+ end
+ end
+
# Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
def controller_class_name
@controller_class_name ||= name.demodulize
@@ -443,60 +447,27 @@ module ActionController #:nodoc:
@view_paths = superclass.view_paths.dup if @view_paths.nil?
@view_paths.push(*path)
end
-
- # Replace sensitive parameter data from the request log.
- # Filters parameters that have any of the arguments as a substring.
- # Looks in all subhashes of the param hash for keys to filter.
- # If a block is given, each key and value of the parameter hash and all
- # subhashes is passed to it, the value or key
- # can be replaced using String#replace or similar method.
- #
- # Examples:
- # filter_parameter_logging
- # => Does nothing, just slows the logging process down
- #
- # filter_parameter_logging :password
- # => replaces the value to all keys matching /password/i with "[FILTERED]"
- #
- # filter_parameter_logging :foo, "bar"
- # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
- #
- # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
- # => reverses the value to all keys matching /secret/i
- #
- # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
- # => reverses the value to all keys matching /secret/i, and
- # replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
- def filter_parameter_logging(*filter_words, &block)
- parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
-
- define_method(:filter_parameters) do |unfiltered_parameters|
- filtered_parameters = {}
-
- unfiltered_parameters.each do |key, value|
- if key =~ parameter_filter
- filtered_parameters[key] = '[FILTERED]'
- elsif value.is_a?(Hash)
- filtered_parameters[key] = filter_parameters(value)
- elsif block_given?
- key = key.dup
- value = value.dup if value
- yield key, value
- filtered_parameters[key] = value
- else
- filtered_parameters[key] = value
- end
- end
-
- filtered_parameters
+
+ @@exempt_from_layout = [ActionView::TemplateHandlers::RJS]
+
+ def exempt_from_layout(*types)
+ types.each do |type|
+ @@exempt_from_layout <<
+ ActionView::Template.handler_class_for_extension(type)
end
- protected :filter_parameters
+
+ @@exempt_from_layout
end
- delegate :exempt_from_layout, :to => 'ActionView::Template'
end
public
+ def call(env)
+ request = ActionDispatch::Request.new(env)
+ response = ActionDispatch::Response.new
+ process(request, response).to_a
+ end
+
# Extracts the action_name from the request parameters and performs that action.
def process(request, response, method = :perform_action, *arguments) #:nodoc:
response.request = request
@@ -504,7 +475,6 @@ module ActionController #:nodoc:
assign_shortcuts(request, response)
initialize_template_class(response)
initialize_current_url
- assign_names
log_processing
send(method, *arguments)
@@ -787,7 +757,6 @@ module ActionController #:nodoc:
# Resets the session by clearing out all the objects stored within and initializing a new session object.
def reset_session #:doc:
request.reset_session
- @_session = request.session
end
private
@@ -804,20 +773,14 @@ module ActionController #:nodoc:
end
def initialize_template_class(response)
- @template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
- response.template.helpers.send :include, self.class.master_helper_module
- response.redirected_to = nil
+ @template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
+ response.template = @template if response.respond_to?(:template=)
+ @template.helpers.send :include, self.class.master_helper_module
@performed_render = @performed_redirect = false
end
def assign_shortcuts(request, response)
- @_request, @_params = request, request.parameters
-
- @_response = response
- @_response.session = request.session
-
- @_session = @_response.session
-
+ @_request, @_response, @_params = request, response, request.parameters
@_headers = @_response.headers
end
@@ -840,13 +803,6 @@ module ActionController #:nodoc:
logger.info(request_id)
end
- def log_processing_for_parameters
- parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
- parameters = parameters.except!(:controller, :action, :format, :_method)
-
- logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
- end
-
def default_render #:nodoc:
render
end
@@ -861,13 +817,13 @@ module ActionController #:nodoc:
return (performed? ? ret : default_render) if called
begin
- default_render
- rescue ActionView::MissingTemplate => e
- raise e unless e.action_name == action_name
- # If the path is the same as the action_name, the action is completely missing
+ view_paths.find_by_parts(action_name, {:formats => formats, :locales => [I18n.locale]}, controller_path)
+ rescue => e
raise UnknownAction, "No action responded to #{action_name}. Actions: " +
"#{action_methods.sort.to_sentence}", caller
end
+
+ default_render
end
# Returns true if a render or redirect has already been performed.
@@ -875,10 +831,6 @@ module ActionController #:nodoc:
@performed_render || @performed_redirect
end
- def assign_names
- @action_name = (params['action'] || 'index')
- end
-
def reset_variables_added_to_assigns
@template.instance_variable_set("@assigns_added", nil)
end
@@ -894,10 +846,6 @@ module ActionController #:nodoc:
"#{request.protocol}#{request.host}#{request.request_uri}"
end
- def close_session
- # @_session.close if @_session && @_session.respond_to?(:close)
- end
-
def default_template(action_name = self.action_name)
self.view_paths.find_template(default_template_name(action_name), default_template_format)
end
@@ -921,7 +869,6 @@ module ActionController #:nodoc:
end
def process_cleanup
- close_session
end
end
@@ -929,7 +876,7 @@ module ActionController #:nodoc:
[ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
Cookies, Caching, Verification, Streaming, SessionManagement,
HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier,
- RequestForgeryProtection, Translation
+ RequestForgeryProtection, Translation, FilterParameterLogging
].each do |mod|
include mod
end
diff --git a/actionpack/lib/action_controller/base/chained/benchmarking.rb b/actionpack/lib/action_controller/base/chained/benchmarking.rb
index 066150f58a..57a1ac8314 100644
--- a/actionpack/lib/action_controller/base/chained/benchmarking.rb
+++ b/actionpack/lib/action_controller/base/chained/benchmarking.rb
@@ -1,4 +1,4 @@
-require 'benchmark'
+require 'active_support/core_ext/benchmark'
module ActionController #:nodoc:
# The benchmarking module times the performance of actions and reports to the logger. If the Active Record
@@ -21,7 +21,7 @@ module ActionController #:nodoc:
# easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
# will only be conducted if the log level is low enough.
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
- if logger && logger.level == log_level
+ if logger && logger.level >= log_level
result = nil
ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)")
diff --git a/actionpack/lib/action_controller/base/chained/filters.rb b/actionpack/lib/action_controller/base/chained/filters.rb
index 9022b8b279..e121c0129d 100644
--- a/actionpack/lib/action_controller/base/chained/filters.rb
+++ b/actionpack/lib/action_controller/base/chained/filters.rb
@@ -160,7 +160,7 @@ module ActionController #:nodoc:
def convert_only_and_except_options_to_sets_of_strings(opts)
[:only, :except].each do |key|
if values = opts[key]
- opts[key] = Array(values).map(&:to_s).to_set
+ opts[key] = Array(values).map {|val| val.to_s }.to_set
end
end
end
@@ -571,12 +571,7 @@ module ActionController #:nodoc:
# Returns an array of Filter objects for this controller.
def filter_chain
- if chain = read_inheritable_attribute('filter_chain')
- return chain
- else
- write_inheritable_attribute('filter_chain', FilterChain.new)
- return filter_chain
- end
+ read_inheritable_attribute('filter_chain') || write_inheritable_attribute('filter_chain', FilterChain.new)
end
# Returns all the before filters for this class and all its ancestors.
diff --git a/actionpack/lib/action_controller/base/chained/flash.rb b/actionpack/lib/action_controller/base/chained/flash.rb
index 56ee9c67e2..04d27bf090 100644
--- a/actionpack/lib/action_controller/base/chained/flash.rb
+++ b/actionpack/lib/action_controller/base/chained/flash.rb
@@ -26,9 +26,18 @@ module ActionController #:nodoc:
#
# See docs on the FlashHash class for more details about the flash.
module Flash
- def self.included(base)
- base.class_eval do
- include InstanceMethods
+ extend ActiveSupport::Concern
+
+ # TODO : Remove the defined? check when new base is the main base
+ depends_on Session if defined?(ActionController::Http)
+
+ included do
+ # TODO : Remove the defined? check when new base is the main base
+ if defined?(ActionController::Http)
+ include InstanceMethodsForNewBase
+ else
+ include InstanceMethodsForBase
+
alias_method_chain :perform_action, :flash
alias_method_chain :reset_session, :flash
end
@@ -120,44 +129,68 @@ module ActionController #:nodoc:
(@used.keys - keys).each{ |k| @used.delete(k) }
end
+ def store(session, key = "flash")
+ return if self.empty?
+ session[key] = self
+ end
+
private
# Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
# use() # marks the entire flash as used
# use('msg') # marks the "msg" entry as used
# use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
# use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
- def use(k=nil, v=true)
- unless k.nil?
- @used[k] = v
- else
- keys.each{ |key| use(key, v) }
- end
+ # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
+ # if no key is passed.
+ def use(key = nil, used = true)
+ Array(key || keys).each { |k| @used[k] = used }
+ return key ? self[key] : self
end
end
- module InstanceMethods #:nodoc:
+ module InstanceMethodsForBase #:nodoc:
protected
def perform_action_with_flash
perform_action_without_flash
- remove_instance_variable(:@_flash) if defined? @_flash
+ if defined? @_flash
+ @_flash.store(session)
+ remove_instance_variable(:@_flash)
+ end
end
def reset_session_with_flash
reset_session_without_flash
- remove_instance_variable(:@_flash) if defined? @_flash
+ remove_instance_variable(:@_flash) if defined?(@_flash)
end
+ end
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
- # to put a new one.
- def flash #:doc:
- unless defined? @_flash
- @_flash = session["flash"] ||= FlashHash.new
- @_flash.sweep
+ module InstanceMethodsForNewBase #:nodoc:
+ protected
+ def process_action(method_name)
+ super
+ if defined? @_flash
+ @_flash.store(session)
+ remove_instance_variable(:@_flash)
end
+ end
- @_flash
+ def reset_session
+ super
+ remove_instance_variable(:@_flash) if defined?(@_flash)
end
end
+
+ protected
+ # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
+ # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
+ # to put a new one.
+ def flash #:doc:
+ if !defined?(@_flash)
+ @_flash = session["flash"] || FlashHash.new
+ @_flash.sweep
+ end
+
+ @_flash
+ end
end
end
diff --git a/actionpack/lib/action_controller/base/cookies.rb b/actionpack/lib/action_controller/base/cookies.rb
index ca380e98d0..d4806623c3 100644
--- a/actionpack/lib/action_controller/base/cookies.rb
+++ b/actionpack/lib/action_controller/base/cookies.rb
@@ -51,7 +51,7 @@ module ActionController #:nodoc:
protected
# Returns the cookie container, which operates as described above.
def cookies
- CookieJar.new(self)
+ @cookies ||= CookieJar.new(self)
end
end
diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/base/filter_parameter_logging.rb
new file mode 100644
index 0000000000..9df286ee24
--- /dev/null
+++ b/actionpack/lib/action_controller/base/filter_parameter_logging.rb
@@ -0,0 +1,97 @@
+module ActionController
+ module FilterParameterLogging
+ extend ActiveSupport::Concern
+
+ # TODO : Remove the defined? check when new base is the main base
+ if defined?(ActionController::Http)
+ depends_on AbstractController::Logger
+ end
+
+ included do
+ if defined?(ActionController::Http)
+ include InstanceMethodsForNewBase
+ end
+ end
+
+ module ClassMethods
+ # Replace sensitive parameter data from the request log.
+ # Filters parameters that have any of the arguments as a substring.
+ # Looks in all subhashes of the param hash for keys to filter.
+ # If a block is given, each key and value of the parameter hash and all
+ # subhashes is passed to it, the value or key
+ # can be replaced using String#replace or similar method.
+ #
+ # Examples:
+ # filter_parameter_logging
+ # => Does nothing, just slows the logging process down
+ #
+ # filter_parameter_logging :password
+ # => replaces the value to all keys matching /password/i with "[FILTERED]"
+ #
+ # filter_parameter_logging :foo, "bar"
+ # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+ #
+ # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
+ # => reverses the value to all keys matching /secret/i
+ #
+ # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
+ # => reverses the value to all keys matching /secret/i, and
+ # replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+ def filter_parameter_logging(*filter_words, &block)
+ parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
+
+ define_method(:filter_parameters) do |unfiltered_parameters|
+ filtered_parameters = {}
+
+ unfiltered_parameters.each do |key, value|
+ if key =~ parameter_filter
+ filtered_parameters[key] = '[FILTERED]'
+ elsif value.is_a?(Hash)
+ filtered_parameters[key] = filter_parameters(value)
+ elsif block_given?
+ key = key.dup
+ value = value.dup if value
+ yield key, value
+ filtered_parameters[key] = value
+ else
+ filtered_parameters[key] = value
+ end
+ end
+
+ filtered_parameters
+ end
+ protected :filter_parameters
+ end
+ end
+
+ module InstanceMethodsForNewBase
+ # TODO : Fix the order of information inside such that it's exactly same as the old base
+ def process(*)
+ ret = super
+
+ if logger
+ parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
+ parameters = parameters.except!(:controller, :action, :format, :_method, :only_path)
+
+ unless parameters.empty?
+ # TODO : Move DelayedLog to AS
+ log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" }
+ logger.info(log)
+ end
+ end
+
+ ret
+ end
+ end
+
+ private
+
+ # TODO : This method is not needed for the new base
+ def log_processing_for_parameters
+ parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
+ parameters = parameters.except!(:controller, :action, :format, :_method)
+
+ logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb
index ba65032f6a..f74158bc13 100644
--- a/actionpack/lib/action_controller/base/helpers.rb
+++ b/actionpack/lib/action_controller/base/helpers.rb
@@ -3,23 +3,19 @@ require 'active_support/dependencies'
# FIXME: helper { ... } is broken on Ruby 1.9
module ActionController #:nodoc:
module Helpers #:nodoc:
- def self.included(base)
+ extend ActiveSupport::Concern
+
+ included do
# Initialize the base module to aggregate its helpers.
- base.class_inheritable_accessor :master_helper_module
- base.master_helper_module = Module.new
+ class_inheritable_accessor :master_helper_module
+ self.master_helper_module = Module.new
# Set the default directory for helpers
- base.class_inheritable_accessor :helpers_dir
- base.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
-
- # Extend base with class methods to declare helpers.
- base.extend(ClassMethods)
+ class_inheritable_accessor :helpers_dir
+ self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
- base.class_eval do
- # Wrap inherited to create a new master helper module for subclasses.
- class << self
- alias_method_chain :inherited, :helper
- end
+ class << self
+ alias_method_chain :inherited, :helper
end
end
diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb
index b6b5267c66..2893290efb 100644
--- a/actionpack/lib/action_controller/base/http_authentication.rb
+++ b/actionpack/lib/action_controller/base/http_authentication.rb
@@ -1,3 +1,5 @@
+require 'active_support/base64'
+
module ActionController
module HttpAuthentication
# Makes it dead easy to do HTTP Basic authentication.
@@ -192,9 +194,10 @@ module ActionController
if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
password = password_procedure.call(credentials[:username])
+ method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
[true, false].any? do |password_is_ha1|
- expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1)
+ expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1)
expected == credentials[:response]
end
end
@@ -276,7 +279,7 @@ module ActionController
t = time.to_i
hashed = [t, secret_key]
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
- Base64.encode64("#{t}:#{digest}").gsub("\n", '')
+ ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '')
end
# Might want a shorter timeout depending on whether the request
@@ -285,7 +288,7 @@ module ActionController
# 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)
- t = Base64.decode64(value).split(":").first.to_i
+ t = ActiveSupport::Base64.decode64(value).split(":").first.to_i
nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
end
diff --git a/actionpack/lib/action_controller/base/layout.rb b/actionpack/lib/action_controller/base/layout.rb
index 4fcef6c5d9..cf5f46a32b 100644
--- a/actionpack/lib/action_controller/base/layout.rb
+++ b/actionpack/lib/action_controller/base/layout.rb
@@ -1,3 +1,7 @@
+require 'active_support/core_ext/enumerable'
+require 'active_support/core_ext/class/delegating_attributes'
+require 'active_support/core_ext/class/inheritable_attributes'
+
module ActionController #:nodoc:
module Layout #:nodoc:
def self.included(base)
@@ -182,7 +186,7 @@ module ActionController #:nodoc:
def memoized_find_layout(layout, formats) #:nodoc:
return layout if layout.nil? || layout.respond_to?(:render)
prefix = layout.to_s =~ /layouts\// ? nil : "layouts"
- view_paths.find_by_parts(layout.to_s, formats, prefix)
+ view_paths.find_by_parts(layout.to_s, {:formats => formats}, prefix)
end
def find_layout(*args)
diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb
index bac225ab2a..3c17dda1a1 100644
--- a/actionpack/lib/action_controller/base/mime_responds.rb
+++ b/actionpack/lib/action_controller/base/mime_responds.rb
@@ -1,111 +1,103 @@
module ActionController #:nodoc:
module MimeResponds #:nodoc:
- def self.included(base)
- base.module_eval do
- include ActionController::MimeResponds::InstanceMethods
- end
- end
-
- module InstanceMethods
- # Without web-service support, an action which collects the data for displaying a list of people
- # might look something like this:
- #
- # def index
- # @people = Person.find(:all)
- # end
- #
- # Here's the same action, with web-service support baked in:
- #
- # def index
- # @people = Person.find(:all)
- #
- # respond_to do |format|
- # format.html
- # format.xml { render :xml => @people.to_xml }
- # end
- # end
- #
- # What that says is, "if the client wants HTML in response to this action, just respond as we
- # would have before, but if the client wants XML, return them the list of people in XML format."
- # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
- #
- # Supposing you have an action that adds a new person, optionally creating their company
- # (by name) if it does not already exist, without web-services, it might look like this:
- #
- # def create
- # @company = Company.find_or_create_by_name(params[:company][:name])
- # @person = @company.people.create(params[:person])
- #
- # redirect_to(person_list_url)
- # end
- #
- # Here's the same action, with web-service support baked in:
- #
- # def create
- # company = params[:person].delete(:company)
- # @company = Company.find_or_create_by_name(company[:name])
- # @person = @company.people.create(params[:person])
- #
- # respond_to do |format|
- # format.html { redirect_to(person_list_url) }
- # format.js
- # format.xml { render :xml => @person.to_xml(:include => @company) }
- # end
- # end
- #
- # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
- # (format.js), then it is an RJS request and we render the RJS template associated with this action.
- # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
- # include the person's company in the rendered XML, so you get something like this:
- #
- # <person>
- # <id>...</id>
- # ...
- # <company>
- # <id>...</id>
- # <name>...</name>
- # ...
- # </company>
- # </person>
- #
- # Note, however, the extra bit at the top of that action:
- #
- # company = params[:person].delete(:company)
- # @company = Company.find_or_create_by_name(company[:name])
- #
- # This is because the incoming XML document (if a web-service request is in process) can only contain a
- # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
- #
- # person[name]=...&person[company][name]=...&...
- #
- # And, like this (xml-encoded):
- #
- # <person>
- # <name>...</name>
- # <company>
- # <name>...</name>
- # </company>
- # </person>
- #
- # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
- # we extract the company data from the request, find or create the company, and then create the new person
- # with the remaining data.
- #
- # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
- # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
- # and accept Rails' defaults, life will be much easier.
- #
- # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
- # environment.rb as follows.
- #
- # Mime::Type.register "image/jpg", :jpg
- def respond_to(*types, &block)
- raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
- block ||= lambda { |responder| types.each { |type| responder.send(type) } }
- responder = Responder.new(self)
- block.call(responder)
- responder.respond
- end
+ # Without web-service support, an action which collects the data for displaying a list of people
+ # might look something like this:
+ #
+ # def index
+ # @people = Person.find(:all)
+ # end
+ #
+ # Here's the same action, with web-service support baked in:
+ #
+ # def index
+ # @people = Person.find(:all)
+ #
+ # respond_to do |format|
+ # format.html
+ # format.xml { render :xml => @people.to_xml }
+ # end
+ # end
+ #
+ # What that says is, "if the client wants HTML in response to this action, just respond as we
+ # would have before, but if the client wants XML, return them the list of people in XML format."
+ # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
+ #
+ # Supposing you have an action that adds a new person, optionally creating their company
+ # (by name) if it does not already exist, without web-services, it might look like this:
+ #
+ # def create
+ # @company = Company.find_or_create_by_name(params[:company][:name])
+ # @person = @company.people.create(params[:person])
+ #
+ # redirect_to(person_list_url)
+ # end
+ #
+ # Here's the same action, with web-service support baked in:
+ #
+ # def create
+ # company = params[:person].delete(:company)
+ # @company = Company.find_or_create_by_name(company[:name])
+ # @person = @company.people.create(params[:person])
+ #
+ # respond_to do |format|
+ # format.html { redirect_to(person_list_url) }
+ # format.js
+ # format.xml { render :xml => @person.to_xml(:include => @company) }
+ # end
+ # end
+ #
+ # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
+ # (format.js), then it is an RJS request and we render the RJS template associated with this action.
+ # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
+ # include the person's company in the rendered XML, so you get something like this:
+ #
+ # <person>
+ # <id>...</id>
+ # ...
+ # <company>
+ # <id>...</id>
+ # <name>...</name>
+ # ...
+ # </company>
+ # </person>
+ #
+ # Note, however, the extra bit at the top of that action:
+ #
+ # company = params[:person].delete(:company)
+ # @company = Company.find_or_create_by_name(company[:name])
+ #
+ # This is because the incoming XML document (if a web-service request is in process) can only contain a
+ # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
+ #
+ # person[name]=...&person[company][name]=...&...
+ #
+ # And, like this (xml-encoded):
+ #
+ # <person>
+ # <name>...</name>
+ # <company>
+ # <name>...</name>
+ # </company>
+ # </person>
+ #
+ # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
+ # we extract the company data from the request, find or create the company, and then create the new person
+ # with the remaining data.
+ #
+ # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
+ # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
+ # and accept Rails' defaults, life will be much easier.
+ #
+ # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
+ # environment.rb as follows.
+ #
+ # Mime::Type.register "image/jpg", :jpg
+ def respond_to(*types, &block)
+ raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
+ block ||= lambda { |responder| types.each { |type| responder.send(type) } }
+ responder = Responder.new(self)
+ block.call(responder)
+ responder.respond
end
class Responder #:nodoc:
@@ -127,8 +119,14 @@ module ActionController #:nodoc:
@order << mime_type
@responses[mime_type] ||= Proc.new do
- @response.template.formats = [mime_type.to_sym]
+ # TODO: Remove this when new base is merged in
+ if defined?(Http)
+ @controller.formats = [mime_type.to_sym]
+ end
+
+ @controller.template.formats = [mime_type.to_sym]
@response.content_type = mime_type.to_s
+
block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
end
end
diff --git a/actionpack/lib/action_controller/base/redirect.rb b/actionpack/lib/action_controller/base/redirect.rb
index 2e92117e7c..7e10f614e2 100644
--- a/actionpack/lib/action_controller/base/redirect.rb
+++ b/actionpack/lib/action_controller/base/redirect.rb
@@ -48,8 +48,6 @@ module ActionController
status = 302
end
- response.redirected_to = options
-
case options
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
@@ -72,7 +70,9 @@ module ActionController
def redirect_to_full_url(url, status)
raise DoubleRenderError if performed?
logger.info("Redirected to #{url}") if logger && logger.info?
- response.redirect(url, interpret_status(status))
+ response.status = interpret_status(status)
+ response.location = url.gsub(/[\r\n]/, '')
+ response.body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
@performed_redirect = true
end
@@ -82,8 +82,6 @@ module ActionController
# The response body is not reset here, see +erase_render_results+
def erase_redirect_results #:nodoc:
@performed_redirect = false
- response.redirected_to = nil
- response.redirected_to_method_params = nil
response.status = DEFAULT_RENDER_STATUS_CODE
response.headers.delete('Location')
end
diff --git a/actionpack/lib/action_controller/base/render.rb b/actionpack/lib/action_controller/base/render.rb
index 606df58518..cc0d878e01 100644
--- a/actionpack/lib/action_controller/base/render.rb
+++ b/actionpack/lib/action_controller/base/render.rb
@@ -253,8 +253,9 @@ module ActionController
response.content_type ||= Mime::JS
render_for_text(js)
- elsif json = options[:json]
- json = json.to_json unless json.is_a?(String)
+ elsif options.include?(:json)
+ json = options[:json]
+ json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json)
@@ -374,12 +375,18 @@ module ActionController
render_for_file(name.sub(/^\//, ''), [layout, true], options)
end
end
-
- def render_for_parts(parts, layout, options = {})
+
+ # ==== Arguments
+ # parts<Array[String, Array[Symbol*], String, Boolean]>::
+ # Example: ["show", [:html, :xml], "users", false]
+ def render_for_parts(parts, layout_details, options = {})
+ parts[1] = {:formats => parts[1], :locales => [I18n.locale]}
+
tmp = view_paths.find_by_parts(*parts)
- layout = _pick_layout(*layout) unless tmp.exempt_from_layout?
-
+ layout = _pick_layout(*layout_details) unless
+ self.class.exempt_from_layout.include?(tmp.handler)
+
render_for_text(
@template._render_template_with_layout(tmp, layout, options, parts[3]))
end
diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb
index 3067122ceb..368c6e9de8 100644
--- a/actionpack/lib/action_controller/base/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb
@@ -3,12 +3,26 @@ module ActionController #:nodoc:
end
module RequestForgeryProtection
- def self.included(base)
- base.class_eval do
- helper_method :form_authenticity_token
- helper_method :protect_against_forgery?
+ extend ActiveSupport::Concern
+
+ # TODO : Remove the defined? check when new base is the main base
+ if defined?(ActionController::Http)
+ depends_on AbstractController::Helpers, Session
+ end
+
+ included do
+ if defined?(ActionController::Http)
+ # 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
+
+ # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
+ class_inheritable_accessor :allow_forgery_protection
+ self.allow_forgery_protection = true
end
- base.extend(ClassMethods)
+
+ helper_method :form_authenticity_token
+ helper_method :protect_against_forgery?
end
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
diff --git a/actionpack/lib/action_controller/base/rescue.rb b/actionpack/lib/action_controller/base/rescue.rb
new file mode 100644
index 0000000000..2717a06a37
--- /dev/null
+++ b/actionpack/lib/action_controller/base/rescue.rb
@@ -0,0 +1,50 @@
+module ActionController #:nodoc:
+ # Actions that fail to perform as expected throw exceptions. These
+ # exceptions can either be rescued for the public view (with a nice
+ # user-friendly explanation) or for the developers view (with tons of
+ # debugging information). The developers view is already implemented by
+ # the Action Controller, but the public view should be tailored to your
+ # specific application.
+ #
+ # The default behavior for public exceptions is to render a static html
+ # file with the name of the error code thrown. If no such file exists, an
+ # empty response is sent with the correct status code.
+ #
+ # You can override what constitutes a local request by overriding the
+ # <tt>local_request?</tt> method in your own controller. Custom rescue
+ # behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
+ # and <tt>rescue_action_locally</tt> methods.
+ module Rescue
+ def self.included(base) #:nodoc:
+ base.send :include, ActiveSupport::Rescuable
+ base.extend(ClassMethods)
+
+ base.class_eval do
+ alias_method_chain :perform_action, :rescue
+ end
+ end
+
+ module ClassMethods
+ def rescue_action(env)
+ exception = env.delete('action_dispatch.rescue.exception')
+ request = ActionDispatch::Request.new(env)
+ response = ActionDispatch::Response.new
+ new.process(request, response, :rescue_action, exception).to_a
+ end
+ end
+
+ protected
+ # Exception handler called when the performance of an action raises
+ # an exception.
+ def rescue_action(exception)
+ rescue_with_handler(exception) || raise(exception)
+ end
+
+ private
+ def perform_action_with_rescue
+ perform_action_without_rescue
+ rescue Exception => exception
+ rescue_action(exception)
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb
index 9f80f48c3d..5f56c95483 100644
--- a/actionpack/lib/action_controller/base/streaming.rb
+++ b/actionpack/lib/action_controller/base/streaming.rb
@@ -2,6 +2,13 @@ module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
# instead of rendering.
module Streaming
+ extend ActiveSupport::Concern
+
+ # TODO : Remove the defined? check when new base is the main base
+ if defined?(ActionController::Http)
+ depends_on ActionController::Renderer
+ end
+
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
:disposition => 'attachment'.freeze,
@@ -88,6 +95,7 @@ module ActionController #:nodoc:
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|
logger.info "Streaming file #{path}" unless logger.nil?
len = options[:buffer_size] || 4096
diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/base/verification.rb
index c62b81b666..31654e36f3 100644
--- a/actionpack/lib/action_controller/base/verification.rb
+++ b/actionpack/lib/action_controller/base/verification.rb
@@ -1,7 +1,10 @@
module ActionController #:nodoc:
module Verification #:nodoc:
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
+ extend ActiveSupport::Concern
+
+ # TODO : Remove the defined? check when new base is the main base
+ if defined?(ActionController::Http)
+ depends_on AbstractController::Callbacks, Session, Flash, Renderer
end
# This module provides a class-level method for specifying that certain
@@ -102,7 +105,7 @@ module ActionController #:nodoc:
end
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
- [*options[:params] ].find { |v| params[v].nil? } ||
+ [*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
[*options[:session]].find { |v| session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
end
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index ffd8081edc..38cf1da6a8 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -24,31 +24,31 @@ module ActionController #:nodoc:
# ActionController::Base.cache_store = :mem_cache_store, "localhost"
# ActionController::Base.cache_store = MyOwnStore.new("parameter")
module Caching
+ extend ActiveSupport::Concern
+
autoload :Actions, 'action_controller/caching/actions'
autoload :Fragments, 'action_controller/caching/fragments'
autoload :Pages, 'action_controller/caching/pages'
autoload :Sweeper, 'action_controller/caching/sweeping'
autoload :Sweeping, 'action_controller/caching/sweeping'
- def self.included(base) #:nodoc:
- base.class_eval do
- @@cache_store = nil
- cattr_reader :cache_store
+ 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)
- end
+ # Defines the storage option for cached fragments
+ def self.cache_store=(store_option)
+ @@cache_store = ActiveSupport::Cache.lookup_store(store_option)
+ end
- include Pages, Actions, Fragments
- include Sweeping if defined?(ActiveRecord)
+ include Pages, Actions, Fragments
+ include Sweeping if defined?(ActiveRecord)
- @@perform_caching = true
- cattr_accessor :perform_caching
+ @@perform_caching = true
+ cattr_accessor :perform_caching
- def self.cache_configured?
- perform_caching && cache_store
- end
+ def self.cache_configured?
+ perform_caching && cache_store
end
end
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index b99feddf77..54148b55d8 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -61,7 +61,15 @@ module ActionController #:nodoc:
filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) }
cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
- around_filter(cache_filter, filter_options)
+
+ # TODO: Remove this once new base is swapped in.
+ if defined?(ActionController::Http)
+ around_filter cache_filter, filter_options
+ else
+ around_filter(filter_options) do |controller, action|
+ cache_filter.filter(controller, action)
+ end
+ end
end
end
@@ -83,8 +91,24 @@ module ActionController #:nodoc:
@options = options
end
+ # TODO: Remove once New Base is merged
+ if defined?(ActionController::Http)
+ def filter(controller)
+ should_continue = before(controller)
+ yield if should_continue
+ after(controller)
+ end
+ else
+ def filter(controller, action)
+ should_continue = before(controller)
+ action.call if should_continue
+ after(controller)
+ end
+ end
+
def before(controller)
cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path)))
+
if cache = controller.read_fragment(cache_path.path, @options[:store_options])
controller.rendered_action_cache = true
set_content_type!(controller, cache_path.extension)
@@ -121,7 +145,9 @@ module ActionController #:nodoc:
end
def content_for_layout(controller)
- controller.response.layout && controller.response.template.instance_variable_get('@cached_content_for_layout')
+ # TODO: Remove this when new base is merged in
+ template = controller.respond_to?(:template) ? controller.template : controller._action_view
+ template.layout && template.instance_variable_get('@cached_content_for_layout')
end
end
diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb
index bb9d8bd063..9ad1cadfd3 100644
--- a/actionpack/lib/action_controller/dispatch/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb
@@ -1,96 +1,65 @@
+require 'active_support/core_ext/module/delegation'
+
module ActionController
# Dispatches requests to the appropriate controller and takes care of
# reloading the app after each request when Dependencies.load? is true.
class Dispatcher
+ cattr_accessor :prepare_each_request
+ self.prepare_each_request = false
+
+ cattr_accessor :router
+ self.router = Routing::Routes
+
+ cattr_accessor :middleware
+ self.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
+ middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
+ middleware.instance_eval(File.read(middlewares), middlewares, 1)
+ end
+
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
- unless self.middleware.include?(ActionDispatch::Reloader)
- self.middleware.insert_after(ActionDispatch::Failsafe, ActionDispatch::Reloader)
+ # Run prepare callbacks before every request in development mode
+ self.prepare_each_request = true
+
+ # Development mode callbacks
+ ActionDispatch::Callbacks.before_dispatch do |app|
+ ActionController::Dispatcher.router.reload
+ end
+
+ ActionDispatch::Callbacks.after_dispatch do
+ # Cleanup the application before processing the current request.
+ ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
+ ActiveSupport::Dependencies.clear
+ ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
end
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
if defined?(ActiveRecord)
- to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
+ to_prepare(:activerecord_instantiate_observers) do
+ ActiveRecord::Base.instantiate_observers
+ end
end
- after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)
+ if Base.logger && Base.logger.respond_to?(:flush)
+ after_dispatch do
+ Base.logger.flush
+ end
+ end
to_prepare do
I18n.reload!
end
end
- # Add a preparation callback. Preparation callbacks are run before every
- # request in development mode, and before the first request in production
- # mode.
- #
- # An optional identifier may be supplied for the callback. If provided,
- # to_prepare may be called again with the same identifier to replace the
- # existing callback. Passing an identifier is a suggested practice if the
- # code adding a preparation block may be reloaded.
- def to_prepare(identifier = nil, &block)
- @prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
- callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
- @prepare_dispatch_callbacks.replace_or_append!(callback)
- end
-
- def run_prepare_callbacks
- new.send :run_callbacks, :prepare_dispatch
- end
-
- def reload_application
- # Run prepare callbacks before every request in development mode
- run_prepare_callbacks
+ delegate :to_prepare, :prepare_dispatch, :before_dispatch, :after_dispatch,
+ :to => ActionDispatch::Callbacks
- Routing::Routes.reload
+ def new
+ @@middleware.build(@@router)
end
-
- def cleanup_application
- # Cleanup the application before processing the current request.
- ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
- ActiveSupport::Dependencies.clear
- ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
- end
- end
-
- cattr_accessor :middleware
- self.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
- middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
- middleware.instance_eval(File.read(middlewares))
- end
-
- include ActiveSupport::Callbacks
- define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
-
- def initialize
- @app = @@middleware.build(lambda { |env| self._call(env) })
- freeze
- end
-
- def call(env)
- @app.call(env)
- end
-
- def _call(env)
- begin
- run_callbacks :before_dispatch
- Routing::Routes.call(env)
- rescue Exception => exception
- if controller ||= (::ApplicationController rescue Base)
- controller.call_with_exception(env, exception).to_a
- else
- raise exception
- end
- ensure
- run_callbacks :after_dispatch, :enumerator => :reverse_each
- end
- end
-
- def flush_logger
- Base.logger.flush
end
end
end
diff --git a/actionpack/lib/action_controller/dispatch/middlewares.rb b/actionpack/lib/action_controller/dispatch/middlewares.rb
index b62b4f84a1..b25ed3fd3f 100644
--- a/actionpack/lib/action_controller/dispatch/middlewares.rb
+++ b/actionpack/lib/action_controller/dispatch/middlewares.rb
@@ -2,12 +2,17 @@ use "Rack::Lock", :if => lambda {
!ActionController::Base.allow_concurrency
}
-use "ActionDispatch::Failsafe"
+use "ActionDispatch::ShowExceptions", lambda { ActionController::Base.consider_all_requests_local }
+use "ActionDispatch::Callbacks", lambda { ActionController::Dispatcher.prepare_each_request }
+use "ActionDispatch::Rescue", lambda {
+ controller = (::ApplicationController rescue ActionController::Base)
+ # TODO: Replace with controller.action(:_rescue_action)
+ controller.method(:rescue_action)
+}
use lambda { ActionController::Base.session_store },
lambda { ActionController::Base.session_options }
-use "ActionDispatch::RewindableInput"
use "ActionDispatch::ParamsParser"
use "Rack::MethodOverride"
-use "Rack::Head"
+use "Rack::Head" \ No newline at end of file
diff --git a/actionpack/lib/action_controller/dispatch/rescue.rb b/actionpack/lib/action_controller/dispatch/rescue.rb
deleted file mode 100644
index df80ac0909..0000000000
--- a/actionpack/lib/action_controller/dispatch/rescue.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-module ActionController #:nodoc:
- # Actions that fail to perform as expected throw exceptions. These
- # exceptions can either be rescued for the public view (with a nice
- # user-friendly explanation) or for the developers view (with tons of
- # debugging information). The developers view is already implemented by
- # the Action Controller, but the public view should be tailored to your
- # specific application.
- #
- # The default behavior for public exceptions is to render a static html
- # file with the name of the error code thrown. If no such file exists, an
- # empty response is sent with the correct status code.
- #
- # You can override what constitutes a local request by overriding the
- # <tt>local_request?</tt> method in your own controller. Custom rescue
- # behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
- # and <tt>rescue_action_locally</tt> methods.
- module Rescue
- LOCALHOST = '127.0.0.1'.freeze
-
- DEFAULT_RESCUE_RESPONSE = :internal_server_error
- DEFAULT_RESCUE_RESPONSES = {
- 'ActionController::RoutingError' => :not_found,
- 'ActionController::UnknownAction' => :not_found,
- 'ActiveRecord::RecordNotFound' => :not_found,
- 'ActiveRecord::StaleObjectError' => :conflict,
- 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
- 'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
- 'ActionController::MethodNotAllowed' => :method_not_allowed,
- 'ActionController::NotImplemented' => :not_implemented,
- 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
- }
-
- DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
- DEFAULT_RESCUE_TEMPLATES = {
- 'ActionView::MissingTemplate' => 'missing_template',
- 'ActionController::RoutingError' => 'routing_error',
- 'ActionController::UnknownAction' => 'unknown_action',
- 'ActionView::TemplateError' => 'template_error'
- }
-
- RESCUES_TEMPLATE_PATH = ActionView::Template::FileSystemPath.new(
- File.join(File.dirname(__FILE__), "templates"))
-
- def self.included(base) #:nodoc:
- base.cattr_accessor :rescue_responses
- base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
- base.rescue_responses.update DEFAULT_RESCUE_RESPONSES
-
- base.cattr_accessor :rescue_templates
- base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
- base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
-
- base.extend(ClassMethods)
- base.send :include, ActiveSupport::Rescuable
-
- base.class_eval do
- alias_method_chain :perform_action, :rescue
- end
- end
-
- module ClassMethods
- def call_with_exception(env, exception) #:nodoc:
- request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env)
- response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new
- new.process(request, response, :rescue_action, exception)
- end
- end
-
- protected
- # Exception handler called when the performance of an action raises
- # an exception.
- def rescue_action(exception)
- rescue_with_handler(exception) ||
- rescue_action_without_handler(exception)
- end
-
- # Overwrite to implement custom logging of errors. By default
- # logs as fatal.
- def log_error(exception) #:doc:
- ActiveSupport::Deprecation.silence do
- if ActionView::TemplateError === exception
- logger.fatal(exception.to_s)
- else
- logger.fatal(
- "\n#{exception.class} (#{exception.message}):\n " +
- clean_backtrace(exception).join("\n ") + "\n\n"
- )
- end
- end
- end
-
- # Overwrite to implement public exception handling (for requests
- # answering false to <tt>local_request?</tt>). By default will call
- # render_optional_error_file. Override this method to provide more
- # user friendly error messages.
- def rescue_action_in_public(exception) #:doc:
- render_optional_error_file response_code_for_rescue(exception)
- end
-
- # Attempts to render a static error page based on the
- # <tt>status_code</tt> thrown, or just return headers if no such file
- # exists. At first, it will try to render a localized static page.
- # For example, if a 500 error is being handled Rails and locale is :da,
- # it will first attempt to render the file at <tt>public/500.da.html</tt>
- # then attempt to render <tt>public/500.html</tt>. If none of them exist,
- # the body of the response will be left empty.
- def render_optional_error_file(status_code)
- status = interpret_status(status_code)
- locale_path = "#{Rails.public_path}/#{status[0,3]}.#{I18n.locale}.html" if I18n.locale
- path = "#{Rails.public_path}/#{status[0,3]}.html"
-
- if locale_path && File.exist?(locale_path)
- render :file => locale_path, :status => status, :content_type => Mime::HTML
- elsif File.exist?(path)
- render :file => path, :status => status, :content_type => Mime::HTML
- else
- head status
- end
- end
-
- # True if the request came from localhost, 127.0.0.1. Override this
- # method if you wish to redefine the meaning of a local request to
- # include remote IP addresses or other criteria.
- def local_request? #:doc:
- request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
- end
-
- # Render detailed diagnostics for unhandled exceptions rescued from
- # a controller action.
- def rescue_action_locally(exception)
- @template.instance_variable_set("@exception", exception)
- @template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
- @template.instance_variable_set("@contents",
- @template._render_template(template_path_for_local_rescue(exception)))
-
- response.content_type = Mime::HTML
- response.status = interpret_status(response_code_for_rescue(exception))
-
- content = @template._render_template(rescues_path("layout"))
- render_for_text(content)
- end
-
- def rescue_action_without_handler(exception)
- log_error(exception) if logger
- erase_results if performed?
-
- # Let the exception alter the response if it wants.
- # For example, MethodNotAllowed sets the Allow header.
- if exception.respond_to?(:handle_response!)
- exception.handle_response!(response)
- end
-
- if consider_all_requests_local || local_request?
- rescue_action_locally(exception)
- else
- rescue_action_in_public(exception)
- end
- end
-
- private
- def perform_action_with_rescue #:nodoc:
- perform_action_without_rescue
- rescue Exception => exception
- rescue_action(exception)
- end
-
- def rescues_path(template_name)
- RESCUES_TEMPLATE_PATH.find_by_parts("rescues/#{template_name}.erb")
- end
-
- def template_path_for_local_rescue(exception)
- rescues_path(rescue_templates[exception.class.name])
- end
-
- def response_code_for_rescue(exception)
- rescue_responses[exception.class.name]
- end
-
- def clean_backtrace(exception)
- defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
- Rails.backtrace_cleaner.clean(exception.backtrace) :
- exception.backtrace
- end
- end
-end
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb
deleted file mode 100644
index e5c647c826..0000000000
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-<h1>
- <%=h @exception.class.to_s %>
- <% if request.parameters['controller'] %>
- in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
- <% end %>
-</h1>
-<pre><%=h @exception.clean_message %></pre>
-
-<%= @template._render_template(@rescues_path.find_by_parts("rescues/_trace.erb")) %>
-<%= @template._render_template(@rescues_path.find_by_parts("rescues/_request_and_response.erb")) %> \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb
index 7c65f1cdc1..df256985ac 100644
--- a/actionpack/lib/action_controller/new_base.rb
+++ b/actionpack/lib/action_controller/new_base.rb
@@ -1,7 +1,47 @@
module ActionController
- autoload :AbstractBase, "action_controller/new_base/base"
- autoload :HideActions, "action_controller/new_base/hide_actions"
- autoload :Layouts, "action_controller/new_base/layouts"
- autoload :Renderer, "action_controller/new_base/renderer"
- autoload :UrlFor, "action_controller/new_base/url_for"
-end \ No newline at end of file
+ autoload :Base, "action_controller/new_base/base"
+ autoload :ConditionalGet, "action_controller/new_base/conditional_get"
+ autoload :HideActions, "action_controller/new_base/hide_actions"
+ autoload :Http, "action_controller/new_base/http"
+ autoload :Layouts, "action_controller/new_base/layouts"
+ autoload :RackConvenience, "action_controller/new_base/rack_convenience"
+ autoload :Rails2Compatibility, "action_controller/new_base/compatibility"
+ autoload :Redirector, "action_controller/new_base/redirector"
+ autoload :Renderer, "action_controller/new_base/renderer"
+ autoload :RenderOptions, "action_controller/new_base/render_options"
+ autoload :Renderers, "action_controller/new_base/render_options"
+ autoload :Rescue, "action_controller/new_base/rescuable"
+ autoload :Testing, "action_controller/new_base/testing"
+ autoload :UrlFor, "action_controller/new_base/url_for"
+ autoload :Session, "action_controller/new_base/session"
+ autoload :Helpers, "action_controller/new_base/helpers"
+
+ # Ported modules
+ # require 'action_controller/routing'
+ autoload :Caching, 'action_controller/caching'
+ autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
+ autoload :MimeResponds, 'action_controller/base/mime_responds'
+ autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
+ autoload :RecordIdentifier, 'action_controller/record_identifier'
+ autoload :Resources, 'action_controller/routing/resources'
+ autoload :SessionManagement, 'action_controller/base/session_management'
+ autoload :TestCase, 'action_controller/testing/test_case'
+ autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
+ autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
+
+ autoload :Verification, 'action_controller/base/verification'
+ autoload :Flash, 'action_controller/base/chained/flash'
+ autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
+ autoload :Streaming, 'action_controller/base/streaming'
+ autoload :HttpAuthentication, 'action_controller/base/http_authentication'
+ autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging'
+ autoload :Translation, 'action_controller/translation'
+ autoload :Cookies, 'action_controller/base/cookies'
+
+ require 'action_controller/routing'
+end
+
+autoload :HTML, 'action_controller/vendor/html-scanner'
+
+require 'action_dispatch'
+require 'action_view'
diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb
index 08e7a1a0e7..d7b65d37fa 100644
--- a/actionpack/lib/action_controller/new_base/base.rb
+++ b/actionpack/lib/action_controller/new_base/base.rb
@@ -1,60 +1,173 @@
module ActionController
- class AbstractBase < AbstractController::Base
-
- # :api: public
- attr_internal :request, :response, :params
-
- # :api: public
- def self.controller_name
- @controller_name ||= controller_path.split("/").last
- end
-
- # :api: public
- def controller_name() self.class.controller_name end
-
- # :api: public
- def self.controller_path
- @controller_path ||= self.name.sub(/Controller$/, '').underscore
- end
-
- # :api: public
- def controller_path() self.class.controller_path end
-
- # :api: private
- def self.action_methods
- @action_names ||= Set.new(self.public_instance_methods - self::CORE_METHODS)
- end
-
- # :api: private
- def self.action_names() action_methods end
-
- # :api: private
- def action_methods() self.class.action_names end
-
- # :api: private
- def action_names() action_methods end
-
- # :api: plugin
- def self.call(env)
- controller = new
- controller.call(env).to_rack
- end
-
- # :api: plugin
- def response_body=(body)
- @_response.body = body
- end
-
- # :api: private
- def call(env)
- @_request = ActionDispatch::Request.new(env)
- @_response = ActionDispatch::Response.new
- process(@_request.parameters[:action])
- end
-
- # :api: private
- def to_rack
- response.to_a
+ class Base < Http
+ abstract!
+
+ include AbstractController::Benchmarker
+ include AbstractController::Callbacks
+ include AbstractController::Logger
+
+ include ActionController::Helpers
+ include ActionController::HideActions
+ include ActionController::UrlFor
+ include ActionController::Redirector
+ include ActionController::Renderer
+ include ActionController::Renderers::All
+ include ActionController::Layouts
+ include ActionController::ConditionalGet
+ include ActionController::RackConvenience
+
+ # Legacy modules
+ include SessionManagement
+ include ActionDispatch::StatusCodes
+ include ActionController::Caching
+ include ActionController::MimeResponds
+
+ # Rails 2.x compatibility
+ include ActionController::Rails2Compatibility
+
+ include ActionController::Cookies
+ include ActionController::Session
+ include ActionController::Flash
+ include ActionController::Verification
+ include ActionController::RequestForgeryProtection
+ include ActionController::Streaming
+ include ActionController::HttpAuthentication::Basic::ControllerMethods
+ include ActionController::HttpAuthentication::Digest::ControllerMethods
+ include ActionController::FilterParameterLogging
+ include ActionController::Translation
+
+ # TODO: Extract into its own module
+ # This should be moved together with other normalizing behavior
+ module ImplicitRender
+ def send_action(method_name)
+ ret = super
+ default_render unless performed?
+ ret
+ end
+
+ def default_render
+ render
+ end
+
+ def method_for_action(action_name)
+ super || begin
+ if view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path)
+ "default_render"
+ end
+ end
+ end
+ end
+
+ include ImplicitRender
+
+ include ActionController::Rescue
+
+ def self.inherited(klass)
+ ::ActionController::Base.subclasses << klass.to_s
+ super
+ end
+
+ def self.subclasses
+ @subclasses ||= []
+ end
+
+ def self.app_loaded!
+ @subclasses.each do |subclass|
+ subclass.constantize._write_layout_method
+ end
+ end
+
+ def _normalize_options(action = nil, options = {}, &blk)
+ if action.is_a?(Hash)
+ options, action = action, nil
+ elsif action.is_a?(String) || action.is_a?(Symbol)
+ key = case action = action.to_s
+ when %r{^/} then :file
+ when %r{/} then :template
+ else :action
+ end
+ options.merge! key => action
+ elsif action
+ options.merge! :partial => action
+ end
+
+ if options.key?(:action) && options[:action].to_s.index("/")
+ options[:template] = options.delete(:action)
+ end
+
+ if options[:status]
+ options[:status] = interpret_status(options[:status]).to_i
+ end
+
+ options[:update] = blk if block_given?
+ options
+ end
+
+ def render(action = nil, options = {}, &blk)
+ options = _normalize_options(action, options, &blk)
+ super(options)
+ end
+
+ def render_to_string(action = nil, options = {}, &blk)
+ options = _normalize_options(action, options, &blk)
+ super(options)
+ end
+
+ # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
+ #
+ # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
+ # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
+ # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
+ # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
+ # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
+ # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
+ #
+ # Examples:
+ # redirect_to :action => "show", :id => 5
+ # redirect_to post
+ # redirect_to "http://www.rubyonrails.org"
+ # redirect_to "/images/screenshot.jpg"
+ # redirect_to articles_url
+ # redirect_to :back
+ #
+ # The redirection happens as a "302 Moved" header unless otherwise specified.
+ #
+ # Examples:
+ # redirect_to post_url(@post), :status=>:found
+ # redirect_to :action=>'atom', :status=>:moved_permanently
+ # redirect_to post_url(@post), :status=>301
+ # redirect_to :action=>'atom', :status=>302
+ #
+ # When using <tt>redirect_to :back</tt>, if there is no referrer,
+ # RedirectBackError will be raised. You may specify some fallback
+ # behavior for this case by rescuing RedirectBackError.
+ def redirect_to(options = {}, response_status = {}) #:doc:
+ raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
+
+ status = if options.is_a?(Hash) && options.key?(:status)
+ interpret_status(options.delete(:status))
+ elsif response_status.key?(:status)
+ interpret_status(response_status[:status])
+ else
+ 302
+ end
+
+ url = case options
+ # The scheme name consist of a letter followed by any combination of
+ # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
+ # characters; and is terminated by a colon (":").
+ when %r{^\w[\w\d+.-]*:.*}
+ options
+ when String
+ request.protocol + request.host_with_port + options
+ when :back
+ raise RedirectBackError unless refer = request.headers["Referer"]
+ refer
+ else
+ url_for(options)
+ end
+
+ super(url, status)
end
end
end
diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb
new file mode 100644
index 0000000000..f278c2da14
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/compatibility.rb
@@ -0,0 +1,138 @@
+module ActionController
+ module Rails2Compatibility
+ extend ActiveSupport::Concern
+
+ class ::ActionController::ActionControllerError < StandardError #:nodoc:
+ end
+
+ # Temporary hax
+ included do
+ ::ActionController::UnknownAction = ::AbstractController::ActionNotFound
+ ::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError
+
+ cattr_accessor :session_options
+ self.session_options = {}
+
+ cattr_accessor :allow_concurrency
+ self.allow_concurrency = false
+
+ cattr_accessor :param_parsers
+ self.param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
+ Mime::URL_ENCODED_FORM => :url_encoded_form,
+ Mime::XML => :xml_simple,
+ Mime::JSON => :json }
+
+ cattr_accessor :relative_url_root
+ self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
+
+ cattr_accessor :default_charset
+ self.default_charset = "utf-8"
+
+ # cattr_reader :protected_instance_variables
+ cattr_accessor :protected_instance_variables
+ self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
+ @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params
+ @_flash @_response)
+
+ # Indicates whether or not optimise the generated named
+ # route helper methods
+ cattr_accessor :optimise_named_routes
+ self.optimise_named_routes = true
+
+ cattr_accessor :resources_path_names
+ self.resources_path_names = { :new => 'new', :edit => 'edit' }
+
+ # Controls the resource action separator
+ cattr_accessor :resource_action_separator
+ self.resource_action_separator = "/"
+
+ cattr_accessor :use_accept_header
+ self.use_accept_header = true
+
+ cattr_accessor :page_cache_directory
+ self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
+
+ cattr_reader :cache_store
+
+ cattr_accessor :consider_all_requests_local
+ self.consider_all_requests_local = true
+
+ # 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
+ end
+
+ # For old tests
+ def initialize_template_class(*) end
+ def assign_shortcuts(*) end
+
+ # TODO: Remove this after we flip
+ def template
+ @template ||= _action_view
+ end
+
+ def process_action(*)
+ template
+ super
+ end
+
+ module ClassMethods
+ def consider_all_requests_local
+ 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
+
+ def render_to_body(options)
+ if options.is_a?(Hash) && options.key?(:template)
+ options[:template].sub!(/^\//, '')
+ end
+
+ options[:text] = nil if options[:nothing] == true
+
+ body = super
+ body = [' '] if body.blank?
+ body
+ end
+
+ def _handle_method_missing
+ method_missing(@_action_name.to_sym)
+ end
+
+ def method_for_action(action_name)
+ super || (respond_to?(:method_missing) && "_handle_method_missing")
+ end
+
+ def _layout_prefix(name)
+ super unless name =~ /\blayouts/
+ end
+
+ 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
+ _action_view.view_paths
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/conditional_get.rb b/actionpack/lib/action_controller/new_base/conditional_get.rb
new file mode 100644
index 0000000000..8bd6db500b
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/conditional_get.rb
@@ -0,0 +1,133 @@
+module ActionController
+ module ConditionalGet
+ extend ActiveSupport::Concern
+
+ depends_on RackConvenience
+
+ # Sets the etag, last_modified, or both on the response and renders a
+ # "304 Not Modified" response if the request is already fresh.
+ #
+ # Parameters:
+ # * <tt>:etag</tt>
+ # * <tt>:last_modified</tt>
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
+ #
+ # Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
+ # end
+ #
+ # This will render the show template if the request isn't sending a matching etag or
+ # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
+ #
+ def fresh_when(options)
+ options.assert_valid_keys(:etag, :last_modified, :public)
+
+ response.etag = options[:etag] if options[:etag]
+ response.last_modified = options[:last_modified] if options[:last_modified]
+
+ if options[:public]
+ cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
+ cache_control.delete("private")
+ cache_control.delete("no-cache")
+ cache_control << "public"
+ response.headers["Cache-Control"] = cache_control.join(', ')
+ end
+
+ if request.fresh?(response)
+ head :not_modified
+ end
+ end
+
+ # Return a response that has no content (merely headers). The options
+ # argument is interpreted to be a hash of header names and values.
+ # This allows you to easily return a response that consists only of
+ # significant headers:
+ #
+ # head :created, :location => person_path(@person)
+ #
+ # It can also be used to return exceptional conditions:
+ #
+ # return head(:method_not_allowed) unless request.post?
+ # return head(:bad_request) unless valid_request?
+ # render
+ def head(*args)
+ if args.length > 2
+ raise ArgumentError, "too many arguments to head"
+ elsif args.empty?
+ raise ArgumentError, "too few arguments to head"
+ end
+ options = args.extract_options!
+ status = args.shift || options.delete(:status) || :ok
+
+ options.each do |key, value|
+ headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
+ end
+
+ render :nothing => true, :status => status
+ end
+
+ # Sets the etag and/or last_modified on the response and checks it against
+ # the client request. If the request doesn't match the options provided, the
+ # request is considered stale and should be generated from scratch. Otherwise,
+ # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
+ #
+ # Parameters:
+ # * <tt>:etag</tt>
+ # * <tt>:last_modified</tt>
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
+ #
+ # Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ #
+ # if stale?(:etag => @article, :last_modified => @article.created_at.utc)
+ # @statistics = @article.really_expensive_call
+ # respond_to do |format|
+ # # all the supported formats
+ # end
+ # end
+ # end
+ def stale?(options)
+ fresh_when(options)
+ !request.fresh?(response)
+ end
+
+ # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
+ # intermediate caches shouldn't cache the response.
+ #
+ # Examples:
+ # expires_in 20.minutes
+ # expires_in 3.hours, :public => true
+ # expires in 3.hours, 'max-stale' => 5.hours, :public => true
+ #
+ # This method will overwrite an existing Cache-Control header.
+ # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
+ def expires_in(seconds, options = {}) #:doc:
+ cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
+
+ cache_control << "max-age=#{seconds}"
+ cache_control.delete("no-cache")
+ if options[:public]
+ cache_control.delete("private")
+ cache_control << "public"
+ else
+ cache_control << "private"
+ end
+
+ # This allows for additional headers to be passed through like 'max-stale' => 5.hours
+ cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
+
+ response.headers["Cache-Control"] = cache_control.join(', ')
+ end
+
+ # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
+ # intermediate caches (like caching proxy servers).
+ def expires_now #:doc:
+ response.headers["Cache-Control"] = "no-cache"
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb
new file mode 100644
index 0000000000..c2ebd343e3
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/helpers.rb
@@ -0,0 +1,129 @@
+require 'active_support/core_ext/load_error'
+require 'active_support/core_ext/name_error'
+require 'active_support/dependencies'
+
+module ActionController
+ module Helpers
+ extend ActiveSupport::Concern
+
+ depends_on AbstractController::Helpers
+
+ included do
+ # Set the default directory for helpers
+ class_inheritable_accessor :helpers_dir
+ self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
+ end
+
+ module ClassMethods
+ def inherited(klass)
+ klass.__send__ :default_helper_module!
+ super
+ end
+
+ # The +helper+ class method can take a series of helper module names, a block, or both.
+ #
+ # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
+ # * <tt>&block</tt>: A block defining helper methods.
+ #
+ # ==== Examples
+ # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
+ # and include the module in the template class. The second form illustrates how to include custom helpers
+ # when working with namespaced controllers, or other cases where the file containing the helper definition is not
+ # in one of Rails' standard load paths:
+ # helper :foo # => requires 'foo_helper' and includes FooHelper
+ # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
+ #
+ # When the argument is a module it will be included directly in the template class.
+ # helper FooHelper # => includes FooHelper
+ #
+ # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers beneath
+ # <tt>ActionController::Base.helpers_dir</tt> (defaults to <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT).
+ # helper :all
+ #
+ # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
+ # to the template.
+ # # One line
+ # helper { def hello() "Hello, world!" end }
+ # # Multi-line
+ # helper do
+ # def foo(bar)
+ # "#{bar} is the very best"
+ # end
+ # end
+ #
+ # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
+ # +symbols+, +strings+, +modules+ and blocks.
+ # helper(:three, BlindHelper) { def mice() 'mice' end }
+ #
+ def helper(*args, &block)
+ args.flatten.each do |arg|
+ case arg
+ when :all
+ helper all_application_helpers
+ when String, Symbol
+ file_name = arg.to_s.underscore + '_helper'
+ class_name = file_name.camelize
+
+ begin
+ require_dependency(file_name)
+ rescue LoadError => load_error
+ requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
+ if requiree == file_name
+ msg = "Missing helper file helpers/#{file_name}.rb"
+ raise LoadError.new(msg).copy_blame!(load_error)
+ else
+ raise
+ end
+ end
+
+ super class_name.constantize
+ else
+ super args
+ end
+ end
+
+ # Evaluate block in template class if given.
+ master_helper_module.module_eval(&block) if block_given?
+ end
+
+ # Declares helper accessors for controller attributes. For example, the
+ # following adds new +name+ and <tt>name=</tt> instance methods to a
+ # controller and makes them available to the view:
+ # helper_attr :name
+ # attr_accessor :name
+ def helper_attr(*attrs)
+ attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
+ end
+
+ # Provides a proxy to access helpers methods from outside the view.
+ def helpers
+ unless @helper_proxy
+ @helper_proxy = ActionView::Base.new
+ @helper_proxy.extend master_helper_module
+ else
+ @helper_proxy
+ end
+ end
+
+ private
+ def default_helper_module!
+ unless name.blank?
+ module_name = name.sub(/Controller$|$/, 'Helper')
+ module_path = module_name.split('::').map { |m| m.underscore }.join('/')
+ require_dependency module_path
+ helper module_name.constantize
+ end
+ rescue MissingSourceFile => e
+ raise unless e.is_missing? module_path
+ rescue NameError => e
+ raise unless e.missing_name? module_name
+ end
+
+ # Extract helper names from files in app/helpers/**/*.rb
+ def all_application_helpers
+ extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/
+ Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb
index 473a8ea72b..b45e520bee 100644
--- a/actionpack/lib/action_controller/new_base/hide_actions.rb
+++ b/actionpack/lib/action_controller/new_base/hide_actions.rb
@@ -1,31 +1,39 @@
module ActionController
module HideActions
- setup do
+ extend ActiveSupport::Concern
+
+ included do
extlib_inheritable_accessor :hidden_actions
- self.hidden_actions ||= Set.new
+ self.hidden_actions ||= Set.new
end
-
- def action_methods() self.class.action_names end
- def action_names() action_methods end
-
- private
-
- def respond_to_action?(action_name)
- !hidden_actions.include?(action_name) && (super || respond_to?(:method_missing))
+
+ def action_methods
+ self.class.action_names
end
-
- module ClassMethods
- def hide_action(*args)
- args.each do |arg|
- self.hidden_actions << arg.to_s
- end
- end
-
- def action_methods
- @action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)})
- end
- def self.action_names() action_methods end
+ def action_names
+ action_methods
end
+
+ private
+ def action_method?(action_name)
+ !hidden_actions.include?(action_name) && super
+ end
+
+ module ClassMethods
+ def hide_action(*args)
+ args.each do |arg|
+ self.hidden_actions << arg.to_s
+ end
+ end
+
+ def action_methods
+ @action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)})
+ end
+
+ def self.action_names
+ action_methods
+ end
+ end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb
new file mode 100644
index 0000000000..c96aaaa865
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/http.rb
@@ -0,0 +1,91 @@
+require 'action_controller/abstract'
+require 'active_support/core_ext/module/delegation'
+
+module ActionController
+ class Http < AbstractController::Base
+ abstract!
+
+ # :api: public
+ attr_internal :params, :env
+
+ # :api: public
+ def self.controller_name
+ @controller_name ||= controller_path.split("/").last
+ end
+
+ # :api: public
+ def controller_name
+ self.class.controller_name
+ end
+
+ # :api: public
+ def self.controller_path
+ @controller_path ||= self.name.sub(/Controller$/, '').underscore
+ end
+
+ # :api: public
+ def controller_path
+ self.class.controller_path
+ end
+
+ # :api: private
+ def self.action_names
+ action_methods
+ end
+
+ # :api: private
+ def action_names
+ action_methods
+ end
+
+ # :api: plugin
+ def self.call(env)
+ controller = new
+ controller.call(env).to_rack
+ end
+
+ # The details below can be overridden to support a specific
+ # Request and Response object. The default ActionController::Base
+ # implementation includes RackConvenience, which makes a request
+ # and response object available. You might wish to control the
+ # environment and response manually for performance reasons.
+
+ attr_internal :status, :headers, :content_type
+
+ def initialize(*)
+ @_headers = {}
+ super
+ end
+
+ # Basic implements for content_type=, location=, and headers are
+ # provided to reduce the dependency on the RackConvenience module
+ # in Renderer and Redirector.
+
+ def content_type=(type)
+ headers["Content-Type"] = type.to_s
+ end
+
+ def location=(url)
+ headers["Location"] = url
+ end
+
+ # :api: private
+ def call(name, env)
+ @_env = env
+ process(name)
+ to_rack
+ end
+
+ # :api: private
+ def to_rack
+ [status, headers, response_body]
+ end
+
+ def self.action(name)
+ @actions ||= {}
+ @actions[name.to_s] ||= proc do |env|
+ new.call(name, env)
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/layouts.rb b/actionpack/lib/action_controller/new_base/layouts.rb
index a8e0809ac6..727358c394 100644
--- a/actionpack/lib/action_controller/new_base/layouts.rb
+++ b/actionpack/lib/action_controller/new_base/layouts.rb
@@ -1,37 +1,34 @@
module ActionController
module Layouts
+ extend ActiveSupport::Concern
+
depends_on ActionController::Renderer
depends_on AbstractController::Layouts
-
+
module ClassMethods
def _implied_layout_name
controller_path
end
end
-
- def render_to_body(options)
- # render :text => ..., :layout => ...
- # or
- # render :anything_else
- if !options.key?(:text) || options.key?(:layout)
- options[:_layout] = options.key?(:layout) ? _layout_for_option(options[:layout]) : _default_layout
+
+ private
+ def _determine_template(options)
+ super
+ if (!options.key?(:text) && !options.key?(:inline) && !options.key?(:partial)) || options.key?(:layout)
+ options[:_layout] = _layout_for_option(options.key?(:layout) ? options[:layout] : :none, options[:_template].details)
+ end
end
-
- super
- end
-
- private
-
- def _layout_for_option(name)
- case name
- when String then _layout_for_name(name)
- when true then _default_layout(true)
- when false, nil then nil
- else
- raise ArgumentError,
- "String, true, or false, expected for `layout'; you passed #{name.inspect}"
+
+ def _layout_for_option(name, details)
+ case name
+ when String then _layout_for_name(name, details)
+ when true then _default_layout(true, details)
+ when :none then _default_layout(false, details)
+ when false, nil then nil
+ else
+ raise ArgumentError,
+ "String, true, or false, expected for `layout'; you passed #{name.inspect}"
+ end
end
- end
-
end
end
diff --git a/actionpack/lib/action_controller/new_base/rack_convenience.rb b/actionpack/lib/action_controller/new_base/rack_convenience.rb
new file mode 100644
index 0000000000..5dfa7d12f3
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/rack_convenience.rb
@@ -0,0 +1,33 @@
+module ActionController
+ module RackConvenience
+ extend ActiveSupport::Concern
+
+ included do
+ delegate :headers, :status=, :location=,
+ :status, :location, :content_type, :to => "@_response"
+ attr_internal :request, :response
+ end
+
+ def call(name, env)
+ @_request = ActionDispatch::Request.new(env)
+ @_response = ActionDispatch::Response.new
+ @_response.request = request
+ super
+ end
+
+ def params
+ @_params ||= @_request.parameters
+ end
+
+ # :api: private
+ def to_rack
+ @_response.prepare!
+ @_response.to_a
+ end
+
+ def response_body=(body)
+ response.body = body if response
+ super
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/redirector.rb b/actionpack/lib/action_controller/new_base/redirector.rb
new file mode 100644
index 0000000000..20060b001f
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/redirector.rb
@@ -0,0 +1,19 @@
+module ActionController
+ class RedirectBackError < AbstractController::Error #:nodoc:
+ DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
+ module Redirector
+ def redirect_to(url, status) #:doc:
+ raise AbstractController::DoubleRenderError if response_body
+ logger.info("Redirected to #{url}") if logger && logger.info?
+ self.status = status
+ self.location = url.gsub(/[\r\n]/, '')
+ self.response_body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/render_options.rb b/actionpack/lib/action_controller/new_base/render_options.rb
new file mode 100644
index 0000000000..04b954134f
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/render_options.rb
@@ -0,0 +1,103 @@
+module ActionController
+ module RenderOptions
+ extend ActiveSupport::Concern
+
+ included do
+ extlib_inheritable_accessor :_renderers
+ self._renderers = []
+ end
+
+ module ClassMethods
+ def _write_render_options
+ renderers = _renderers.map do |r|
+ <<-RUBY_EVAL
+ if options.key?(:#{r})
+ _process_options(options)
+ return _render_#{r}(options[:#{r}], options)
+ end
+ RUBY_EVAL
+ end
+
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def _handle_render_options(options)
+ #{renderers.join}
+ end
+ RUBY_EVAL
+ end
+
+ def _add_render_option(name)
+ _renderers << name
+ _write_render_options
+ end
+ end
+
+ def render_to_body(options)
+ _handle_render_options(options) || super
+ end
+ end
+
+ module RenderOption #:nodoc:
+ def self.extended(base)
+ base.extend ActiveSupport::Concern
+ base.depends_on ::ActionController::RenderOptions
+
+ def base.register_renderer(name)
+ included { _add_render_option(name) }
+ end
+ end
+ end
+
+ module Renderers
+ module Json
+ extend RenderOption
+ register_renderer :json
+
+ def _render_json(json, options)
+ json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
+ json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
+ self.content_type ||= Mime::JSON
+ self.response_body = json
+ end
+ end
+
+ module Js
+ extend RenderOption
+ register_renderer :js
+
+ def _render_js(js, options)
+ self.content_type ||= Mime::JS
+ self.response_body = js
+ end
+ end
+
+ module Xml
+ extend RenderOption
+ register_renderer :xml
+
+ def _render_xml(xml, options)
+ self.content_type ||= Mime::XML
+ self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml
+ end
+ end
+
+ module RJS
+ extend RenderOption
+ register_renderer :update
+
+ def _render_update(proc, options)
+ generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(_action_view, &proc)
+ self.content_type = Mime::JS
+ self.response_body = generator.to_s
+ end
+ end
+
+ module All
+ extend ActiveSupport::Concern
+
+ depends_on ActionController::Renderers::Json
+ depends_on ActionController::Renderers::Js
+ depends_on ActionController::Renderers::Xml
+ depends_on ActionController::Renderers::RJS
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/renderer.rb b/actionpack/lib/action_controller/new_base/renderer.rb
index ed34c46aed..e132d4fdbb 100644
--- a/actionpack/lib/action_controller/new_base/renderer.rb
+++ b/actionpack/lib/action_controller/new_base/renderer.rb
@@ -1,62 +1,78 @@
module ActionController
module Renderer
+ extend ActiveSupport::Concern
+
depends_on AbstractController::Renderer
-
- def initialize(*)
- self.formats = [:html]
+
+ def process_action(*)
+ self.formats = request.formats.map {|x| x.to_sym}
super
end
-
- def render(action, options = {})
- # TODO: Move this into #render_to_body
- if action.is_a?(Hash)
- options, action = action, nil
- else
- options.merge! :action => action
+
+ def render(options)
+ super
+ options[:_template] ||= _action_view._partial
+ self.content_type ||= begin
+ mime = options[:_template].mime_type
+ formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
end
-
- _process_options(options)
-
- self.response_body = render_to_body(options)
+ response_body
end
def render_to_body(options)
- unless options.is_a?(Hash)
- options = {:action => options}
- end
+ _process_options(options)
- if options.key?(:text)
- options[:_template] = ActionView::TextTemplate.new(_text(options))
- template = nil
- elsif options.key?(:template)
- options[:_template_name] = options[:template]
- elsif options.key?(:action)
- options[:_template_name] = options[:action].to_s
- options[:_prefix] = _prefix
+ if options.key?(:partial)
+ _render_partial(options[:partial], options)
end
-
- super(options)
+
+ super
end
-
- private
-
- def _prefix
- controller_path
- end
-
- def _text(options)
- text = options[:text]
-
- case text
- when nil then " "
- else text.to_s
+
+ private
+ def _prefix
+ controller_path
end
- end
-
- def _process_options(options)
- if status = options[:status]
- response.status = status.to_i
+
+ def _determine_template(options)
+ if options.key?(:text)
+ options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)
+ elsif options.key?(:inline)
+ handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
+ template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
+ options[:_template] = template
+ elsif options.key?(:template)
+ options[:_template_name] = options[:template]
+ elsif options.key?(:file)
+ options[:_template_name] = options[:file]
+ elsif !options.key?(:partial)
+ options[:_template_name] = (options[:action] || action_name).to_s
+ options[:_prefix] = _prefix
+ end
+
+ super
+ end
+
+ def _render_partial(partial, options)
+ case partial
+ when true
+ options[:_prefix] = _prefix
+ when String
+ options[:_prefix] = _prefix unless partial.index('/')
+ options[:_template_name] = partial
+ else
+ options[:_partial_object] = true
+ return
+ end
+
+ options[:_partial] = options[:object] || true
+ 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
- end
end
end
diff --git a/actionpack/lib/action_controller/new_base/rescuable.rb b/actionpack/lib/action_controller/new_base/rescuable.rb
new file mode 100644
index 0000000000..029e643d93
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/rescuable.rb
@@ -0,0 +1,52 @@
+module ActionController #:nodoc:
+ # Actions that fail to perform as expected throw exceptions. These
+ # exceptions can either be rescued for the public view (with a nice
+ # user-friendly explanation) or for the developers view (with tons of
+ # debugging information). The developers view is already implemented by
+ # the Action Controller, but the public view should be tailored to your
+ # specific application.
+ #
+ # The default behavior for public exceptions is to render a static html
+ # file with the name of the error code thrown. If no such file exists, an
+ # empty response is sent with the correct status code.
+ #
+ # You can override what constitutes a local request by overriding the
+ # <tt>local_request?</tt> method in your own controller. Custom rescue
+ # behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
+ # and <tt>rescue_action_locally</tt> methods.
+ module Rescue
+ extend ActiveSupport::Concern
+
+ included do
+ include ActiveSupport::Rescuable
+ end
+
+ module ClassMethods
+ # This can be removed once we can move action(:_rescue_action) into middlewares.rb
+ # Currently, it does controller.method(:rescue_action), which is hiding the implementation
+ # difference between the old and new base.
+ def rescue_action(env)
+ action(:_rescue_action).call(env)
+ end
+ end
+
+ attr_internal :rescued_exception
+
+ private
+ def method_for_action(action_name)
+ return action_name if self.rescued_exception = request.env.delete("action_dispatch.rescue.exception")
+ super
+ end
+
+ def _rescue_action
+ rescue_with_handler(rescued_exception) || raise(rescued_exception)
+ end
+
+ def process_action(*)
+ super
+ rescue Exception => exception
+ self.rescued_exception = exception
+ _rescue_action
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/session.rb b/actionpack/lib/action_controller/new_base/session.rb
new file mode 100644
index 0000000000..a585630230
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/session.rb
@@ -0,0 +1,15 @@
+module ActionController
+ module Session
+ extend ActiveSupport::Concern
+
+ depends_on RackConvenience
+
+ def session
+ @_request.session
+ end
+
+ def reset_session
+ @_request.reset_session
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/testing.rb b/actionpack/lib/action_controller/new_base/testing.rb
new file mode 100644
index 0000000000..e8d210d149
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/testing.rb
@@ -0,0 +1,39 @@
+module ActionController
+ module Testing
+ extend ActiveSupport::Concern
+
+ depends_on RackConvenience
+
+ # OMG MEGA HAX
+ def process_with_new_base_test(request, response)
+ @_request = request
+ @_response = response
+ @_response.request = request
+ ret = process(request.parameters[:action])
+ @_response.body ||= self.response_body
+ @_response.prepare!
+ set_test_assigns
+ ret
+ end
+
+ def set_test_assigns
+ @assigns = {}
+ (instance_variable_names - self.class.protected_instance_variables).each do |var|
+ name, value = var[1..-1], instance_variable_get(var)
+ @assigns[name] = value
+ end
+ end
+
+ # TODO : Rewrite tests using controller.headers= to use Rack env
+ def headers=(new_headers)
+ @_response ||= ActionDispatch::Response.new
+ @_response.headers.replace(new_headers)
+ end
+
+ module ClassMethods
+ def before_filters
+ _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/url_for.rb b/actionpack/lib/action_controller/new_base/url_for.rb
index af5b21012b..902cac4d15 100644
--- a/actionpack/lib/action_controller/new_base/url_for.rb
+++ b/actionpack/lib/action_controller/new_base/url_for.rb
@@ -1,5 +1,14 @@
module ActionController
module UrlFor
+ extend ActiveSupport::Concern
+
+ depends_on RackConvenience
+
+ def process_action(*)
+ initialize_current_url
+ super
+ end
+
def initialize_current_url
@url = UrlRewriter.new(request, params.clone)
end
@@ -16,7 +25,7 @@ module ActionController
# by this method.
def default_url_options(options = nil)
end
-
+
def rewrite_options(options) #:nodoc:
if defaults = default_url_options(options)
defaults.merge(options)
@@ -24,7 +33,7 @@ module ActionController
options
end
end
-
+
def url_for(options = {})
options ||= {}
case options
@@ -37,4 +46,4 @@ module ActionController
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 6bda27e23a..b4408e4e1d 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module'
+
module ActionController
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
# Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
index c0eb61340b..ce59866531 100644
--- a/actionpack/lib/action_controller/routing.rb
+++ b/actionpack/lib/action_controller/routing.rb
@@ -1,5 +1,9 @@
require 'cgi'
require 'uri'
+require 'set'
+
+require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/module/attribute_accessors'
require 'action_controller/routing/optimisations'
require 'action_controller/routing/routing_ext'
require 'action_controller/routing/route'
diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb
index d9590c88b8..42ad12e1ea 100644
--- a/actionpack/lib/action_controller/routing/builder.rb
+++ b/actionpack/lib/action_controller/routing/builder.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/except'
+
module ActionController
module Routing
class RouteBuilder #:nodoc:
diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb
index e2077edad8..eba05a3c5a 100644
--- a/actionpack/lib/action_controller/routing/route.rb
+++ b/actionpack/lib/action_controller/routing/route.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/misc'
+
module ActionController
module Routing
class Route #:nodoc:
@@ -65,7 +67,7 @@ module ActionController
# map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
#
def parameter_shell
- @parameter_shell ||= returning({}) do |shell|
+ @parameter_shell ||= {}.tap do |shell|
requirements.each do |key, requirement|
shell[key] = requirement unless requirement.is_a? Regexp
end
@@ -76,7 +78,7 @@ module ActionController
# includes keys that appear inside the path, and keys that have requirements
# placed upon them.
def significant_keys
- @significant_keys ||= returning([]) do |sk|
+ @significant_keys ||= [].tap do |sk|
segments.each { |segment| sk << segment.key if segment.respond_to? :key }
sk.concat requirements.keys
sk.uniq!
@@ -86,7 +88,7 @@ module ActionController
# Return a hash of key/value pairs representing the keys in the route that
# have defaults, or which are specified by non-regexp requirements.
def defaults
- @defaults ||= returning({}) do |hash|
+ @defaults ||= {}.tap do |hash|
segments.each do |segment|
next unless segment.respond_to? :default
hash[segment.key] = segment.default unless segment.default.nil?
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
index 70cd1f642d..45ad8a3a3b 100644
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ b/actionpack/lib/action_controller/routing/route_set.rb
@@ -430,7 +430,7 @@ module ActionController
def call(env)
request = ActionDispatch::Request.new(env)
app = Routing::Routes.recognize(request)
- app.call(env).to_a
+ app.action(request.parameters[:action] || 'index').call(env)
end
def recognize(request)
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
index 4f936d51d2..2603855476 100644
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ b/actionpack/lib/action_controller/routing/segments.rb
@@ -1,7 +1,7 @@
module ActionController
module Routing
class Segment #:nodoc:
- RESERVED_PCHAR = ':@&=+$,;'
+ RESERVED_PCHAR = ':@&=+$,;%'
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
if RUBY_VERSION >= '1.9'
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
diff --git a/actionpack/lib/action_controller/testing/assertions/response.rb b/actionpack/lib/action_controller/testing/assertions/response.rb
deleted file mode 100644
index ca0a9bbf52..0000000000
--- a/actionpack/lib/action_controller/testing/assertions/response.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-module ActionController
- module Assertions
- # A small suite of assertions that test responses from Rails applications.
- module ResponseAssertions
- # Asserts that the response is one of the following types:
- #
- # * <tt>:success</tt> - Status code was 200
- # * <tt>:redirect</tt> - Status code was in the 300-399 range
- # * <tt>:missing</tt> - Status code was 404
- # * <tt>:error</tt> - Status code was in the 500-599 range
- #
- # You can also pass an explicit status number like assert_response(501)
- # or its symbolic equivalent assert_response(:not_implemented).
- # See ActionDispatch::StatusCodes for a full list.
- #
- # ==== Examples
- #
- # # assert that the response was a redirection
- # assert_response :redirect
- #
- # # assert that the response code was status code 401 (unauthorized)
- # assert_response 401
- #
- def assert_response(type, message = nil)
- clean_backtrace do
- if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
- assert_block("") { true } # to count the assertion
- elsif type.is_a?(Fixnum) && @response.response_code == type
- assert_block("") { true } # to count the assertion
- elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
- assert_block("") { true } # to count the assertion
- else
- if @response.error?
- exception = @response.template.instance_variable_get(:@exception)
- exception_message = exception && exception.message
- assert_block(build_message(message, "Expected response to be a <?>, but was <?>\n<?>", type, @response.response_code, exception_message.to_s)) { false }
- else
- assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
- end
- end
- end
- end
-
- # Assert that the redirection options passed in match those of the redirect called in the latest action.
- # This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
- # match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
- #
- # ==== Examples
- #
- # # assert that the redirection was to the "index" action on the WeblogController
- # assert_redirected_to :controller => "weblog", :action => "index"
- #
- # # assert that the redirection was to the named route login_url
- # assert_redirected_to login_url
- #
- # # assert that the redirection was to the url for @customer
- # assert_redirected_to @customer
- #
- def assert_redirected_to(options = {}, message=nil)
- clean_backtrace do
- assert_response(:redirect, message)
- return true if options == @response.redirected_to
-
- # Support partial arguments for hash redirections
- if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
- return true if options.all? {|(key, value)| @response.redirected_to[key] == value}
- end
-
- redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to)
- options_after_normalisation = normalize_argument_to_redirection(options)
-
- if redirected_to_after_normalisation != options_after_normalisation
- flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"
- end
- end
- end
-
- # Asserts that the request was rendered with the appropriate template file or partials
- #
- # ==== Examples
- #
- # # assert that the "new" view template was rendered
- # assert_template "new"
- #
- # # assert that the "_customer" partial was rendered twice
- # assert_template :partial => '_customer', :count => 2
- #
- # # assert that no partials were rendered
- # assert_template :partial => false
- #
- def assert_template(options = {}, message = nil)
- clean_backtrace do
- case options
- when NilClass, String
- rendered = @response.rendered[:template].to_s
- msg = build_message(message,
- "expecting <?> but rendering with <?>",
- options, rendered)
- assert_block(msg) do
- if options.nil?
- @response.rendered[:template].blank?
- else
- rendered.to_s.match(options)
- end
- end
- when Hash
- if expected_partial = options[:partial]
- partials = @response.rendered[:partials]
- if expected_count = options[:count]
- found = partials.detect { |p, _| p.to_s.match(expected_partial) }
- actual_count = found.nil? ? 0 : found.second
- msg = build_message(message,
- "expecting ? to be rendered ? time(s) but rendered ? time(s)",
- expected_partial, expected_count, actual_count)
- assert(actual_count == expected_count.to_i, msg)
- else
- msg = build_message(message,
- "expecting partial <?> but action rendered <?>",
- options[:partial], partials.keys)
- assert(partials.keys.any? { |p| p.to_s.match(expected_partial) }, msg)
- end
- else
- assert @response.rendered[:partials].empty?,
- "Expected no partials to be rendered"
- end
- end
- end
- end
-
- private
- # Proxy to to_param if the object will respond to it.
- def parameterize(value)
- value.respond_to?(:to_param) ? value.to_param : value
- 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
- end
- @request.protocol + @request.host_with_port + after_routing
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb
index d51b9b63ff..af4ccb7837 100644
--- a/actionpack/lib/action_controller/testing/integration.rb
+++ b/actionpack/lib/action_controller/testing/integration.rb
@@ -2,8 +2,119 @@ require 'stringio'
require 'uri'
require 'active_support/test_case'
+require 'rack/mock_session'
+require 'rack/test/cookie_jar'
+
module ActionController
module Integration #:nodoc:
+ module RequestHelpers
+ # Performs a GET request with the given parameters.
+ #
+ # - +path+: The URI (as a String) on which you want to perform a GET
+ # request.
+ # - +parameters+: The HTTP parameters that you want to pass. This may
+ # be +nil+,
+ # a Hash, or a String that is appropriately encoded
+ # (<tt>application/x-www-form-urlencoded</tt> or
+ # <tt>multipart/form-data</tt>).
+ # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
+ # automatically be upcased, with the prefix 'HTTP_' added if needed.
+ #
+ # This method returns an Response object, which one can use to
+ # inspect the details of the response. Furthermore, if this method was
+ # called from an ActionController::IntegrationTest object, then that
+ # object's <tt>@response</tt> instance variable will point to the same
+ # response object.
+ #
+ # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
+ # +put+, +delete+, and +head+.
+ def get(path, parameters = nil, headers = nil)
+ process :get, path, parameters, headers
+ end
+
+ # Performs a POST request with the given parameters. See get() for more
+ # details.
+ def post(path, parameters = nil, headers = nil)
+ process :post, path, parameters, headers
+ end
+
+ # Performs a PUT request with the given parameters. See get() for more
+ # details.
+ def put(path, parameters = nil, headers = nil)
+ process :put, path, parameters, headers
+ end
+
+ # Performs a DELETE request with the given parameters. See get() for
+ # more details.
+ def delete(path, parameters = nil, headers = nil)
+ process :delete, path, parameters, headers
+ end
+
+ # Performs a HEAD request with the given parameters. See get() for more
+ # details.
+ def head(path, parameters = nil, headers = nil)
+ process :head, path, parameters, headers
+ end
+
+ # Performs an XMLHttpRequest request with the given parameters, mirroring
+ # a request from the Prototype library.
+ #
+ # The request_method is :get, :post, :put, :delete or :head; the
+ # parameters are +nil+, a hash, or a url-encoded or multipart string;
+ # the headers are a hash. Keys are automatically upcased and prefixed
+ # with 'HTTP_' if not already.
+ def xml_http_request(request_method, path, parameters = nil, headers = nil)
+ headers ||= {}
+ headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
+ headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
+ process(request_method, path, parameters, headers)
+ end
+ alias xhr :xml_http_request
+
+ # Follow a single redirect response. If the last response was not a
+ # redirect, an exception will be raised. Otherwise, the redirect is
+ # performed on the location header.
+ def follow_redirect!
+ raise "not a redirect! #{status} #{status_message}" unless redirect?
+ get(response.location)
+ status
+ end
+
+ # Performs a request using the specified method, following any subsequent
+ # redirect. Note that the redirects are followed until the response is
+ # not a redirect--this means you may run into an infinite loop if your
+ # redirect loops back to itself.
+ def request_via_redirect(http_method, path, parameters = nil, headers = nil)
+ process(http_method, path, parameters, headers)
+ follow_redirect! while redirect?
+ status
+ end
+
+ # Performs a GET request, following any subsequent redirect.
+ # See +request_via_redirect+ for more information.
+ def get_via_redirect(path, parameters = nil, headers = nil)
+ request_via_redirect(:get, path, parameters, headers)
+ end
+
+ # Performs a POST request, following any subsequent redirect.
+ # See +request_via_redirect+ for more information.
+ def post_via_redirect(path, parameters = nil, headers = nil)
+ request_via_redirect(:post, path, parameters, headers)
+ end
+
+ # Performs a PUT request, following any subsequent redirect.
+ # See +request_via_redirect+ for more information.
+ def put_via_redirect(path, parameters = nil, headers = nil)
+ request_via_redirect(:put, path, parameters, headers)
+ end
+
+ # Performs a DELETE request, following any subsequent redirect.
+ # See +request_via_redirect+ for more information.
+ def delete_via_redirect(path, parameters = nil, headers = nil)
+ request_via_redirect(:delete, path, parameters, headers)
+ end
+ end
+
# An integration Session instance represents a set of requests and responses
# performed sequentially by some virtual user. Because you can instantiate
# multiple sessions and run them side-by-side, you can also mimic (to some
@@ -13,24 +124,20 @@ module ActionController
# IntegrationTest#open_session, rather than instantiating
# Integration::Session directly.
class Session
+ DEFAULT_HOST = "www.example.com"
+
include Test::Unit::Assertions
- include ActionController::TestCase::Assertions
+ include ActionDispatch::Assertions
include ActionController::TestProcess
+ include RequestHelpers
- # Rack application to use
- attr_accessor :application
-
- # The integer HTTP status code of the last request.
- attr_reader :status
-
- # The status message that accompanied the status code of the last request.
- attr_reader :status_message
-
- # The body of the last request.
- attr_reader :body
+ %w( status status_message headers body redirect? ).each do |method|
+ delegate method, :to => :response, :allow_nil => true
+ end
- # The URI of the last request.
- attr_reader :path
+ %w( path ).each do |method|
+ delegate method, :to => :request, :allow_nil => true
+ end
# The hostname used in the last request.
attr_accessor :host
@@ -43,10 +150,9 @@ module ActionController
# A map of the cookies returned by the last response, and which will be
# sent with the next request.
- attr_reader :cookies
-
- # A map of the headers returned by the last response.
- attr_reader :headers
+ def cookies
+ @mock_session.cookie_jar
+ end
# A reference to the controller instance used by the last request.
attr_reader :controller
@@ -60,12 +166,9 @@ module ActionController
# A running counter of the number of requests processed.
attr_accessor :request_count
- class MultiPartNeededException < Exception
- end
-
# Create and initialize a new Session instance.
def initialize(app = nil)
- @application = app || ActionController::Dispatcher.new
+ @app = app || ActionController::Dispatcher.new
reset!
end
@@ -75,14 +178,12 @@ module ActionController
#
# session.reset!
def reset!
- @status = @path = @headers = nil
- @result = @status_message = nil
@https = false
- @cookies = {}
+ @mock_session = Rack::MockSession.new(@app, DEFAULT_HOST)
@controller = @request = @response = nil
@request_count = 0
- self.host = "www.example.com"
+ self.host = DEFAULT_HOST
self.remote_addr = "127.0.0.1"
self.accept = "text/xml,application/xml,application/xhtml+xml," +
"text/html;q=0.9,text/plain;q=0.8,image/png," +
@@ -124,117 +225,6 @@ module ActionController
@host = name
end
- # Follow a single redirect response. If the last response was not a
- # redirect, an exception will be raised. Otherwise, the redirect is
- # performed on the location header.
- def follow_redirect!
- raise "not a redirect! #{@status} #{@status_message}" unless redirect?
- get(interpret_uri(headers['location']))
- status
- end
-
- # Performs a request using the specified method, following any subsequent
- # redirect. Note that the redirects are followed until the response is
- # not a redirect--this means you may run into an infinite loop if your
- # redirect loops back to itself.
- def request_via_redirect(http_method, path, parameters = nil, headers = nil)
- send(http_method, path, parameters, headers)
- follow_redirect! while redirect?
- status
- end
-
- # Performs a GET request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def get_via_redirect(path, parameters = nil, headers = nil)
- request_via_redirect(:get, path, parameters, headers)
- end
-
- # Performs a POST request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def post_via_redirect(path, parameters = nil, headers = nil)
- request_via_redirect(:post, path, parameters, headers)
- end
-
- # Performs a PUT request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def put_via_redirect(path, parameters = nil, headers = nil)
- request_via_redirect(:put, path, parameters, headers)
- end
-
- # Performs a DELETE request, following any subsequent redirect.
- # See +request_via_redirect+ for more information.
- def delete_via_redirect(path, parameters = nil, headers = nil)
- request_via_redirect(:delete, path, parameters, headers)
- end
-
- # Returns +true+ if the last response was a redirect.
- def redirect?
- status/100 == 3
- end
-
- # Performs a GET request with the given parameters.
- #
- # - +path+: The URI (as a String) on which you want to perform a GET
- # request.
- # - +parameters+: The HTTP parameters that you want to pass. This may
- # be +nil+,
- # a Hash, or a String that is appropriately encoded
- # (<tt>application/x-www-form-urlencoded</tt> or
- # <tt>multipart/form-data</tt>).
- # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
- # automatically be upcased, with the prefix 'HTTP_' added if needed.
- #
- # This method returns an Response object, which one can use to
- # inspect the details of the response. Furthermore, if this method was
- # called from an ActionController::IntegrationTest object, then that
- # object's <tt>@response</tt> instance variable will point to the same
- # response object.
- #
- # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
- # +put+, +delete+, and +head+.
- def get(path, parameters = nil, headers = nil)
- process :get, path, parameters, headers
- end
-
- # Performs a POST request with the given parameters. See get() for more
- # details.
- def post(path, parameters = nil, headers = nil)
- process :post, path, parameters, headers
- end
-
- # Performs a PUT request with the given parameters. See get() for more
- # details.
- def put(path, parameters = nil, headers = nil)
- process :put, path, parameters, headers
- end
-
- # Performs a DELETE request with the given parameters. See get() for
- # more details.
- def delete(path, parameters = nil, headers = nil)
- process :delete, path, parameters, headers
- end
-
- # Performs a HEAD request with the given parameters. See get() for more
- # details.
- def head(path, parameters = nil, headers = nil)
- process :head, path, parameters, headers
- end
-
- # Performs an XMLHttpRequest request with the given parameters, mirroring
- # a request from the Prototype library.
- #
- # The request_method is :get, :post, :put, :delete or :head; the
- # parameters are +nil+, a hash, or a url-encoded or multipart string;
- # the headers are a hash. Keys are automatically upcased and prefixed
- # with 'HTTP_' if not already.
- def xml_http_request(request_method, path, parameters = nil, headers = nil)
- headers ||= {}
- headers['X-Requested-With'] = 'XMLHttpRequest'
- headers['Accept'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
- process(request_method, path, parameters, headers)
- end
- alias xhr :xml_http_request
-
# Returns the URL for the given options, according to the rules specified
# in the application's routes.
def url_for(options)
@@ -244,137 +234,54 @@ module ActionController
end
private
- # Tailors the session based on the given URI, setting the HTTPS value
- # and the hostname.
- def interpret_uri(path)
- location = URI.parse(path)
- https! URI::HTTPS === location if location.scheme
- host! location.host if location.host
- location.query ? "#{location.path}?#{location.query}" : location.path
- end
# Performs the actual request.
- def process(method, path, parameters = nil, headers = nil)
- data = requestify(parameters)
- path = interpret_uri(path) if path =~ %r{://}
- path = "/#{path}" unless path[0] == ?/
- @path = path
- env = {}
-
- if method == :get
- env["QUERY_STRING"] = data
- data = nil
+ def process(method, path, parameters = nil, rack_environment = nil)
+ if path =~ %r{://}
+ location = URI.parse(path)
+ https! URI::HTTPS === location if location.scheme
+ host! location.host if location.host
+ path = location.query ? "#{location.path}?#{location.query}" : location.path
end
- env["QUERY_STRING"] ||= ""
+ [ControllerCapture, ActionController::ProcessWithTest].each do |mod|
+ unless ActionController::Base < mod
+ ActionController::Base.class_eval { include mod }
+ end
+ end
- data = data.is_a?(IO) ? data : StringIO.new(data || '')
+ opts = {
+ :method => method,
+ :params => parameters,
- env.update(
- "REQUEST_METHOD" => method.to_s.upcase,
"SERVER_NAME" => host,
"SERVER_PORT" => (https? ? "443" : "80"),
"HTTPS" => https? ? "on" : "off",
"rack.url_scheme" => https? ? "https" : "http",
- "SCRIPT_NAME" => "",
"REQUEST_URI" => path,
"PATH_INFO" => path,
"HTTP_HOST" => host,
"REMOTE_ADDR" => remote_addr,
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
- "CONTENT_LENGTH" => data ? data.length.to_s : nil,
- "HTTP_COOKIE" => encode_cookies,
- "HTTP_ACCEPT" => accept,
-
- "rack.version" => [0,1],
- "rack.input" => data,
- "rack.errors" => StringIO.new,
- "rack.multithread" => true,
- "rack.multiprocess" => true,
- "rack.run_once" => false,
-
- "rack.test" => true
- )
-
- (headers || {}).each do |key, value|
- key = key.to_s.upcase.gsub(/-/, "_")
- key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
- env[key] = value
- end
+ "HTTP_ACCEPT" => accept
+ }
+ env = Rack::MockRequest.env_for(path, opts)
- [ControllerCapture, ActionController::ProcessWithTest].each do |mod|
- unless ActionController::Base < mod
- ActionController::Base.class_eval { include mod }
- end
+ (rack_environment || {}).each do |key, value|
+ env[key] = value
end
- ActionController::Base.clear_last_instantiation!
-
- app = @application
- # Rack::Lint doesn't accept String headers or bodies in Ruby 1.9
- unless RUBY_VERSION >= '1.9.0' && Rack.release <= '0.9.0'
- app = Rack::Lint.new(app)
+ @controller = ActionController::Base.capture_instantiation do
+ @mock_session.request(URI.parse(path), env)
end
- status, headers, body = app.call(env)
@request_count += 1
-
+ @request = ActionDispatch::Request.new(env)
+ @response = ActionDispatch::TestResponse.from_response(@mock_session.last_response)
@html_document = nil
- @status = status.to_i
- @status_message = ActionDispatch::StatusCodes::STATUS_CODES[@status]
-
- @headers = Rack::Utils::HeaderHash.new(headers)
-
- (@headers['Set-Cookie'] || "").split("\n").each do |cookie|
- name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2]
- @cookies[name] = value
- end
-
- if body.is_a?(String)
- @body_parts = [body]
- @body = body
- else
- @body_parts = []
- body.each { |part| @body_parts << part.to_s }
- @body = @body_parts.join
- end
-
- if @controller = ActionController::Base.last_instantiation
- @request = @controller.request
- @response = @controller.response
- @controller.send(:set_test_assigns)
- else
- # Decorate responses from Rack Middleware and Rails Metal
- # as an Response for the purposes of integration testing
- @response = ActionDispatch::Response.new
- @response.status = status.to_s
- @response.headers.replace(@headers)
- @response.body = @body_parts
- end
-
- # Decorate the response with the standard behavior of the
- # TestResponse so that things like assert_response can be
- # used in integration tests.
- @response.extend(TestResponseBehavior)
-
- return @status
- rescue MultiPartNeededException
- boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
- status = process(method, path,
- multipart_body(parameters, boundary),
- (headers || {}).merge(
- {"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
- return status
- end
-
- # Encode the cookies hash in a format suitable for passing to a
- # request.
- def encode_cookies
- cookies.inject("") do |string, (name, value)|
- string << "#{name}=#{value}; "
- end
+ return response.status
end
# Get a temporary URL writer object
@@ -389,97 +296,29 @@ module ActionController
}
UrlRewriter.new(ActionDispatch::Request.new(env), {})
end
-
- def name_with_prefix(prefix, name)
- prefix ? "#{prefix}[#{name}]" : name.to_s
- end
-
- # Convert the given parameters to a request string. The parameters may
- # be a string, +nil+, or a Hash.
- def requestify(parameters, prefix=nil)
- if TestUploadedFile === parameters
- raise MultiPartNeededException
- elsif Hash === parameters
- return nil if parameters.empty?
- parameters.map { |k,v|
- requestify(v, name_with_prefix(prefix, k))
- }.join("&")
- elsif Array === parameters
- parameters.map { |v|
- requestify(v, name_with_prefix(prefix, ""))
- }.join("&")
- elsif prefix.nil?
- parameters
- else
- "#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
- end
- end
-
- def multipart_requestify(params, first=true)
- returning Hash.new do |p|
- params.each do |key, value|
- k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]"
- if Hash === value
- multipart_requestify(value, false).each do |subkey, subvalue|
- p[k + subkey] = subvalue
- end
- else
- p[k] = value
- end
- end
- end
- end
-
- def multipart_body(params, boundary)
- multipart_requestify(params).map do |key, value|
- if value.respond_to?(:original_filename)
- File.open(value.path, "rb") do |f|
- f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
-
- <<-EOF
---#{boundary}\r
-Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r
-Content-Type: #{value.content_type}\r
-Content-Length: #{File.stat(value.path).size}\r
-\r
-#{f.read}\r
-EOF
- end
- else
-<<-EOF
---#{boundary}\r
-Content-Disposition: form-data; name="#{key}"\r
-\r
-#{value}\r
-EOF
- end
- end.join("")+"--#{boundary}--\r"
- end
end
# A module used to extend ActionController::Base, so that integration tests
# can capture the controller used to satisfy a request.
module ControllerCapture #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- base.class_eval do
- class << self
- alias_method_chain :new, :capture
- end
- end
+ extend ActiveSupport::Concern
+
+ included do
+ alias_method_chain :initialize, :capture
+ end
+
+ def initialize_with_capture(*args)
+ initialize_without_capture
+ self.class.last_instantiation ||= self
end
module ClassMethods #:nodoc:
mattr_accessor :last_instantiation
- def clear_last_instantiation!
+ def capture_instantiation
self.last_instantiation = nil
- end
-
- def new_with_capture(*args)
- controller = new_without_capture(*args)
- self.last_instantiation ||= controller
- controller
+ yield
+ return last_instantiation
end
end
end
@@ -513,8 +352,8 @@ EOF
# By default, a single session is automatically created for you, but you
# can use this method to open multiple sessions that ought to be tested
# simultaneously.
- def open_session(application = nil)
- session = Integration::Session.new(application)
+ def open_session(app = nil)
+ session = Integration::Session.new(app)
# delegate the fixture accessors back to the test instance
extras = Module.new { attr_accessor :delegate, :test_result }
@@ -636,54 +475,5 @@ EOF
# end
class IntegrationTest < ActiveSupport::TestCase
include Integration::Runner
-
- # Work around a bug in test/unit caused by the default test being named
- # as a symbol (:default_test), which causes regex test filters
- # (like "ruby test.rb -n /foo/") to fail because =~ doesn't work on
- # symbols.
- def initialize(name) #:nodoc:
- super(name.to_s)
- end
-
- # Work around test/unit's requirement that every subclass of TestCase have
- # at least one test method. Note that this implementation extends to all
- # subclasses, as well, so subclasses of IntegrationTest may also exist
- # without any test methods.
- def run(*args) #:nodoc:
- return if @method_name == "default_test"
- super
- end
-
- # Because of how use_instantiated_fixtures and use_transactional_fixtures
- # are defined, we need to treat them as special cases. Otherwise, users
- # would potentially have to set their values for both Test::Unit::TestCase
- # ActionController::IntegrationTest, since by the time the value is set on
- # TestCase, IntegrationTest has already been defined and cannot inherit
- # changes to those variables. So, we make those two attributes
- # copy-on-write.
-
- class << self
- def use_transactional_fixtures=(flag) #:nodoc:
- @_use_transactional_fixtures = true
- @use_transactional_fixtures = flag
- end
-
- def use_instantiated_fixtures=(flag) #:nodoc:
- @_use_instantiated_fixtures = true
- @use_instantiated_fixtures = flag
- end
-
- def use_transactional_fixtures #:nodoc:
- @_use_transactional_fixtures ?
- @use_transactional_fixtures :
- superclass.use_transactional_fixtures
- end
-
- def use_instantiated_fixtures #:nodoc:
- @_use_instantiated_fixtures ?
- @use_instantiated_fixtures :
- superclass.use_instantiated_fixtures
- end
- end
end
end
diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb
index 7e2857614c..9647f8ce45 100644
--- a/actionpack/lib/action_controller/testing/process.rb
+++ b/actionpack/lib/action_controller/testing/process.rb
@@ -1,94 +1,13 @@
require 'rack/session/abstract/id'
-module ActionController #:nodoc:
- class TestRequest < ActionDispatch::Request #:nodoc:
- attr_accessor :cookies, :session_options
- attr_accessor :query_parameters, :path, :session
- attr_accessor :host
-
- def self.new(env = {})
- super
- end
+require 'active_support/core_ext/object/conversions'
+module ActionController #:nodoc:
+ class TestRequest < ActionDispatch::TestRequest #:nodoc:
def initialize(env = {})
- super(Rack::MockRequest.env_for("/").merge(env))
-
- @query_parameters = {}
- @session = TestSession.new
- default_rack_options = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
- @session_options ||= {:id => generate_sid(default_rack_options[:sidbits])}.merge(default_rack_options)
-
- initialize_default_values
- initialize_containers
- end
-
- def reset_session
- @session.reset
- end
-
- # Wraps raw_post in a StringIO.
- def body_stream #:nodoc:
- StringIO.new(raw_post)
- end
-
- # Either the RAW_POST_DATA environment variable or the URL-encoded request
- # parameters.
- def raw_post
- @env['RAW_POST_DATA'] ||= begin
- data = url_encoded_request_parameters
- data.force_encoding(Encoding::BINARY) if data.respond_to?(:force_encoding)
- data
- end
- end
-
- def port=(number)
- @env["SERVER_PORT"] = number.to_i
- end
-
- def action=(action_name)
- @query_parameters.update({ "action" => action_name })
- @parameters = nil
- end
-
- # Used to check AbstractRequest's request_uri functionality.
- # Disables the use of @path and @request_uri so superclass can handle those.
- def set_REQUEST_URI(value)
- @env["REQUEST_URI"] = value
- @request_uri = nil
- @path = nil
- end
-
- def request_uri=(uri)
- @request_uri = uri
- @path = uri.split("?").first
- end
-
- def request_method=(method)
- @request_method = method
- end
-
- def accept=(mime_types)
- @env["HTTP_ACCEPT"] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",")
- @accepts = nil
- end
-
- def if_modified_since=(last_modified)
- @env["HTTP_IF_MODIFIED_SINCE"] = last_modified
- end
-
- def if_none_match=(etag)
- @env["HTTP_IF_NONE_MATCH"] = etag
- end
-
- def remote_addr=(addr)
- @env['REMOTE_ADDR'] = addr
- end
-
- def request_uri(*args)
- @request_uri || super()
- end
+ super
- def path(*args)
- @path || super()
+ self.session = TestSession.new
+ self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16))
end
def assign_parameters(controller_path, action, parameters)
@@ -108,247 +27,46 @@ module ActionController #:nodoc:
path_parameters[key.to_s] = value
end
end
- raw_post # populate env['RAW_POST_DATA']
- @parameters = nil # reset TestRequest#parameters to use the new path_parameters
- end
- def recycle!
- @env["action_controller.request.request_parameters"] = {}
- self.query_parameters = {}
- self.path_parameters = {}
- @headers, @request_method, @accepts, @content_type = nil, nil, nil, nil
- end
-
- def user_agent=(user_agent)
- @env['HTTP_USER_AGENT'] = user_agent
- end
-
- private
- def generate_sid(sidbits)
- "%0#{sidbits / 4}x" % rand(2**sidbits - 1)
- end
-
- def initialize_containers
- @cookies = {}
- end
-
- def initialize_default_values
- @host = "test.host"
- @request_uri = "/"
- @env['HTTP_USER_AGENT'] = "Rails Testing"
- @env['REMOTE_ADDR'] = "0.0.0.0"
- @env["SERVER_PORT"] = 80
- @env['REQUEST_METHOD'] = "GET"
- end
-
- def url_encoded_request_parameters
- params = self.request_parameters.dup
-
- %w(controller action only_path).each do |k|
- params.delete(k)
- params.delete(k.to_sym)
- end
+ params = self.request_parameters.dup
- params.to_query
+ %w(controller action only_path).each do |k|
+ params.delete(k)
+ params.delete(k.to_sym)
end
- end
-
- # A refactoring of TestResponse to allow the same behavior to be applied
- # to the "real" CgiResponse class in integration tests.
- module TestResponseBehavior #:nodoc:
- # The response code of the request
- def response_code
- status.to_s[0,3].to_i rescue 0
- end
-
- # Returns a String to ensure compatibility with Net::HTTPResponse
- def code
- status.to_s.split(' ')[0]
- end
-
- def message
- status.to_s.split(' ',2)[1]
- end
-
- # Was the response successful?
- def success?
- (200..299).include?(response_code)
- end
- # Was the URL not found?
- def missing?
- response_code == 404
+ data = params.to_query
+ @env['CONTENT_LENGTH'] = data.length.to_s
+ @env['rack.input'] = StringIO.new(data)
end
- # Were we redirected?
- def redirect?
- (300..399).include?(response_code)
- end
-
- # Was there a server-side error?
- def error?
- (500..599).include?(response_code)
- end
-
- alias_method :server_error?, :error?
-
- # Was there a client client?
- def client_error?
- (400..499).include?(response_code)
- end
-
- # Returns the redirection location or nil
- def redirect_url
- headers['Location']
- end
-
- # Does the redirect location match this regexp pattern?
- def redirect_url_match?( pattern )
- return false if redirect_url.nil?
- p = Regexp.new(pattern) if pattern.class == String
- p = pattern if pattern.class == Regexp
- return false if p.nil?
- p.match(redirect_url) != nil
- end
-
- # Returns the template of the file which was used to
- # render this response (or nil)
- def rendered
- template.instance_variable_get(:@_rendered)
- end
-
- # A shortcut to the flash. Returns an empty hash if no session flash exists.
- def flash
- session['flash'] || {}
- end
-
- # Do we have a flash?
- def has_flash?
- !flash.empty?
- end
-
- # Do we have a flash that has contents?
- def has_flash_with_contents?
- !flash.empty?
- end
-
- # Does the specified flash object exist?
- def has_flash_object?(name=nil)
- !flash[name].nil?
- end
-
- # Does the specified object exist in the session?
- def has_session_object?(name=nil)
- !session[name].nil?
- end
-
- # A shortcut to the template.assigns
- def template_objects
- template.assigns || {}
- end
-
- # Does the specified template object exist?
- def has_template_object?(name=nil)
- !template_objects[name].nil?
- end
-
- # Returns the response cookies, converted to a Hash of (name => value) pairs
- #
- # assert_equal 'AuthorOfNewPage', r.cookies['author']
- def cookies
- cookies = {}
- Array(headers['Set-Cookie']).each do |cookie|
- key, value = cookie.split(";").first.split("=").map {|val| Rack::Utils.unescape(val)}
- cookies[key] = value
- end
- cookies
- end
-
- # Returns binary content (downloadable file), converted to a String
- def binary_content
- raise "Response body is not a Proc: #{body_parts.inspect}" unless body_parts.kind_of?(Proc)
- require 'stringio'
-
- sio = StringIO.new
- body_parts.call(self, sio)
-
- sio.rewind
- sio.read
- end
- end
-
- # Integration test methods such as ActionController::Integration::Session#get
- # and ActionController::Integration::Session#post return objects of class
- # TestResponse, which represent the HTTP response results of the requested
- # controller actions.
- #
- # See Response for more information on controller response objects.
- class TestResponse < ActionDispatch::Response
- include TestResponseBehavior
-
def recycle!
- body_parts.clear
- headers.delete('ETag')
- headers.delete('Last-Modified')
+ @formats = nil
+ @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
+ @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
+ @env['action_dispatch.request.query_parameters'] = {}
end
end
- class TestSession < Hash #:nodoc:
- attr_accessor :session_id
-
- def initialize(attributes = nil)
- reset_session_id
- replace_attributes(attributes)
- end
-
- def reset
- reset_session_id
- replace_attributes({ })
- end
-
- def data
- to_hash
- end
-
- def [](key)
- super(key.to_s)
- end
-
- def []=(key, value)
- super(key.to_s, value)
- end
-
- def update(hash = nil)
- if hash.nil?
- ActiveSupport::Deprecation.warn('use replace instead', caller)
- replace({})
- else
- super(hash)
- end
- end
-
- def delete(key = nil)
- if key.nil?
- ActiveSupport::Deprecation.warn('use clear instead', caller)
- clear
- else
- super(key.to_s)
- end
- end
+ class TestResponse < ActionDispatch::TestResponse
+ def recycle!
+ @status = 200
+ @header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS)
+ @writer = lambda { |x| @body << x }
+ @block = nil
+ @length = 0
+ @body = []
- def close
- ActiveSupport::Deprecation.warn('sessions should no longer be closed', caller)
+ @request = @template = nil
end
+ end
- private
-
- def reset_session_id
- @session_id = ''
- end
+ class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
+ DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
- def replace_attributes(attributes = nil)
- attributes ||= {}
- replace(attributes.stringify_keys)
+ def initialize(session = {})
+ replace(session.stringify_keys)
+ @loaded = true
end
end
@@ -363,34 +81,7 @@ module ActionController #:nodoc:
#
# Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows):
# post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary)
- require 'tempfile'
- class TestUploadedFile
- # The filename, *not* including the path, of the "uploaded" file
- attr_reader :original_filename
-
- # The content type of the "uploaded" file
- attr_accessor :content_type
-
- def initialize(path, content_type = Mime::TEXT, binary = false)
- raise "#{path} file does not exist" unless File.exist?(path)
- @content_type = content_type
- @original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
- @tempfile = Tempfile.new(@original_filename)
- @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
- @tempfile.binmode if binary
- FileUtils.copy_file(path, @tempfile.path)
- end
-
- def path #:nodoc:
- @tempfile.path
- end
-
- alias local_path path
-
- def method_missing(method_name, *args, &block) #:nodoc:
- @tempfile.__send__(method_name, *args, &block)
- end
- end
+ TestUploadedFile = Rack::Utils::Multipart::UploadedFile
module TestProcess
def self.included(base)
@@ -433,9 +124,7 @@ module ActionController #:nodoc:
@response.recycle!
@html_document = nil
- @request.env['REQUEST_METHOD'] = http_method
-
- @request.action = action.to_s
+ @request.request_method = http_method
parameters ||= {}
@request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
@@ -445,7 +134,19 @@ module ActionController #:nodoc:
build_request_uri(action, parameters)
Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
- @controller.process_with_test(@request, @response)
+
+ env = @request.env
+ app = @controller
+
+ # TODO: Enable Lint
+ # app = Rack::Lint.new(app)
+
+ status, headers, body = app.action(action, env)
+ response = Rack::MockResponse.new(status, headers, body)
+
+ @response.request, @response.template = @request, @controller.template
+ @response.status, @response.headers, @response.body = response.status, response.headers, response.body
+ @response
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@@ -459,11 +160,13 @@ module ActionController #:nodoc:
alias xhr :xml_http_request
def assigns(key = nil)
- if key.nil?
- @response.template.assigns
- else
- @response.template.assigns[key.to_s]
+ assigns = {}
+ @controller.instance_variable_names.each do |ivar|
+ next if ActionController::Base.protected_instance_variables.include?(ivar)
+ assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
end
+
+ key.nil? ? assigns : assigns[key.to_s]
end
def session
@@ -471,7 +174,7 @@ module ActionController #:nodoc:
end
def flash
- @response.flash
+ @request.flash
end
def cookies
@@ -488,7 +191,7 @@ module ActionController #:nodoc:
options.update(:only_path => true, :action => action)
url = ActionController::UrlRewriter.new(@request, parameters)
- @request.set_REQUEST_URI(url.rewrite(options))
+ @request.request_uri = url.rewrite(options)
end
end
@@ -561,11 +264,14 @@ module ActionController #:nodoc:
module ProcessWithTest #:nodoc:
def self.included(base)
- base.class_eval { attr_reader :assigns }
+ base.class_eval {
+ attr_reader :assigns
+ alias_method_chain :process, :test
+ }
end
def process_with_test(*args)
- process(*args).tap { set_test_assigns }
+ process_without_test(*args).tap { set_test_assigns }
end
private
@@ -574,7 +280,7 @@ module ActionController #:nodoc:
(instance_variable_names - self.class.protected_instance_variables).each do |var|
name, value = var[1..-1], instance_variable_get(var)
@assigns[name] = value
- response.template.assigns[name] = value if response
+ @template.assigns[name] = value if response
end
end
end
diff --git a/actionpack/lib/action_controller/testing/process2.rb b/actionpack/lib/action_controller/testing/process2.rb
new file mode 100644
index 0000000000..1c6fd2d80a
--- /dev/null
+++ b/actionpack/lib/action_controller/testing/process2.rb
@@ -0,0 +1,74 @@
+require "action_controller/testing/process"
+
+module ActionController
+ module TestProcess
+
+ # Executes a request simulating GET HTTP method and set/volley the response
+ def get(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "GET")
+ end
+
+ # Executes a request simulating POST HTTP method and set/volley the response
+ def post(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "POST")
+ end
+
+ # Executes a request simulating PUT HTTP method and set/volley the response
+ def put(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "PUT")
+ end
+
+ # Executes a request simulating DELETE HTTP method and set/volley the response
+ def delete(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "DELETE")
+ end
+
+ # Executes a request simulating HEAD HTTP method and set/volley the response
+ def head(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "HEAD")
+ end
+
+ 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|
+ 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
+ end
+
+ @request.recycle!
+ @response.recycle!
+ @controller.response_body = nil
+ @controller.formats = nil
+ @controller.params = nil
+
+ @html_document = nil
+ @request.env['REQUEST_METHOD'] = http_method
+
+ parameters ||= {}
+ @request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
+
+ @request.session = ActionController::TestSession.new(session) unless session.nil?
+ @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
+
+ @controller.request = @request
+ @controller.params.merge!(parameters)
+ build_request_uri(action, parameters)
+ # Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
+ @controller.process_with_new_base_test(@request, @response)
+ @response
+ end
+
+ 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)
+ end
+ end
+
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb
index b020b755a0..7b4eda58e5 100644
--- a/actionpack/lib/action_controller/testing/test_case.rb
+++ b/actionpack/lib/action_controller/testing/test_case.rb
@@ -105,20 +105,7 @@ module ActionController
class TestCase < ActiveSupport::TestCase
include TestProcess
- module Assertions
- %w(response selector tag dom routing model).each do |kind|
- include ActionController::Assertions.const_get("#{kind.camelize}Assertions")
- end
-
- def clean_backtrace(&block)
- yield
- rescue ActiveSupport::TestCase::Assertion => error
- framework_path = Regexp.new(File.expand_path("#{File.dirname(__FILE__)}/assertions"))
- error.backtrace.reject! { |line| File.expand_path(line) =~ framework_path }
- raise
- end
- end
- include Assertions
+ include ActionDispatch::Assertions
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
# (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index ae20f9947c..a992f7d912 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -73,7 +73,7 @@ module HTML
# Specifies the default Set of tags that the #sanitize helper will allow unscathed.
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
- sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr
+ sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
acronym a img blockquote del ins))
# Specifies the default Set of html attributes that the #sanitize helper will leave
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index bd5a38cc82..884828a01a 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -21,35 +21,36 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift activesupport_path if File.directory?(activesupport_path)
+require 'active_support'
-$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-1.0"
begin
- gem 'rack', '~> 1.0.0'
- require 'rack'
-rescue Gem::LoadError
- require 'action_dispatch/vendor/rack-1.0/rack'
+ gem 'rack', '~> 1.1.pre'
+rescue Gem::LoadError, ArgumentError
+ $:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-1.1.pre"
end
+require 'rack'
+
+$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-test"
+
module ActionDispatch
autoload :Request, 'action_dispatch/http/request'
autoload :Response, 'action_dispatch/http/response'
autoload :StatusCodes, 'action_dispatch/http/status_codes'
- autoload :Failsafe, 'action_dispatch/middleware/failsafe'
+ autoload :Callbacks, 'action_dispatch/middleware/callbacks'
autoload :ParamsParser, 'action_dispatch/middleware/params_parser'
- autoload :Reloader, 'action_dispatch/middleware/reloader'
- autoload :RewindableInput, 'action_dispatch/middleware/rewindable_input'
+ autoload :Rescue, 'action_dispatch/middleware/rescue'
+ autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions'
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
+ autoload :HTML, 'action_controller/vendor/html-scanner'
+ autoload :Assertions, 'action_dispatch/testing/assertions'
+ autoload :TestRequest, 'action_dispatch/testing/test_request'
+ autoload :TestResponse, 'action_dispatch/testing/test_response'
+
module Http
autoload :Headers, 'action_dispatch/http/headers'
end
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 02ad7f7d94..25156a4c75 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -1,8 +1,9 @@
require 'set'
+require 'active_support/core_ext/class/attribute_accessors'
module Mime
SET = []
- EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
+ EXTENSION_LOOKUP = {}
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
def self.[](type)
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 2d7fba1173..7c28cac419 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -1,9 +1,9 @@
# Build list of Mime types for HTTP responses
# http://www.iana.org/assignments/media-types/
+Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
Mime::Type.register "*/*", :all
Mime::Type.register "text/plain", :text, [], %w(txt)
-Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
Mime::Type.register "text/css", :css
Mime::Type.register "text/calendar", :ics
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 523ab32b35..140feb9a68 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -3,6 +3,9 @@ require 'stringio'
require 'strscan'
require 'active_support/memoizable'
+require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/core_ext/object/tap'
module ActionDispatch
class Request < Rack::Request
@@ -31,7 +34,7 @@ module ActionDispatch
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
# constant above, an UnknownHttpMethod exception is raised.
def request_method
- @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(ActionController::UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ HTTP_METHOD_LOOKUP[super] || raise(ActionController::UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
end
# Returns the HTTP request \method used for action processing as a
@@ -85,7 +88,7 @@ module ActionDispatch
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
def content_type
- @content_type ||= begin
+ @env["action_dispatch.request.content_type"] ||= begin
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
Mime::Type.lookup($1.strip.downcase)
else
@@ -93,10 +96,14 @@ module ActionDispatch
end
end
end
-
+
+ def media_type
+ content_type.to_s
+ end
+
# Returns the accepted MIME type for the request.
def accepts
- @accepts ||= begin
+ @env["action_dispatch.request.accepts"] ||= begin
header = @env['HTTP_ACCEPT'].to_s.strip
fallback = xhr? ? Mime::JS : Mime::HTML
@@ -156,7 +163,7 @@ module ActionDispatch
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
def format(view_path = [])
- @format ||=
+ @env["action_dispatch.request.format"] ||=
if parameters[:format]
Mime[parameters[:format]]
elsif ActionController::Base.use_accept_header && !(accepts == ONLY_ALL)
@@ -167,12 +174,23 @@ module ActionDispatch
end
def formats
- @formats =
- if ActionController::Base.use_accept_header
- Array(Mime[parameters[:format]] || accepts)
+ if ActionController::Base.use_accept_header
+ if param = parameters[:format]
+ Array.wrap(Mime[param])
else
- [format]
+ accepts.dup
+ end.tap do |ret|
+ if defined?(ActionController::Http)
+ if ret == ONLY_ALL
+ ret.replace Mime::SET
+ elsif all = ret.index(Mime::ALL)
+ ret.delete_at(all) && ret.insert(all, *Mime::SET)
+ end
+ end
end
+ else
+ [format] + Mime::SET
+ end
end
# Sets the \format by string extension, which can be used to force custom formats
@@ -188,7 +206,7 @@ module ActionDispatch
# end
def format=(extension)
parameters[:format] = extension.to_s
- @format = Mime::Type.lookup_by_extension(parameters[:format])
+ @env["action_dispatch.request.format"] = Mime::Type.lookup_by_extension(parameters[:format])
end
# Returns a symbolized version of the <tt>:format</tt> parameter of the request.
@@ -324,6 +342,10 @@ EOM
port == standard_port ? '' : ":#{port}"
end
+ def server_port
+ @env['SERVER_PORT'].to_i
+ end
+
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
def domain(tld_length = 1)
@@ -344,7 +366,7 @@ EOM
# Returns the query string, accounting for server idiosyncrasies.
def query_string
- @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '')
+ @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
end
# Returns the request URI, accounting for server idiosyncrasies.
@@ -392,18 +414,19 @@ EOM
# Returns both GET and POST \parameters in a single hash.
def parameters
- @parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
+ @env["action_dispatch.request.parameters"] ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
end
alias_method :params, :parameters
def path_parameters=(parameters) #:nodoc:
- @env["rack.routing_args"] = parameters
- @symbolized_path_parameters = @parameters = nil
+ @env.delete("action_dispatch.request.symbolized_path_parameters")
+ @env.delete("action_dispatch.request.parameters")
+ @env["action_dispatch.request.path_parameters"] = parameters
end
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
def symbolized_path_parameters
- @symbolized_path_parameters ||= path_parameters.symbolize_keys
+ @env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
end
# Returns a hash with the \parameters used to form the \path of the request.
@@ -413,7 +436,7 @@ EOM
#
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
def path_parameters
- @env["rack.routing_args"] ||= {}
+ @env["action_dispatch.request.path_parameters"] ||= {}
end
# The request body is an IO input stream. If the RAW_POST_DATA environment
@@ -433,13 +456,13 @@ EOM
# Override Rack's GET method to support indifferent access
def GET
- @env["action_controller.request.query_parameters"] ||= normalize_parameters(super)
+ @env["action_dispatch.request.query_parameters"] ||= normalize_parameters(super)
end
alias_method :query_parameters, :GET
# Override Rack's POST method to support indifferent access
def POST
- @env["action_controller.request.request_parameters"] ||= normalize_parameters(super)
+ @env["action_dispatch.request.request_parameters"] ||= normalize_parameters(super)
end
alias_method :request_parameters, :POST
@@ -447,29 +470,21 @@ EOM
@env['rack.input']
end
- def session
- @env['rack.session'] ||= {}
+ def reset_session
+ self.session_options.delete(:id)
+ self.session = {}
end
def session=(session) #:nodoc:
@env['rack.session'] = session
end
- def reset_session
- @env['rack.session.options'].delete(:id)
- @env['rack.session'] = {}
- end
-
- def session_options
- @env['rack.session.options'] ||= {}
- end
-
def session_options=(options)
@env['rack.session.options'] = options
end
- def server_port
- @env['SERVER_PORT'].to_i
+ def flash
+ session['flash'] || {}
end
private
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index ecf40b8103..b9db7a4508 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -1,4 +1,5 @@
require 'digest/md5'
+require 'active_support/core_ext/module/delegation'
module ActionDispatch # :nodoc:
# Represents an HTTP response generated by a controller action. One can use
@@ -34,17 +35,31 @@ module ActionDispatch # :nodoc:
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
attr_accessor :request
- attr_accessor :session, :assigns, :template, :layout
- attr_accessor :redirected_to, :redirected_to_method_params
+ attr_writer :header
+ alias_method :headers=, :header=
delegate :default_charset, :to => 'ActionController::Base'
def initialize
super
@header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS)
- @session, @assigns = [], []
end
+ # The response code of the request
+ def response_code
+ status.to_s[0,3].to_i rescue 0
+ end
+
+ # Returns a String to ensure compatibility with Net::HTTPResponse
+ def code
+ status.to_s.split(' ')[0]
+ end
+
+ def message
+ status.to_s.split(' ',2)[1] || StatusCodes::STATUS_CODES[response_code]
+ end
+ alias_method :status_message, :message
+
def body
str = ''
each { |part| str << part.to_s }
@@ -53,7 +68,7 @@ module ActionDispatch # :nodoc:
def body=(body)
@body =
- if body.is_a?(String)
+ if body.respond_to?(:to_str)
[body]
else
body
@@ -64,9 +79,14 @@ module ActionDispatch # :nodoc:
@body
end
- def location; headers['Location'] end
- def location=(url) headers['Location'] = url end
+ def location
+ headers['Location']
+ end
+ alias_method :redirect_url, :location
+ def location=(url)
+ headers['Location'] = url
+ end
# Sets the HTTP response's content MIME type. For example, in the controller
# you could write this:
@@ -137,19 +157,20 @@ module ActionDispatch # :nodoc:
end
end
- def redirect(url, status)
- self.status = status
- self.location = url.gsub(/[\r\n]/, '')
- self.body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
- end
-
def sending_file?
headers["Content-Transfer-Encoding"] == "binary"
end
def assign_default_content_type_and_charset!
- self.content_type ||= Mime::HTML
- self.charset ||= default_charset unless sending_file?
+ if type = headers['Content-Type'] || headers['type']
+ unless type =~ /charset=/ || sending_file?
+ headers['Content-Type'] = "#{type}; charset=#{default_charset}"
+ end
+ else
+ type = Mime::HTML.to_s
+ type += "; charset=#{default_charset}" unless sending_file?
+ headers['Content-Type'] = type
+ end
end
def prepare!
@@ -165,10 +186,8 @@ module ActionDispatch # :nodoc:
if @body.respond_to?(:call)
@writer = lambda { |x| callback.call(x) }
@body.call(self, self)
- elsif @body.is_a?(String)
- callback.call(@body)
else
- @body.each(&callback)
+ @body.each { |part| callback.call(part.to_s) }
end
@writer = callback
@@ -192,6 +211,23 @@ module ActionDispatch # :nodoc:
super(key, value)
end
+ # Returns the response cookies, converted to a Hash of (name => value) pairs
+ #
+ # assert_equal 'AuthorOfNewPage', r.cookies['author']
+ def cookies
+ cookies = {}
+ if header = headers['Set-Cookie']
+ header = header.split("\n") if header.respond_to?(:to_str)
+ header.each do |cookie|
+ if pair = cookie.split(';').first
+ key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
+ cookies[key] = value
+ end
+ end
+ end
+ cookies
+ end
+
private
def handle_conditional_get!
if etag? || last_modified?
@@ -245,7 +281,13 @@ module ActionDispatch # :nodoc:
end
def convert_cookies!
- headers['Set-Cookie'] = Array(headers['Set-Cookie']).compact
+ headers['Set-Cookie'] =
+ if header = headers['Set-Cookie']
+ header = header.split("\n") if header.respond_to?(:to_str)
+ header.compact
+ else
+ []
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb
index 830de2a6db..5bac842ec1 100644
--- a/actionpack/lib/action_dispatch/http/status_codes.rb
+++ b/actionpack/lib/action_dispatch/http/status_codes.rb
@@ -1,3 +1,5 @@
+require 'active_support/inflector'
+
module ActionDispatch
module StatusCodes #:nodoc:
STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES.merge({
@@ -16,7 +18,7 @@ module ActionDispatch
# :created or :not_implemented) into its corresponding HTTP status
# code (like 200 or 501).
SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) { |hash, (code, message)|
- hash[message.gsub(/ /, "").underscore.to_sym] = code
+ hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code
hash
}.freeze
@@ -37,4 +39,4 @@ module ActionDispatch
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
new file mode 100644
index 0000000000..0a2b4cf5f7
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -0,0 +1,40 @@
+module ActionDispatch
+ class Callbacks
+ include ActiveSupport::Callbacks
+ define_callbacks :prepare, :before, :after
+
+ class << self
+ # DEPRECATED
+ alias_method :prepare_dispatch, :prepare
+ alias_method :before_dispatch, :before
+ alias_method :after_dispatch, :after
+ end
+
+ # Add a preparation callback. Preparation callbacks are run before every
+ # request in development mode, and before the first request in production
+ # mode.
+ #
+ # An optional identifier may be supplied for the callback. If provided,
+ # to_prepare may be called again with the same identifier to replace the
+ # existing callback. Passing an identifier is a suggested practice if the
+ # code adding a preparation block may be reloaded.
+ def self.to_prepare(identifier = nil, &block)
+ @prepare_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
+ callback = ActiveSupport::Callbacks::Callback.new(:prepare, block, :identifier => identifier)
+ @prepare_callbacks.replace_or_append!(callback)
+ end
+
+ def initialize(app, prepare_each_request = false)
+ @app, @prepare_each_request = app, prepare_each_request
+ run_callbacks :prepare
+ end
+
+ def call(env)
+ run_callbacks :before
+ run_callbacks :prepare if @prepare_each_request
+ @app.call(env)
+ ensure
+ run_callbacks :after, :enumerator => :reverse_each
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/failsafe.rb b/actionpack/lib/action_dispatch/middleware/failsafe.rb
deleted file mode 100644
index 7379a696aa..0000000000
--- a/actionpack/lib/action_dispatch/middleware/failsafe.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module ActionDispatch
- class Failsafe
- cattr_accessor :error_file_path
- self.error_file_path = Rails.public_path if defined?(Rails.public_path)
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
- @app.call(env)
- rescue Exception => exception
- # Reraise exception in test environment
- if env["rack.test"]
- raise exception
- else
- failsafe_response(exception)
- end
- end
-
- private
- def failsafe_response(exception)
- log_failsafe_exception(exception)
- [500, {'Content-Type' => 'text/html'}, failsafe_response_body]
- rescue Exception => failsafe_error # Logger or IO errors
- $stderr.puts "Error during failsafe response: #{failsafe_error}"
- end
-
- def failsafe_response_body
- error_path = "#{self.class.error_file_path}/500.html"
- if File.exist?(error_path)
- File.read(error_path)
- else
- "<html><body><h1>500 Internal Server Error</h1></body></html>"
- end
- end
-
- def log_failsafe_exception(exception)
- message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n"
- message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception
- failsafe_logger.fatal(message)
- end
-
- def failsafe_logger
- if defined?(Rails) && Rails.logger
- Rails.logger
- else
- Logger.new($stderr)
- end
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 6df572268c..e83cf9236b 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -1,3 +1,5 @@
+require 'active_support/json'
+
module ActionDispatch
class ParamsParser
ActionController::Base.param_parsers[Mime::XML] = :xml_simple
@@ -9,7 +11,7 @@ module ActionDispatch
def call(env)
if params = parse_formatted_parameters(env)
- env["action_controller.request.request_parameters"] = params
+ env["action_dispatch.request.request_parameters"] = params
end
@app.call(env)
@@ -30,16 +32,14 @@ module ActionDispatch
when Proc
strategy.call(request.raw_post)
when :xml_simple, :xml_node
- body = request.raw_post
- body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
+ request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access
when :yaml
- YAML.load(request.raw_post)
+ YAML.load(request.body)
when :json
- body = request.raw_post
- if body.blank?
+ if request.body.size == 0
{}
else
- data = ActiveSupport::JSON.decode(body)
+ data = ActiveSupport::JSON.decode(request.body)
data = {:_json => data} unless data.is_a?(Hash)
data.with_indifferent_access
end
diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb
deleted file mode 100644
index 67313e30e4..0000000000
--- a/actionpack/lib/action_dispatch/middleware/reloader.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module ActionDispatch
- class Reloader
- def initialize(app)
- @app = app
- end
-
- def call(env)
- ActionController::Dispatcher.reload_application
- @app.call(env)
- ensure
- ActionController::Dispatcher.cleanup_application
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/rescue.rb b/actionpack/lib/action_dispatch/middleware/rescue.rb
new file mode 100644
index 0000000000..1456825526
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/rescue.rb
@@ -0,0 +1,14 @@
+module ActionDispatch
+ class Rescue
+ def initialize(app, rescuer)
+ @app, @rescuer = app, rescuer
+ end
+
+ def call(env)
+ @app.call(env)
+ rescue Exception => exception
+ env['action_dispatch.rescue.exception'] = exception
+ @rescuer.call(env)
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/rewindable_input.rb b/actionpack/lib/action_dispatch/middleware/rewindable_input.rb
deleted file mode 100644
index c818f28cce..0000000000
--- a/actionpack/lib/action_dispatch/middleware/rewindable_input.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module ActionDispatch
- class RewindableInput
- def initialize(app)
- @app = app
- end
-
- def call(env)
- begin
- env['rack.input'].rewind
- rescue NoMethodError, Errno::ESPIPE
- # Handles exceptions raised by input streams that cannot be rewound
- # such as when using plain CGI under Apache
- env['rack.input'] = StringIO.new(env['rack.input'].read)
- end
-
- @app.call(env)
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 6c039cf62d..6d109f4624 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -15,6 +15,7 @@ module ActionDispatch
@by = by
@env = env
@loaded = false
+ @updated = false
end
def session_id
@@ -26,12 +27,13 @@ module ActionDispatch
def [](key)
load! unless @loaded
- super
+ super(key.to_s)
end
def []=(key, value)
load! unless @loaded
- super
+ super(key.to_s, value)
+ @updated = true
end
def to_hash
@@ -40,6 +42,24 @@ module ActionDispatch
h
end
+ def update(hash = nil)
+ if hash.nil?
+ ActiveSupport::Deprecation.warn('use replace instead', caller)
+ replace({})
+ else
+ super(hash.stringify_keys)
+ end
+ end
+
+ def delete(key = nil)
+ if key.nil?
+ ActiveSupport::Deprecation.warn('use clear instead', caller)
+ clear
+ else
+ super(key.to_s)
+ end
+ end
+
def data
ActiveSupport::Deprecation.warn(
"ActionController::Session::AbstractStore::SessionHash#data " +
@@ -47,6 +67,10 @@ module ActionDispatch
to_hash
end
+ def close
+ ActiveSupport::Deprecation.warn('sessions should no longer be closed', caller)
+ end
+
def inspect
load! unless @loaded
super
@@ -57,11 +81,15 @@ module ActionDispatch
@loaded
end
+ def updated?
+ @updated
+ end
+
def load!
stale_session_check! do
id, session = @by.send(:load_session, @env)
(@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
- replace(session)
+ replace(session.stringify_keys)
@loaded = true
end
end
@@ -74,7 +102,7 @@ module ActionDispatch
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
- raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
+ raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
retry
@@ -125,7 +153,10 @@ module ActionDispatch
options = env[ENV_SESSION_OPTIONS_KEY]
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
- session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
+ if session_data.is_a?(AbstractStore::SessionHash)
+ session_data.send(:load!) if !session_data.send(:loaded?)
+ return response if !session_data.send(:updated?)
+ end
sid = options[:id] || generate_sid
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 433c4cc070..547a2d2062 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -143,7 +143,8 @@ module ActionDispatch
request = Rack::Request.new(env)
session_data = request.cookies[@key]
data = unmarshal(session_data) || persistent_session_id!({})
- [data[:session_id], data]
+ data.stringify_keys!
+ [data["session_id"], data]
end
# Marshal a session hash into safe cookie data. Include an integrity hash.
@@ -206,12 +207,12 @@ module ActionDispatch
end
def inject_persistent_session_id(data)
- requires_session_id?(data) ? { :session_id => generate_sid } : {}
+ requires_session_id?(data) ? { "session_id" => generate_sid } : {}
end
def requires_session_id?(data)
if data
- data.respond_to?(:key?) && !data.key?(:session_id)
+ data.respond_to?(:key?) && !data.key?("session_id")
else
true
end
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
new file mode 100644
index 0000000000..bfff307669
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -0,0 +1,141 @@
+module ActionDispatch
+ class ShowExceptions
+ include StatusCodes
+
+ LOCALHOST = '127.0.0.1'.freeze
+
+ RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
+
+ cattr_accessor :rescue_responses
+ @@rescue_responses = Hash.new(:internal_server_error)
+ @@rescue_responses.update({
+ 'ActionController::RoutingError' => :not_found,
+ # TODO: Clean this up after the switch
+ ActionController::UnknownAction.name => :not_found,
+ 'ActiveRecord::RecordNotFound' => :not_found,
+ 'ActiveRecord::StaleObjectError' => :conflict,
+ 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
+ 'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
+ 'ActionController::NotImplemented' => :not_implemented,
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
+ })
+
+ cattr_accessor :rescue_templates
+ @@rescue_templates = Hash.new('diagnostics')
+ @@rescue_templates.update({
+ 'ActionView::MissingTemplate' => 'missing_template',
+ 'ActionController::RoutingError' => 'routing_error',
+ ActionController::UnknownAction.name => 'unknown_action',
+ 'ActionView::TemplateError' => 'template_error'
+ })
+
+ FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
+ ["<html><body><h1>500 Internal Server Error</h1>" <<
+ "If you are the administrator of this website, then please read this web " <<
+ "application's log file and/or the web server's log file to find out what " <<
+ "went wrong.</body></html>"]]
+
+ def initialize(app, consider_all_requests_local = false)
+ @app = app
+ @consider_all_requests_local = consider_all_requests_local
+ end
+
+ def call(env)
+ @app.call(env)
+ rescue Exception => exception
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
+ end
+
+ private
+ def render_exception(env, exception)
+ log_error(exception)
+
+ request = Request.new(env)
+ if @consider_all_requests_local || local_request?(request)
+ rescue_action_locally(request, exception)
+ else
+ rescue_action_in_public(exception)
+ end
+ rescue Exception => failsafe_error
+ $stderr.puts "Error during failsafe response: #{failsafe_error}"
+ FAILSAFE_RESPONSE
+ end
+
+ # Render detailed diagnostics for unhandled exceptions rescued from
+ # a controller action.
+ def rescue_action_locally(request, exception)
+ template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
+ :request => request,
+ :exception => exception
+ )
+ file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
+ body = template.render(:file => file, :layout => 'rescues/layout.erb')
+ render(status_code(exception), body)
+ end
+
+ # Attempts to render a static error page based on the
+ # <tt>status_code</tt> thrown, or just return headers if no such file
+ # exists. At first, it will try to render a localized static page.
+ # For example, if a 500 error is being handled Rails and locale is :da,
+ # it will first attempt to render the file at <tt>public/500.da.html</tt>
+ # then attempt to render <tt>public/500.html</tt>. If none of them exist,
+ # the body of the response will be left empty.
+ def rescue_action_in_public(exception)
+ status = status_code(exception)
+ locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
+ path = "#{public_path}/#{status}.html"
+
+ if locale_path && File.exist?(locale_path)
+ render(status, File.read(locale_path))
+ elsif File.exist?(path)
+ render(status, File.read(path))
+ else
+ render(status, '')
+ end
+ end
+
+ # True if the request came from localhost, 127.0.0.1.
+ def local_request?(request)
+ request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
+ end
+
+ def status_code(exception)
+ interpret_status(@@rescue_responses[exception.class.name]).to_i
+ end
+
+ def render(status, body)
+ [status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, [body]]
+ end
+
+ def public_path
+ defined?(Rails.public_path) ? Rails.public_path : 'public_path'
+ end
+
+ def log_error(exception)
+ return unless logger
+
+ ActiveSupport::Deprecation.silence do
+ if ActionView::TemplateError === exception
+ logger.fatal(exception.to_s)
+ else
+ logger.fatal(
+ "\n#{exception.class} (#{exception.message}):\n " +
+ clean_backtrace(exception).join("\n ") + "\n\n"
+ )
+ end
+ end
+ end
+
+ def clean_backtrace(exception)
+ defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
+ Rails.backtrace_cleaner.clean(exception.backtrace) :
+ exception.backtrace
+ end
+
+ def logger
+ defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index ee5f28d5cb..ade2d6f05e 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -34,8 +34,6 @@ module ActionDispatch
else
@klass.to_s.constantize
end
- rescue NameError
- @klass
end
def active?
@@ -61,7 +59,7 @@ module ActionDispatch
def inspect
str = klass.to_s
- args.each { |arg| str += ", #{arg.inspect}" }
+ args.each { |arg| str += ", #{build_args.inspect}" }
str
end
@@ -74,7 +72,6 @@ module ActionDispatch
end
private
-
def build_args
Array(args).map { |arg| arg.respond_to?(:call) ? arg.call : arg }
end
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 64b34650b1..5224403dab 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -6,7 +6,7 @@
<% end %>
<%
- clean_params = request.parameters.clone
+ clean_params = @request.parameters.clone
clean_params.delete("action")
clean_params.delete("controller")
@@ -17,8 +17,8 @@
<p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
<p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
-<div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
+<div id="session_dump" style="display:none"><%= debug(@request.session.instance_variable_get("@data")) %></div>
<h2 style="margin-top: 30px">Response</h2>
-<p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>
+<p><b>Headers</b>: <pre><%=h @response ? @response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
index bb2d8375bd..bb2d8375bd 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
new file mode 100644
index 0000000000..693e56270a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
@@ -0,0 +1,10 @@
+<h1>
+ <%=h @exception.class.to_s %>
+ <% if @request.parameters['controller'] %>
+ in <%=h @request.parameters['controller'].humanize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
+ <% end %>
+</h1>
+<pre><%=h @exception.clean_message %></pre>
+
+<%= render :file => "rescues/_trace.erb" %>
+<%= render :file => "rescues/_request_and_response.erb" %>
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index 4a04742e40..6c32fb17b8 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -23,7 +23,7 @@
</head>
<body>
-<%= @contents %>
+<%= yield %>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb
index dbfdf76947..dbfdf76947 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
index ccfa858cce..ccfa858cce 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
index 2e34e03bd5..02fa18211d 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
@@ -1,6 +1,6 @@
<h1>
<%=h @exception.original_exception.class.to_s %> in
- <%=h request.parameters["controller"].capitalize if request.parameters["controller"]%>#<%=h request.parameters["action"] %>
+ <%=h @request.parameters["controller"].capitalize if @request.parameters["controller"]%>#<%=h @request.parameters["action"] %>
</h1>
<p>
@@ -15,7 +15,7 @@
<% @real_exception = @exception
@exception = @exception.original_exception || @exception %>
-<%= render :file => @rescues_path["rescues/_trace.erb"] %>
+<%= render :file => "rescues/_trace.erb" %>
<% @exception = @real_exception %>
-<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %>
+<%= render :file => "rescues/_request_and_response.erb" %>
diff --git a/actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb
index 683379da10..683379da10 100644
--- a/actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb
diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb
new file mode 100644
index 0000000000..96f08f2355
--- /dev/null
+++ b/actionpack/lib/action_dispatch/testing/assertions.rb
@@ -0,0 +1,8 @@
+module ActionDispatch
+ module Assertions
+ %w(response selector tag dom routing model).each do |kind|
+ require "action_dispatch/testing/assertions/#{kind}"
+ include const_get("#{kind.camelize}Assertions")
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/testing/assertions/dom.rb b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
index 5ffe5f1883..9a917f704a 100644
--- a/actionpack/lib/action_controller/testing/assertions/dom.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
@@ -1,4 +1,4 @@
-module ActionController
+module ActionDispatch
module Assertions
module DomAssertions
# Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
@@ -9,13 +9,11 @@ module ActionController
# assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
#
def assert_dom_equal(expected, actual, message = "")
- clean_backtrace do
- expected_dom = HTML::Document.new(expected).root
- actual_dom = HTML::Document.new(actual).root
- full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
+ expected_dom = HTML::Document.new(expected).root
+ actual_dom = HTML::Document.new(actual).root
+ full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
- assert_block(full_message) { expected_dom == actual_dom }
- end
+ assert_block(full_message) { expected_dom == actual_dom }
end
# The negated form of +assert_dom_equivalent+.
@@ -26,13 +24,11 @@ module ActionController
# assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
#
def assert_dom_not_equal(expected, actual, message = "")
- clean_backtrace do
- expected_dom = HTML::Document.new(expected).root
- actual_dom = HTML::Document.new(actual).root
- full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
+ expected_dom = HTML::Document.new(expected).root
+ actual_dom = HTML::Document.new(actual).root
+ full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
- assert_block(full_message) { expected_dom != actual_dom }
- end
+ assert_block(full_message) { expected_dom != actual_dom }
end
end
end
diff --git a/actionpack/lib/action_controller/testing/assertions/model.rb b/actionpack/lib/action_dispatch/testing/assertions/model.rb
index 3a7b39b106..46714418c6 100644
--- a/actionpack/lib/action_controller/testing/assertions/model.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/model.rb
@@ -1,4 +1,4 @@
-module ActionController
+module ActionDispatch
module Assertions
module ModelAssertions
# Ensures that the passed record is valid by Active Record standards and
@@ -12,9 +12,7 @@ module ActionController
#
def assert_valid(record)
::ActiveSupport::Deprecation.warn("assert_valid is deprecated. Use assert record.valid? instead", caller)
- clean_backtrace do
- assert record.valid?, record.errors.full_messages.join("\n")
- end
+ assert record.valid?, record.errors.full_messages.join("\n")
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
new file mode 100644
index 0000000000..501a7c4dfb
--- /dev/null
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -0,0 +1,145 @@
+module ActionDispatch
+ module Assertions
+ # A small suite of assertions that test responses from Rails applications.
+ module ResponseAssertions
+ # Asserts that the response is one of the following types:
+ #
+ # * <tt>:success</tt> - Status code was 200
+ # * <tt>:redirect</tt> - Status code was in the 300-399 range
+ # * <tt>:missing</tt> - Status code was 404
+ # * <tt>:error</tt> - Status code was in the 500-599 range
+ #
+ # You can also pass an explicit status number like assert_response(501)
+ # or its symbolic equivalent assert_response(:not_implemented).
+ # See ActionDispatch::StatusCodes for a full list.
+ #
+ # ==== Examples
+ #
+ # # assert that the response was a redirection
+ # assert_response :redirect
+ #
+ # # assert that the response code was status code 401 (unauthorized)
+ # assert_response 401
+ #
+ def assert_response(type, message = nil)
+ validate_request!
+
+ if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
+ assert_block("") { true } # to count the assertion
+ elsif type.is_a?(Fixnum) && @response.response_code == type
+ assert_block("") { true } # to count the assertion
+ elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
+ assert_block("") { true } # to count the assertion
+ else
+ assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
+ end
+ end
+
+ # Assert that the redirection options passed in match those of the redirect called in the latest action.
+ # This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
+ # match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
+ #
+ # ==== Examples
+ #
+ # # assert that the redirection was to the "index" action on the WeblogController
+ # assert_redirected_to :controller => "weblog", :action => "index"
+ #
+ # # assert that the redirection was to the named route login_url
+ # assert_redirected_to login_url
+ #
+ # # assert that the redirection was to the url for @customer
+ # assert_redirected_to @customer
+ #
+ def assert_redirected_to(options = {}, message=nil)
+ validate_request!
+
+ assert_response(:redirect, message)
+ return true if options == @response.location
+
+ redirected_to_after_normalisation = normalize_argument_to_redirection(@response.location)
+ options_after_normalisation = normalize_argument_to_redirection(options)
+
+ if redirected_to_after_normalisation != options_after_normalisation
+ flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"
+ end
+ end
+
+ # Asserts that the request was rendered with the appropriate template file or partials
+ #
+ # ==== Examples
+ #
+ # # assert that the "new" view template was rendered
+ # assert_template "new"
+ #
+ # # assert that the "_customer" partial was rendered twice
+ # assert_template :partial => '_customer', :count => 2
+ #
+ # # assert that no partials were rendered
+ # assert_template :partial => false
+ #
+ def assert_template(options = {}, message = nil)
+ validate_request!
+
+ case options
+ when NilClass, String
+ rendered = (@controller.template.rendered[:template] || []).map { |t| t.identifier }
+ msg = build_message(message,
+ "expecting <?> but rendering with <?>",
+ options, rendered.join(', '))
+ assert_block(msg) do
+ if options.nil?
+ @controller.template.rendered[:template].blank?
+ else
+ rendered.any? { |t| t.match(options) }
+ end
+ end
+ when Hash
+ if expected_partial = options[:partial]
+ partials = @controller.template.rendered[:partials]
+ if expected_count = options[:count]
+ found = partials.detect { |p, _| p.identifier.match(expected_partial) }
+ actual_count = found.nil? ? 0 : found.second
+ msg = build_message(message,
+ "expecting ? to be rendered ? time(s) but rendered ? time(s)",
+ expected_partial, expected_count, actual_count)
+ assert(actual_count == expected_count.to_i, msg)
+ else
+ msg = build_message(message,
+ "expecting partial <?> but action rendered <?>",
+ options[:partial], partials.keys)
+ assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg)
+ end
+ else
+ assert @controller.template.rendered[:partials].empty?,
+ "Expected no partials to be rendered"
+ end
+ end
+ end
+
+ private
+ # Proxy to to_param if the object will respond to it.
+ def parameterize(value)
+ value.respond_to?(:to_param) ? value.to_param : value
+ 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
+ end
+ @request.protocol + @request.host_with_port + after_routing
+ end
+ end
+
+ def validate_request!
+ unless @request.is_a?(ActionDispatch::Request)
+ raise ArgumentError, "@request must be an ActionDispatch::Request"
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 5101751cea..89d1a49403 100644
--- a/actionpack/lib/action_controller/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -1,4 +1,4 @@
-module ActionController
+module ActionDispatch
module Assertions
# Suite of assertions to test routes generated by Rails and the handling of requests made to them.
module RoutingAssertions
@@ -44,19 +44,17 @@ module ActionController
request_method = nil
end
- clean_backtrace do
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
- request = recognized_request_for(path, request_method)
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+ request = recognized_request_for(path, request_method)
- expected_options = expected_options.clone
- extras.each_key { |key| expected_options.delete key } unless extras.nil?
+ expected_options = expected_options.clone
+ extras.each_key { |key| expected_options.delete key } unless extras.nil?
- expected_options.stringify_keys!
- routing_diff = expected_options.diff(request.path_parameters)
- msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
- request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
- assert_block(msg) { request.path_parameters == expected_options }
- end
+ expected_options.stringify_keys!
+ routing_diff = expected_options.diff(request.path_parameters)
+ msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
+ request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
+ assert_block(msg) { request.path_parameters == expected_options }
end
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
@@ -78,21 +76,19 @@ module ActionController
# # Asserts that the generated route gives us our custom route
# assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" }
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
- clean_backtrace do
- expected_path = "/#{expected_path}" unless expected_path[0] == ?/
- # Load routes.rb if it hasn't been loaded.
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+ expected_path = "/#{expected_path}" unless expected_path[0] == ?/
+ # Load routes.rb if it hasn't been loaded.
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
- generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
- found_extras = options.reject {|k, v| ! extra_keys.include? k}
+ generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
+ found_extras = options.reject {|k, v| ! extra_keys.include? k}
- msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
- assert_block(msg) { found_extras == extras }
+ msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
+ assert_block(msg) { found_extras == extras }
- msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
- expected_path)
- assert_block(msg) { expected_path == generated_path }
- end
+ msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
+ expected_path)
+ assert_block(msg) { expected_path == generated_path }
end
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
diff --git a/actionpack/lib/action_controller/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 0d56ea5ef7..dd75cda6b9 100644
--- a/actionpack/lib/action_controller/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -3,7 +3,7 @@
# Under MIT and/or CC By license.
#++
-module ActionController
+module ActionDispatch
module Assertions
unless const_defined?(:NO_STRIP)
NO_STRIP = %w{pre script style textarea}
diff --git a/actionpack/lib/action_controller/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
index 80249e0e83..ef6867576e 100644
--- a/actionpack/lib/action_controller/testing/assertions/tag.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
@@ -1,4 +1,4 @@
-module ActionController
+module ActionDispatch
module Assertions
# Pair of assertions to testing elements in the HTML output of the response.
module TagAssertions
@@ -94,11 +94,9 @@ module ActionController
# that allow optional closing tags (p, li, td). <em>You must explicitly
# close all of your tags to use these assertions.</em>
def assert_tag(*opts)
- clean_backtrace do
- opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
- tag = find_tag(opts)
- assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
- end
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
+ tag = find_tag(opts)
+ assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
end
# Identical to +assert_tag+, but asserts that a matching tag does _not_
@@ -116,11 +114,9 @@ module ActionController
# assert_no_tag :tag => "p",
# :children => { :count => 1..3, :only => { :tag => "img" } }
def assert_no_tag(*opts)
- clean_backtrace do
- opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
- tag = find_tag(opts)
- assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
- end
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
+ tag = find_tag(opts)
+ assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
new file mode 100644
index 0000000000..20288aa7a5
--- /dev/null
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -0,0 +1,83 @@
+module ActionDispatch
+ class TestRequest < Request
+ DEFAULT_ENV = Rack::MockRequest.env_for('/')
+
+ def self.new(env = {})
+ super
+ end
+
+ def initialize(env = {})
+ super(DEFAULT_ENV.merge(env))
+
+ self.host = 'test.host'
+ self.remote_addr = '0.0.0.0'
+ self.user_agent = 'Rails Testing'
+ end
+
+ def env
+ write_cookies!
+ delete_nil_values!
+ super
+ end
+
+ def request_method=(method)
+ @env['REQUEST_METHOD'] = method.to_s.upcase
+ end
+
+ def host=(host)
+ @env['HTTP_HOST'] = host
+ end
+
+ def port=(number)
+ @env['SERVER_PORT'] = number.to_i
+ end
+
+ def request_uri=(uri)
+ @env['REQUEST_URI'] = uri
+ end
+
+ def path=(path)
+ @env['PATH_INFO'] = path
+ end
+
+ def action=(action_name)
+ path_parameters["action"] = action_name.to_s
+ end
+
+ def if_modified_since=(last_modified)
+ @env['HTTP_IF_MODIFIED_SINCE'] = last_modified
+ end
+
+ def if_none_match=(etag)
+ @env['HTTP_IF_NONE_MATCH'] = etag
+ end
+
+ def remote_addr=(addr)
+ @env['REMOTE_ADDR'] = addr
+ end
+
+ def user_agent=(user_agent)
+ @env['HTTP_USER_AGENT'] = user_agent
+ end
+
+ def accept=(mime_types)
+ @env.delete('action_dispatch.request.accepts')
+ @env['HTTP_ACCEPT'] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",")
+ end
+
+ def cookies
+ @cookies ||= super
+ end
+
+ private
+ def write_cookies!
+ unless @cookies.blank?
+ @env['HTTP_COOKIE'] = @cookies.map { |name, value| "#{name}=#{value};" }.join(' ')
+ end
+ end
+
+ def delete_nil_values!
+ @env.delete_if { |k, v| v.nil? }
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
new file mode 100644
index 0000000000..c35982e075
--- /dev/null
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -0,0 +1,131 @@
+module ActionDispatch
+ # Integration test methods such as ActionController::Integration::Session#get
+ # and ActionController::Integration::Session#post return objects of class
+ # TestResponse, which represent the HTTP response results of the requested
+ # controller actions.
+ #
+ # See Response for more information on controller response objects.
+ class TestResponse < Response
+ def self.from_response(response)
+ new.tap do |resp|
+ resp.status = response.status
+ resp.headers = response.headers
+ resp.body = response.body
+ end
+ end
+
+ module DeprecatedHelpers
+ def template
+ ActiveSupport::Deprecation.warn("response.template has been deprecated. Use controller.template instead", caller)
+ @template
+ end
+ attr_writer :template
+
+ def session
+ ActiveSupport::Deprecation.warn("response.session has been deprecated. Use request.session instead", caller)
+ @request.session
+ end
+
+ def assigns
+ ActiveSupport::Deprecation.warn("response.assigns has been deprecated. Use controller.assigns instead", caller)
+ @template.controller.assigns
+ end
+
+ def layout
+ ActiveSupport::Deprecation.warn("response.layout has been deprecated. Use template.layout instead", caller)
+ @template.layout
+ end
+
+ def redirect_url_match?(pattern)
+ ::ActiveSupport::Deprecation.warn("response.redirect_url_match? is deprecated. Use assert_match(/foo/, response.redirect_url) instead", caller)
+ return false if redirect_url.nil?
+ p = Regexp.new(pattern) if pattern.class == String
+ p = pattern if pattern.class == Regexp
+ return false if p.nil?
+ p.match(redirect_url) != nil
+ end
+
+ # Returns the template of the file which was used to
+ # render this response (or nil)
+ def rendered
+ ActiveSupport::Deprecation.warn("response.rendered has been deprecated. Use tempate.rendered instead", caller)
+ @template.instance_variable_get(:@_rendered)
+ end
+
+ # A shortcut to the flash. Returns an empty hash if no session flash exists.
+ def flash
+ ActiveSupport::Deprecation.warn("response.flash has been deprecated. Use request.flash instead", caller)
+ request.session['flash'] || {}
+ end
+
+ # Do we have a flash?
+ def has_flash?
+ ActiveSupport::Deprecation.warn("response.has_flash? has been deprecated. Use flash.any? instead", caller)
+ !flash.empty?
+ end
+
+ # Do we have a flash that has contents?
+ def has_flash_with_contents?
+ ActiveSupport::Deprecation.warn("response.has_flash_with_contents? has been deprecated. Use flash.any? instead", caller)
+ !flash.empty?
+ end
+
+ # Does the specified flash object exist?
+ def has_flash_object?(name=nil)
+ ActiveSupport::Deprecation.warn("response.has_flash_object? has been deprecated. Use flash[name] instead", caller)
+ !flash[name].nil?
+ end
+
+ # Does the specified object exist in the session?
+ def has_session_object?(name=nil)
+ ActiveSupport::Deprecation.warn("response.has_session_object? has been deprecated. Use session[name] instead", caller)
+ !session[name].nil?
+ end
+
+ # A shortcut to the template.assigns
+ def template_objects
+ ActiveSupport::Deprecation.warn("response.template_objects has been deprecated. Use tempate.assigns instead", caller)
+ @template.assigns || {}
+ end
+
+ # Does the specified template object exist?
+ def has_template_object?(name=nil)
+ ActiveSupport::Deprecation.warn("response.has_template_object? has been deprecated. Use tempate.assigns[name].nil? instead", caller)
+ !template_objects[name].nil?
+ end
+
+ # Returns binary content (downloadable file), converted to a String
+ def binary_content
+ ActiveSupport::Deprecation.warn("response.binary_content has been deprecated. Use response.body instead", caller)
+ body
+ end
+ end
+ include DeprecatedHelpers
+
+ # Was the response successful?
+ def success?
+ (200..299).include?(response_code)
+ end
+
+ # Was the URL not found?
+ def missing?
+ response_code == 404
+ end
+
+ # Were we redirected?
+ def redirect?
+ (300..399).include?(response_code)
+ end
+
+ # Was there a server-side error?
+ def error?
+ (500..599).include?(response_code)
+ end
+ alias_method :server_error?, :error?
+
+ # Was there a client client?
+ def client_error?
+ (400..499).include?(response_code)
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/reloader.rb b/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/reloader.rb
deleted file mode 100644
index b17d8c0926..0000000000
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/reloader.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require 'thread'
-
-module Rack
- # Rack::Reloader checks on every request, but at most every +secs+
- # seconds, if a file loaded changed, and reloads it, logging to
- # rack.errors.
- #
- # It is recommended you use ShowExceptions to catch SyntaxErrors etc.
-
- class Reloader
- def initialize(app, secs=10)
- @app = app
- @secs = secs # reload every @secs seconds max
- @last = Time.now
- end
-
- def call(env)
- if Time.now > @last + @secs
- Thread.exclusive {
- reload!(env['rack.errors'])
- @last = Time.now
- }
- end
-
- @app.call(env)
- end
-
- def reload!(stderr=$stderr)
- need_reload = $LOADED_FEATURES.find_all { |loaded|
- begin
- if loaded =~ /\A[.\/]/ # absolute filename or 1.9
- abs = loaded
- else
- abs = $LOAD_PATH.map { |path| ::File.join(path, loaded) }.
- find { |file| ::File.exist? file }
- end
-
- if abs
- ::File.mtime(abs) > @last - @secs rescue false
- else
- false
- end
- end
- }
-
- need_reload.each { |l|
- $LOADED_FEATURES.delete l
- }
-
- need_reload.each { |to_load|
- begin
- if require to_load
- stderr.puts "#{self.class}: reloaded `#{to_load}'"
- end
- rescue LoadError, SyntaxError => e
- raise e # Possibly ShowExceptions
- end
- }
-
- stderr.flush
- need_reload
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack.rb
index 6349b95094..371d015690 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack.rb
@@ -3,7 +3,8 @@
# Rack is freely distributable under the terms of an MIT-style license.
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
-$:.unshift(File.expand_path(File.dirname(__FILE__)))
+path = File.expand_path(File.dirname(__FILE__))
+$:.unshift(path) unless $:.include?(path)
# The Rack main module, serving as a namespace for all core Rack
@@ -14,7 +15,7 @@ $:.unshift(File.expand_path(File.dirname(__FILE__)))
module Rack
# The Rack protocol version number implemented.
- VERSION = [0,1]
+ VERSION = [1,0]
# Return the Rack protocol version as a dotted string.
def self.version
@@ -23,7 +24,7 @@ module Rack
# Return the Rack release as a dotted string.
def self.release
- "1.0 bundled"
+ "1.0"
end
autoload :Builder, "rack/builder"
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/adapter/camping.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/adapter/camping.rb
index 63bc787f54..63bc787f54 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/adapter/camping.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/adapter/camping.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/abstract/handler.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb
index 214df6299e..214df6299e 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/abstract/handler.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/abstract/request.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/request.rb
index 1d9ccec685..1d9ccec685 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/abstract/request.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/request.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/basic.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/basic.rb
index 9557224648..9557224648 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/basic.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/basic.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/md5.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/md5.rb
index e579dc9632..e579dc9632 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/md5.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/md5.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/nonce.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb
index dbe109f29a..dbe109f29a 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/nonce.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/params.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/params.rb
index 730e2efdc8..730e2efdc8 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/params.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/params.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/request.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/request.rb
index a8aa3bf996..a8aa3bf996 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/digest/request.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/request.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/openid.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/openid.rb
index c5f6a5143e..c5f6a5143e 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/auth/openid.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/openid.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/builder.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/builder.rb
index 295235e56a..295235e56a 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/builder.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/builder.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/cascade.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/cascade.rb
index a038aa1105..a038aa1105 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/cascade.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/cascade.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/chunked.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/chunked.rb
index 280d89dd65..280d89dd65 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/chunked.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/chunked.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/commonlogger.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/commonlogger.rb
index 5e68ac626d..5e68ac626d 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/commonlogger.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/commonlogger.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/conditionalget.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/conditionalget.rb
index 7bec824181..046ebdb00a 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/conditionalget.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/conditionalget.rb
@@ -26,6 +26,8 @@ module Rack
headers = Utils::HeaderHash.new(headers)
if etag_matches?(env, headers) || modified_since?(env, headers)
status = 304
+ headers.delete('Content-Type')
+ headers.delete('Content-Length')
body = []
end
[status, headers, body]
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/content_length.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_length.rb
index 1e56d43853..1e56d43853 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/content_length.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_length.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/content_type.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_type.rb
index 0c1e1ca3e1..0c1e1ca3e1 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/content_type.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_type.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/deflater.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/deflater.rb
index a42b7477ae..14137a944d 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/deflater.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/deflater.rb
@@ -33,17 +33,15 @@ module Rack
case encoding
when "gzip"
+ headers['Content-Encoding'] = "gzip"
+ headers.delete('Content-Length')
mtime = headers.key?("Last-Modified") ?
Time.httpdate(headers["Last-Modified"]) : Time.now
- body = self.class.gzip(body, mtime)
- size = Rack::Utils.bytesize(body)
- headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s)
- [status, headers, [body]]
+ [status, headers, GzipStream.new(body, mtime)]
when "deflate"
- body = self.class.deflate(body)
- size = Rack::Utils.bytesize(body)
- headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s)
- [status, headers, [body]]
+ headers['Content-Encoding'] = "deflate"
+ headers.delete('Content-Length')
+ [status, headers, DeflateStream.new(body)]
when "identity"
[status, headers, body]
when nil
@@ -52,34 +50,47 @@ module Rack
end
end
- def self.gzip(body, mtime)
- io = StringIO.new
- gzip = Zlib::GzipWriter.new(io)
- gzip.mtime = mtime
+ class GzipStream
+ def initialize(body, mtime)
+ @body = body
+ @mtime = mtime
+ end
- # TODO: Add streaming
- body.each { |part| gzip << part }
+ def each(&block)
+ @writer = block
+ gzip =::Zlib::GzipWriter.new(self)
+ gzip.mtime = @mtime
+ @body.each { |part| gzip << part }
+ @body.close if @body.respond_to?(:close)
+ gzip.close
+ @writer = nil
+ end
- gzip.close
- return io.string
+ def write(data)
+ @writer.call(data)
+ end
end
- DEFLATE_ARGS = [
- Zlib::DEFAULT_COMPRESSION,
- # drop the zlib header which causes both Safari and IE to choke
- -Zlib::MAX_WBITS,
- Zlib::DEF_MEM_LEVEL,
- Zlib::DEFAULT_STRATEGY
- ]
+ class DeflateStream
+ DEFLATE_ARGS = [
+ Zlib::DEFAULT_COMPRESSION,
+ # drop the zlib header which causes both Safari and IE to choke
+ -Zlib::MAX_WBITS,
+ Zlib::DEF_MEM_LEVEL,
+ Zlib::DEFAULT_STRATEGY
+ ]
- # Loosely based on Mongrel's Deflate handler
- def self.deflate(body)
- deflater = Zlib::Deflate.new(*DEFLATE_ARGS)
-
- # TODO: Add streaming
- body.each { |part| deflater << part }
+ def initialize(body)
+ @body = body
+ end
- return deflater.finish
+ def each
+ deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
+ @body.each { |part| yield deflater.deflate(part) }
+ @body.close if @body.respond_to?(:close)
+ yield deflater.finish
+ nil
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/directory.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/directory.rb
index acdd3029d3..acdd3029d3 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/directory.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/directory.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/file.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/file.rb
index fe62bd6b86..fe62bd6b86 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/file.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/file.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler.rb
index 1018af64c7..5624a1e79d 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler.rb
@@ -10,16 +10,37 @@ module Rack
module Handler
def self.get(server)
return unless server
+ server = server.to_s
if klass = @handlers[server]
obj = Object
klass.split("::").each { |x| obj = obj.const_get(x) }
obj
else
- Rack::Handler.const_get(server.capitalize)
+ try_require('rack/handler', server)
+ const_get(server)
end
end
+ # Transforms server-name constants to their canonical form as filenames,
+ # then tries to require them but silences the LoadError if not found
+ #
+ # Naming convention:
+ #
+ # Foo # => 'foo'
+ # FooBar # => 'foo_bar.rb'
+ # FooBAR # => 'foobar.rb'
+ # FOObar # => 'foobar.rb'
+ # FOOBAR # => 'foobar.rb'
+ # FooBarBaz # => 'foo_bar_baz.rb'
+ def self.try_require(prefix, const_name)
+ file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }.
+ gsub(/[A-Z]+[^A-Z]/, '_\&').downcase
+
+ require(::File.join(prefix, file))
+ rescue LoadError
+ end
+
def self.register(server, klass)
@handlers ||= {}
@handlers[server] = klass
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/cgi.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/cgi.rb
index e38156c7f0..f45f3d735a 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/cgi.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/cgi.rb
@@ -15,7 +15,7 @@ module Rack
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
- env.update({"rack.version" => [0,1],
+ env.update({"rack.version" => [1,0],
"rack.input" => $stdin,
"rack.errors" => $stderr,
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/evented_mongrel.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb
index 0f5cbf7293..0f5cbf7293 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/evented_mongrel.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/fastcgi.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/fastcgi.rb
index 6324c7d274..11e1fcaa74 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/fastcgi.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/fastcgi.rb
@@ -1,6 +1,17 @@
require 'fcgi'
require 'socket'
require 'rack/content_length'
+require 'rack/rewindable_input'
+
+class FCGI::Stream
+ alias _rack_read_without_buffer read
+
+ def read(n, buffer=nil)
+ buf = _rack_read_without_buffer n
+ buffer.replace(buf.to_s) if buffer
+ buf
+ end
+end
module Rack
module Handler
@@ -13,34 +24,18 @@ module Rack
}
end
- module ProperStream # :nodoc:
- def each # This is missing by default.
- while line = gets
- yield line
- end
- end
-
- def read(*args)
- if args.empty?
- super || "" # Empty string on EOF.
- else
- super
- end
- end
- end
-
def self.serve(request, app)
app = Rack::ContentLength.new(app)
env = request.env
env.delete "HTTP_CONTENT_LENGTH"
- request.in.extend ProperStream
-
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
+
+ rack_input = RewindableInput.new(request.in)
- env.update({"rack.version" => [0,1],
- "rack.input" => request.in,
+ env.update({"rack.version" => [1,0],
+ "rack.input" => rack_input,
"rack.errors" => request.err,
"rack.multithread" => false,
@@ -57,12 +52,16 @@ module Rack
env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
- status, headers, body = app.call(env)
begin
- send_headers request.out, status, headers
- send_body request.out, body
+ status, headers, body = app.call(env)
+ begin
+ send_headers request.out, status, headers
+ send_body request.out, body
+ ensure
+ body.close if body.respond_to? :close
+ end
ensure
- body.close if body.respond_to? :close
+ rack_input.close
request.finish
end
end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/lsws.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/lsws.rb
index c65ba3ec8e..7231336d7b 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/lsws.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/lsws.rb
@@ -15,7 +15,7 @@ module Rack
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
- env.update({"rack.version" => [0,1],
+ env.update({"rack.version" => [1,0],
"rack.input" => StringIO.new($stdin.read.to_s),
"rack.errors" => $stderr,
"rack.multithread" => false,
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/mongrel.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/mongrel.rb
index f0c0d58330..3a5ef32d4b 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/mongrel.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/mongrel.rb
@@ -45,7 +45,7 @@ module Rack
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
- env.update({"rack.version" => [0,1],
+ env.update({"rack.version" => [1,0],
"rack.input" => request.body || StringIO.new(""),
"rack.errors" => $stderr,
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/scgi.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/scgi.rb
index 9495c66374..6c4932df95 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/scgi.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/scgi.rb
@@ -32,7 +32,7 @@ module Rack
env["PATH_INFO"] = env["REQUEST_PATH"]
env["QUERY_STRING"] ||= ""
env["SCRIPT_NAME"] = ""
- env.update({"rack.version" => [0,1],
+ env.update({"rack.version" => [1,0],
"rack.input" => StringIO.new(input_body),
"rack.errors" => $stderr,
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb
index 4bafd0b953..4bafd0b953 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/thin.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/thin.rb
index 3d4fedff75..3d4fedff75 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/thin.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/thin.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/webrick.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/webrick.rb
index 829e7d6bf8..2bdc83a9ff 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/handler/webrick.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/webrick.rb
@@ -22,7 +22,7 @@ module Rack
env = req.meta_vars
env.delete_if { |k, v| v.nil? }
- env.update({"rack.version" => [0,1],
+ env.update({"rack.version" => [1,0],
"rack.input" => StringIO.new(req.body.to_s),
"rack.errors" => $stderr,
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/head.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/head.rb
index deab822a99..deab822a99 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/head.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/head.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lint.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lint.rb
index 44a33ce36e..bf2e9787a1 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lint.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lint.rb
@@ -88,9 +88,9 @@ module Rack
## within the application. This may be an
## empty string, if the request URL targets
## the application root and does not have a
- ## trailing slash. This information should be
- ## decoded by the server if it comes from a
- ## URL.
+ ## trailing slash. This value may be
+ ## percent-encoded when I originating from
+ ## a URL.
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
## follows the <tt>?</tt>, if any. May be
@@ -111,19 +111,48 @@ module Rack
## In addition to this, the Rack environment must include these
## Rack-specific variables:
- ## <tt>rack.version</tt>:: The Array [0,1], representing this version of Rack.
+ ## <tt>rack.version</tt>:: The Array [1,0], representing this version of Rack.
## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
## <tt>rack.input</tt>:: See below, the input stream.
## <tt>rack.errors</tt>:: See below, the error stream.
## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
+ ##
+
+ ## Additional environment specifications have approved to
+ ## standardized middleware APIs. None of these are required to
+ ## be implemented by the server.
+
+ ## <tt>rack.session</tt>:: A hash like interface for storing request session data.
+ ## The store must implement:
+ if session = env['rack.session']
+ ## store(key, value) (aliased as []=);
+ assert("session #{session.inspect} must respond to store and []=") {
+ session.respond_to?(:store) && session.respond_to?(:[]=)
+ }
+
+ ## fetch(key, default = nil) (aliased as []);
+ assert("session #{session.inspect} must respond to fetch and []") {
+ session.respond_to?(:fetch) && session.respond_to?(:[])
+ }
+
+ ## delete(key);
+ assert("session #{session.inspect} must respond to delete") {
+ session.respond_to?(:delete)
+ }
+
+ ## clear;
+ assert("session #{session.inspect} must respond to clear") {
+ session.respond_to?(:clear)
+ }
+ end
## The server or the application can store their own data in the
## environment, too. The keys must contain at least one dot,
## and should be prefixed uniquely. The prefix <tt>rack.</tt>
- ## is reserved for use with the Rack core distribution and must
- ## not be used otherwise.
+ ## is reserved for use with the Rack core distribution and other
+ ## accepted specifications and must not be used otherwise.
##
%w[REQUEST_METHOD SERVER_NAME SERVER_PORT
@@ -202,9 +231,12 @@ module Rack
end
## === The Input Stream
+ ##
+ ## The input stream is an IO-like object which contains the raw HTTP
+ ## POST data. If it is a file then it must be opened in binary mode.
def check_input(input)
- ## The input stream must respond to +gets+, +each+ and +read+.
- [:gets, :each, :read].each { |method|
+ ## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
+ [:gets, :each, :read, :rewind].each { |method|
assert("rack.input #{input} does not respond to ##{method}") {
input.respond_to? method
}
@@ -222,10 +254,6 @@ module Rack
@input.size
end
- def rewind
- @input.rewind
- end
-
## * +gets+ must be called without arguments and return a string,
## or +nil+ on EOF.
def gets(*args)
@@ -237,21 +265,44 @@ module Rack
v
end
- ## * +read+ must be called without or with one integer argument
- ## and return a string, or +nil+ on EOF.
+ ## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
+ ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must
+ ## be a String and may not be nil. If +length+ is given and not nil, then this method
+ ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
+ ## then this method reads all data until EOF.
+ ## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
+ ## if +length+ is not given or is nil.
+ ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
+ ## newly created String object.
def read(*args)
assert("rack.input#read called with too many arguments") {
- args.size <= 1
+ args.size <= 2
}
- if args.size == 1
- assert("rack.input#read called with non-integer argument") {
- args.first.kind_of? Integer
+ if args.size >= 1
+ assert("rack.input#read called with non-integer and non-nil length") {
+ args.first.kind_of?(Integer) || args.first.nil?
+ }
+ assert("rack.input#read called with a negative length") {
+ args.first.nil? || args.first >= 0
}
end
+ if args.size >= 2
+ assert("rack.input#read called with non-String buffer") {
+ args[1].kind_of?(String)
+ }
+ end
+
v = @input.read(*args)
- assert("rack.input#read didn't return a String") {
+
+ assert("rack.input#read didn't return nil or a String") {
v.nil? or v.instance_of? String
}
+ if args[0].nil?
+ assert("rack.input#read(nil) returned nil on EOF") {
+ !v.nil?
+ }
+ end
+
v
end
@@ -265,6 +316,23 @@ module Rack
yield line
}
end
+
+ ## * +rewind+ must be called without arguments. It rewinds the input
+ ## stream back to the beginning. It must not raise Errno::ESPIPE:
+ ## that is, it may not be a pipe or a socket. Therefore, handler
+ ## developers must buffer the input data into some rewindable object
+ ## if the underlying input stream is not rewindable.
+ def rewind(*args)
+ assert("rack.input#rewind called with arguments") { args.size == 0 }
+ assert("rack.input#rewind raised Errno::ESPIPE") {
+ begin
+ @input.rewind
+ true
+ rescue Errno::ESPIPE
+ false
+ end
+ }
+ end
## * +close+ must never be called on the input stream.
def close(*args)
@@ -316,13 +384,14 @@ module Rack
## === The Status
def check_status(status)
- ## The status, if parsed as integer (+to_i+), must be greater than or equal to 100.
+ ## This is an HTTP status. When parsed as integer (+to_i+), it must be
+ ## greater than or equal to 100.
assert("Status must be >=100 seen as integer") { status.to_i >= 100 }
end
## === The Headers
def check_headers(header)
- ## The header must respond to each, and yield values of key and value.
+ ## The header must respond to +each+, and yield values of key and value.
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
header.respond_to? :each
}
@@ -344,7 +413,8 @@ module Rack
## The values of the header must be Strings,
assert("a header value must be a String, but the value of " +
"'#{key}' is a #{value.class}") { value.kind_of? String }
- ## consisting of lines (for multiple header values) seperated by "\n".
+ ## consisting of lines (for multiple header values, e.g. multiple
+ ## <tt>Set-Cookie</tt> values) seperated by "\n".
value.split("\n").each { |item|
## The lines must not contain characters below 037.
assert("invalid header value #{key}: #{item.inspect}") {
@@ -416,7 +486,7 @@ module Rack
## === The Body
def each
@closed = false
- ## The Body must respond to #each
+ ## The Body must respond to +each+
@body.each { |part|
## and must only yield String values.
assert("Body yielded non-string value #{part.inspect}") {
@@ -425,14 +495,19 @@ module Rack
yield part
}
##
- ## If the Body responds to #close, it will be called after iteration.
+ ## The Body itself should not be an instance of String, as this will
+ ## break in Ruby 1.9.
+ ##
+ ## If the Body responds to +close+, it will be called after iteration.
# XXX howto: assert("Body has not been closed") { @closed }
##
- ## If the Body responds to #to_path, it must return a String
+ ## If the Body responds to +to_path+, it must return a String
## identifying the location of a file whose contents are identical
- ## to that produced by calling #each.
+ ## to that produced by calling +each+; this may be used by the
+ ## server as an alternative, possibly more efficient way to
+ ## transport the response.
if @body.respond_to?(:to_path)
assert("The file identified by body.to_path does not exist") {
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lobster.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lobster.rb
index f63f419a49..f63f419a49 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lobster.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lobster.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lock.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lock.rb
index 93238528c4..93238528c4 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/lock.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lock.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/methodoverride.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/methodoverride.rb
index 0eed29f471..0eed29f471 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/methodoverride.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/methodoverride.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/mime.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mime.rb
index 5a6a73a97b..5a6a73a97b 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/mime.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mime.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/mock.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mock.rb
index 70852da3db..fdefb0340a 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/mock.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mock.rb
@@ -40,7 +40,7 @@ module Rack
end
DEFAULT_ENV = {
- "rack.version" => [0,1],
+ "rack.version" => [1,0],
"rack.input" => StringIO.new,
"rack.errors" => StringIO.new,
"rack.multithread" => true,
@@ -73,14 +73,17 @@ module Rack
# Return the Rack environment used for a request to +uri+.
def self.env_for(uri="", opts={})
uri = URI(uri)
+ uri.path = "/#{uri.path}" unless uri.path[0] == ?/
+
env = DEFAULT_ENV.dup
- env["REQUEST_METHOD"] = opts[:method] || "GET"
+ env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
env["SERVER_NAME"] = uri.host || "example.org"
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
env["QUERY_STRING"] = uri.query.to_s
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
env["rack.url_scheme"] = uri.scheme || "http"
+ env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
env["SCRIPT_NAME"] = opts[:script_name] || ""
@@ -90,6 +93,27 @@ module Rack
env["rack.errors"] = StringIO.new
end
+ if params = opts[:params]
+ if env["REQUEST_METHOD"] == "GET"
+ params = Utils.parse_nested_query(params) if params.is_a?(String)
+ params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
+ env["QUERY_STRING"] = Utils.build_nested_query(params)
+ elsif !opts.has_key?(:input)
+ opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
+ if params.is_a?(Hash)
+ if data = Utils::Multipart.build_multipart(params)
+ opts[:input] = data
+ opts["CONTENT_LENGTH"] ||= data.length.to_s
+ opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
+ else
+ opts[:input] = Utils.build_nested_query(params)
+ end
+ else
+ opts[:input] = params
+ end
+ end
+ end
+
opts[:input] ||= ""
if String === opts[:input]
env["rack.input"] = StringIO.new(opts[:input])
@@ -125,7 +149,7 @@ module Rack
@body = ""
body.each { |part| @body << part }
- @errors = errors.string
+ @errors = errors.string if errors.respond_to?(:string)
end
# Status
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/recursive.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/recursive.rb
index bf8b965925..bf8b965925 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/recursive.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/recursive.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb
new file mode 100644
index 0000000000..aa2f060be5
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb
@@ -0,0 +1,106 @@
+# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
+# All files in this distribution are subject to the terms of the Ruby license.
+
+require 'pathname'
+
+module Rack
+
+ # High performant source reloader
+ #
+ # This class acts as Rack middleware.
+ #
+ # What makes it especially suited for use in a production environment is that
+ # any file will only be checked once and there will only be made one system
+ # call stat(2).
+ #
+ # Please note that this will not reload files in the background, it does so
+ # only when actively called.
+ #
+ # It is performing a check/reload cycle at the start of every request, but
+ # also respects a cool down time, during which nothing will be done.
+ class Reloader
+ def initialize(app, cooldown = 10, backend = Stat)
+ @app = app
+ @cooldown = cooldown
+ @last = (Time.now - cooldown)
+ @cache = {}
+ @mtimes = {}
+
+ extend backend
+ end
+
+ def call(env)
+ if @cooldown and Time.now > @last + @cooldown
+ if Thread.list.size > 1
+ Thread.exclusive{ reload! }
+ else
+ reload!
+ end
+
+ @last = Time.now
+ end
+
+ @app.call(env)
+ end
+
+ def reload!(stderr = $stderr)
+ rotation do |file, mtime|
+ previous_mtime = @mtimes[file] ||= mtime
+ safe_load(file, mtime, stderr) if mtime > previous_mtime
+ end
+ end
+
+ # A safe Kernel::load, issuing the hooks depending on the results
+ def safe_load(file, mtime, stderr = $stderr)
+ load(file)
+ stderr.puts "#{self.class}: reloaded `#{file}'"
+ file
+ rescue LoadError, SyntaxError => ex
+ stderr.puts ex
+ ensure
+ @mtimes[file] = mtime
+ end
+
+ module Stat
+ def rotation
+ files = [$0, *$LOADED_FEATURES].uniq
+ paths = ['./', *$LOAD_PATH].uniq
+
+ files.map{|file|
+ next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
+
+ found, stat = figure_path(file, paths)
+ next unless found and stat and mtime = stat.mtime
+
+ @cache[file] = found
+
+ yield(found, mtime)
+ }.compact
+ end
+
+ # Takes a relative or absolute +file+ name, a couple possible +paths+ that
+ # the +file+ might reside in. Returns the full path and File::Stat for the
+ # path.
+ def figure_path(file, paths)
+ found = @cache[file]
+ found = file if !found and Pathname.new(file).absolute?
+ found, stat = safe_stat(found)
+ return found, stat if found
+
+ paths.each do |possible_path|
+ path = ::File.join(possible_path, file)
+ found, stat = safe_stat(path)
+ return ::File.expand_path(found), stat if found
+ end
+ end
+
+ def safe_stat(file)
+ return unless file
+ stat = ::File.stat(file)
+ return file, stat if stat.file?
+ rescue Errno::ENOENT, Errno::ENOTDIR
+ @cache.delete(file) and false
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/request.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/request.rb
index d77fa26575..0bff7af038 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/request.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/request.rb
@@ -17,7 +17,7 @@ module Rack
# The environment of the request.
attr_reader :env
- def self.new(env)
+ def self.new(env, *args)
if self == Rack::Request
env["rack.request"] ||= super
else
@@ -38,6 +38,8 @@ module Rack
def query_string; @env["QUERY_STRING"].to_s end
def content_length; @env['CONTENT_LENGTH'] end
def content_type; @env['CONTENT_TYPE'] end
+ def session; @env['rack.session'] ||= {} end
+ def session_options; @env['rack.session.options'] ||= {} end
# The media type (type/subtype) portion of the CONTENT_TYPE header
# without any media type parameters. e.g., when CONTENT_TYPE is
@@ -46,7 +48,7 @@ module Rack
# For more information on the use of media types in HTTP, see:
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
def media_type
- content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase
+ content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
end
# The media type parameters provided in CONTENT_TYPE as a Hash, or
@@ -92,6 +94,14 @@ module Rack
'multipart/form-data'
]
+ # The set of media-types. Requests that do not indicate
+ # one of the media types presents in this list will not be eligible
+ # for param parsing like soap attachments or generic multiparts
+ PARSEABLE_DATA_MEDIA_TYPES = [
+ 'multipart/related',
+ 'multipart/mixed'
+ ]
+
# Determine whether the request body contains form-data by checking
# the request media_type against registered form-data media-types:
# "application/x-www-form-urlencoded" and "multipart/form-data". The
@@ -101,6 +111,12 @@ module Rack
FORM_DATA_MEDIA_TYPES.include?(media_type)
end
+ # Determine whether the request body contains data by checking
+ # the request media_type against registered parse-data media-types
+ def parseable_data?
+ PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
+ end
+
# Returns the data recieved in the query string.
def GET
if @env["rack.request.query_string"] == query_string
@@ -119,7 +135,7 @@ module Rack
def POST
if @env["rack.request.form_input"].eql? @env["rack.input"]
@env["rack.request.form_hash"]
- elsif form_data?
+ elsif form_data? || parseable_data?
@env["rack.request.form_input"] = @env["rack.input"]
unless @env["rack.request.form_hash"] =
Utils::Multipart.parse_multipart(env)
@@ -131,12 +147,7 @@ module Rack
@env["rack.request.form_vars"] = form_vars
@env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars)
- begin
- @env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind)
- rescue Errno::ESPIPE
- # Handles exceptions raised by input streams that cannot be rewound
- # such as when using plain CGI under Apache
- end
+ @env["rack.input"].rewind
end
@env["rack.request.form_hash"]
else
@@ -211,11 +222,13 @@ module Rack
url
end
-
+
+ def path
+ script_name + path_info
+ end
+
def fullpath
- path = script_name + path_info
- path << "?" << query_string unless query_string.empty?
- path
+ query_string.empty? ? path : "#{path}?#{query_string}"
end
def accept_encoding
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/response.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/response.rb
index caf60d5b19..28b4d8302f 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/response.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/response.rb
@@ -95,6 +95,10 @@ module Rack
:expires => Time.at(0) }.merge(value))
end
+ def redirect(target, status=302)
+ self.status = status
+ self["Location"] = target
+ end
def finish(&block)
@block = block
@@ -120,7 +124,7 @@ module Rack
#
def write(str)
s = str.to_s
- @length += s.size
+ @length += Rack::Utils.bytesize(s)
@writer.call s
header["Content-Length"] = @length.to_s
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb
new file mode 100644
index 0000000000..9e9b21ff99
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb
@@ -0,0 +1,98 @@
+require 'tempfile'
+
+module Rack
+ # Class which can make any IO object rewindable, including non-rewindable ones. It does
+ # this by buffering the data into a tempfile, which is rewindable.
+ #
+ # rack.input is required to be rewindable, so if your input stream IO is non-rewindable
+ # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class
+ # to easily make it rewindable.
+ #
+ # Don't forget to call #close when you're done. This frees up temporary resources that
+ # RewindableInput uses, though it does *not* close the original IO object.
+ class RewindableInput
+ def initialize(io)
+ @io = io
+ @rewindable_io = nil
+ @unlinked = false
+ end
+
+ def gets
+ make_rewindable unless @rewindable_io
+ @rewindable_io.gets
+ end
+
+ def read(*args)
+ make_rewindable unless @rewindable_io
+ @rewindable_io.read(*args)
+ end
+
+ def each(&block)
+ make_rewindable unless @rewindable_io
+ @rewindable_io.each(&block)
+ end
+
+ def rewind
+ make_rewindable unless @rewindable_io
+ @rewindable_io.rewind
+ end
+
+ # Closes this RewindableInput object without closing the originally
+ # wrapped IO oject. Cleans up any temporary resources that this RewindableInput
+ # has created.
+ #
+ # This method may be called multiple times. It does nothing on subsequent calls.
+ def close
+ if @rewindable_io
+ if @unlinked
+ @rewindable_io.close
+ else
+ @rewindable_io.close!
+ end
+ @rewindable_io = nil
+ end
+ end
+
+ private
+
+ # Ruby's Tempfile class has a bug. Subclass it and fix it.
+ class Tempfile < ::Tempfile
+ def _close
+ @tmpfile.close if @tmpfile
+ @data[1] = nil if @data
+ @tmpfile = nil
+ end
+ end
+
+ def make_rewindable
+ # Buffer all data into a tempfile. Since this tempfile is private to this
+ # RewindableInput object, we chmod it so that nobody else can read or write
+ # it. On POSIX filesystems we also unlink the file so that it doesn't
+ # even have a file entry on the filesystem anymore, though we can still
+ # access it because we have the file handle open.
+ @rewindable_io = Tempfile.new('RackRewindableInput')
+ @rewindable_io.chmod(0000)
+ if filesystem_has_posix_semantics?
+ @rewindable_io.unlink
+ @unlinked = true
+ end
+
+ buffer = ""
+ while @io.read(1024 * 4, buffer)
+ entire_buffer_written_out = false
+ while !entire_buffer_written_out
+ written = @rewindable_io.write(buffer)
+ entire_buffer_written_out = written == buffer.size
+ if !entire_buffer_written_out
+ buffer.slice!(0 .. written - 1)
+ end
+ end
+ end
+ @rewindable_io.rewind
+ end
+
+ def filesystem_has_posix_semantics?
+ RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/abstract/id.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb
index 218144c17f..218144c17f 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/abstract/id.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/cookie.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/cookie.rb
index eace9bd0c6..eace9bd0c6 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/cookie.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/cookie.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/memcache.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/memcache.rb
index 4a65cbf35d..4a65cbf35d 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/memcache.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/memcache.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/pool.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/pool.rb
index f6f87408bb..f6f87408bb 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/session/pool.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/pool.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/showexceptions.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showexceptions.rb
index 697bc41fdb..697bc41fdb 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/showexceptions.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showexceptions.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/showstatus.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showstatus.rb
index 28258c7c89..28258c7c89 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/showstatus.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showstatus.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/static.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/static.rb
index 168e8f83b2..168e8f83b2 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/static.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/static.rb
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/urlmap.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/urlmap.rb
index 0ff32df181..fcf6616c58 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/urlmap.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/urlmap.rb
@@ -30,7 +30,7 @@ module Rack
location = location.chomp('/')
[host, location, app]
- }.sort_by { |(h, l, a)| [-l.size, h.to_s.size] } # Longest path first
+ }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first
end
def call(env)
diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/utils.rb
index 0a61bce707..42e2e698f4 100644
--- a/actionpack/lib/action_dispatch/vendor/rack-1.0/rack/utils.rb
+++ b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/utils.rb
@@ -1,3 +1,5 @@
+# -*- encoding: binary -*-
+
require 'set'
require 'tempfile'
@@ -63,7 +65,7 @@ module Rack
module_function :parse_nested_query
def normalize_params(params, name, v = nil)
- name =~ %r([\[\]]*([^\[\]]+)\]*)
+ name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
k = $1 || ''
after = $' || ''
@@ -73,12 +75,12 @@ module Rack
params[k] = v
elsif after == "[]"
params[k] ||= []
- raise TypeError unless params[k].is_a?(Array)
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
params[k] << v
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
child_key = $1
params[k] ||= []
- raise TypeError unless params[k].is_a?(Array)
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
normalize_params(params[k].last, child_key, v)
else
@@ -86,6 +88,7 @@ module Rack
end
else
params[k] ||= {}
+ raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
params[k] = normalize_params(params[k], after, v)
end
@@ -104,6 +107,25 @@ module Rack
end
module_function :build_query
+ def build_nested_query(value, prefix = nil)
+ case value
+ when Array
+ value.map { |v|
+ build_nested_query(v, "#{prefix}[]")
+ }.join("&")
+ when Hash
+ value.map { |k, v|
+ build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
+ }.join("&")
+ when String
+ raise ArgumentError, "value must be a Hash" if prefix.nil?
+ "#{prefix}=#{escape(value)}"
+ else
+ prefix
+ end
+ end
+ module_function :build_nested_query
+
# Escape ampersands, brackets and quotes to their HTML/XML entities.
def escape_html(string)
string.to_s.gsub("&", "&amp;").
@@ -288,11 +310,39 @@ module Rack
# Usually, Rack::Request#POST takes care of calling this.
module Multipart
+ class UploadedFile
+ # The filename, *not* including the path, of the "uploaded" file
+ attr_reader :original_filename
+
+ # The content type of the "uploaded" file
+ attr_accessor :content_type
+
+ def initialize(path, content_type = "text/plain", binary = false)
+ raise "#{path} file does not exist" unless ::File.exist?(path)
+ @content_type = content_type
+ @original_filename = ::File.basename(path)
+ @tempfile = Tempfile.new(@original_filename)
+ @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
+ @tempfile.binmode if binary
+ FileUtils.copy_file(path, @tempfile.path)
+ end
+
+ def path
+ @tempfile.path
+ end
+ alias_method :local_path, :path
+
+ def method_missing(method_name, *args, &block) #:nodoc:
+ @tempfile.__send__(method_name, *args, &block)
+ end
+ end
+
EOL = "\r\n"
+ MULTIPART_BOUNDARY = "AaB03x"
def self.parse_multipart(env)
unless env['CONTENT_TYPE'] =~
- %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
+ %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
nil
else
boundary = "--#{$1}"
@@ -301,13 +351,16 @@ module Rack
buf = ""
content_length = env['CONTENT_LENGTH'].to_i
input = env['rack.input']
+ input.rewind
- boundary_size = boundary.size + EOL.size
+ boundary_size = Utils.bytesize(boundary) + EOL.size
bufsize = 16384
content_length -= boundary_size
- status = input.read(boundary_size)
+ read_buffer = ''
+
+ status = input.read(boundary_size, read_buffer)
raise EOFError, "bad content body" unless status == boundary + EOL
rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
@@ -318,15 +371,15 @@ module Rack
filename = content_type = name = nil
until head && buf =~ rx
- if !head && i = buf.index("\r\n\r\n")
+ if !head && i = buf.index(EOL+EOL)
head = buf.slice!(0, i+2) # First \r\n
buf.slice!(0, 2) # Second \r\n
filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
- content_type = head[/Content-Type: (.*)\r\n/ni, 1]
- name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
+ content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
+ name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
- if filename
+ if content_type || filename
body = Tempfile.new("RackMultipart")
body.binmode if body.respond_to?(:binmode)
end
@@ -339,7 +392,7 @@ module Rack
body << buf.slice!(0, buf.size - (boundary_size+4))
end
- c = input.read(bufsize < content_length ? bufsize : content_length)
+ c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
raise EOFError, "bad content body" if c.nil? || c.empty?
buf << c
content_length -= c.size
@@ -368,6 +421,12 @@ module Rack
data = {:filename => filename, :type => content_type,
:name => name, :tempfile => body, :head => head}
+ elsif !filename && content_type
+ body.rewind
+
+ # Generic multipart cases, not coming from a form
+ data = {:type => content_type,
+ :name => name, :tempfile => body, :head => head}
else
data = body
end
@@ -377,16 +436,81 @@ module Rack
break if buf.empty? || content_length == -1
}
- begin
- input.rewind if input.respond_to?(:rewind)
- rescue Errno::ESPIPE
- # Handles exceptions raised by input streams that cannot be rewound
- # such as when using plain CGI under Apache
- end
+ input.rewind
params
end
end
+
+ def self.build_multipart(params, first = true)
+ if first
+ unless params.is_a?(Hash)
+ raise ArgumentError, "value must be a Hash"
+ end
+
+ multipart = false
+ query = lambda { |value|
+ case value
+ when Array
+ value.each(&query)
+ when Hash
+ value.values.each(&query)
+ when UploadedFile
+ multipart = true
+ end
+ }
+ params.values.each(&query)
+ return nil unless multipart
+ end
+
+ flattened_params = Hash.new
+
+ params.each do |key, value|
+ k = first ? key.to_s : "[#{key}]"
+
+ case value
+ when Array
+ value.map { |v|
+ build_multipart(v, false).each { |subkey, subvalue|
+ flattened_params["#{k}[]#{subkey}"] = subvalue
+ }
+ }
+ when Hash
+ build_multipart(value, false).each { |subkey, subvalue|
+ flattened_params[k + subkey] = subvalue
+ }
+ else
+ flattened_params[k] = value
+ end
+ end
+
+ if first
+ flattened_params.map { |name, file|
+ if file.respond_to?(:original_filename)
+ ::File.open(file.path, "rb") do |f|
+ f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
+<<-EOF
+--#{MULTIPART_BOUNDARY}\r
+Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r
+Content-Type: #{file.content_type}\r
+Content-Length: #{::File.stat(file.path).size}\r
+\r
+#{f.read}\r
+EOF
+ end
+ else
+<<-EOF
+--#{MULTIPART_BOUNDARY}\r
+Content-Disposition: form-data; name="#{name}"\r
+\r
+#{file}\r
+EOF
+ end
+ }.join + "--#{MULTIPART_BOUNDARY}--\r"
+ else
+ flattened_params
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb
new file mode 100644
index 0000000000..eba6226538
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb
@@ -0,0 +1,50 @@
+module Rack
+
+ class MockSession
+ attr_writer :cookie_jar
+ attr_reader :last_response
+
+ def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
+ @app = app
+ @default_host = default_host
+ end
+
+ def clear_cookies
+ @cookie_jar = Rack::Test::CookieJar.new([], @default_host)
+ end
+
+ def set_cookie(cookie, uri = nil)
+ cookie_jar.merge(cookie, uri)
+ end
+
+ def request(uri, env)
+ env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
+ @last_request = Rack::Request.new(env)
+ status, headers, body = @app.call(@last_request.env)
+ @last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
+ cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
+
+ @last_response
+ end
+
+ # Return the last request issued in the session. Raises an error if no
+ # requests have been sent yet.
+ def last_request
+ raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
+ @last_request
+ end
+
+ # Return the last response received in the session. Raises an error if
+ # no requests have been sent yet.
+ def last_response
+ raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
+ @last_response
+ end
+
+ def cookie_jar
+ @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
+ end
+
+ end
+
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb
new file mode 100644
index 0000000000..70384b1d76
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb
@@ -0,0 +1,239 @@
+unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__) + "/.."))
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/.."))
+end
+
+require "uri"
+require "rack"
+require "rack/mock_session"
+require "rack/test/cookie_jar"
+require "rack/test/mock_digest_request"
+require "rack/test/utils"
+require "rack/test/methods"
+require "rack/test/uploaded_file"
+
+module Rack
+ module Test
+
+ VERSION = "0.3.0"
+
+ DEFAULT_HOST = "example.org"
+ MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
+
+ # The common base class for exceptions raised by Rack::Test
+ class Error < StandardError; end
+
+ class Session
+ extend Forwardable
+ include Rack::Test::Utils
+
+ def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
+
+ # Initialize a new session for the given Rack app
+ def initialize(app, default_host = DEFAULT_HOST)
+ @headers = {}
+ @default_host = default_host
+ @rack_mock_session = Rack::MockSession.new(app, default_host)
+ end
+
+ # Issue a GET request for the given URI with the given params and Rack
+ # environment. Stores the issues request object in #last_request and
+ # the app's response in #last_response. Yield #last_response to a block
+ # if given.
+ #
+ # Example:
+ # get "/"
+ def get(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "GET", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a POST request for the given URI. See #get
+ #
+ # Example:
+ # post "/signup", "name" => "Bryan"
+ def post(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "POST", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a PUT request for the given URI. See #get
+ #
+ # Example:
+ # put "/"
+ def put(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "PUT", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a DELETE request for the given URI. See #get
+ #
+ # Example:
+ # delete "/"
+ def delete(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "DELETE", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a HEAD request for the given URI. See #get
+ #
+ # Example:
+ # head "/"
+ def head(uri, params = {}, env = {}, &block)
+ env = env_for(uri, env.merge(:method => "HEAD", :params => params))
+ process_request(uri, env, &block)
+ end
+
+ # Issue a request to the Rack app for the given URI and optional Rack
+ # environment. Stores the issues request object in #last_request and
+ # the app's response in #last_response. Yield #last_response to a block
+ # if given.
+ #
+ # Example:
+ # request "/"
+ def request(uri, env = {}, &block)
+ env = env_for(uri, env)
+ process_request(uri, env, &block)
+ end
+
+ # Set a header to be included on all subsequent requests through the
+ # session. Use a value of nil to remove a previously configured header.
+ #
+ # Example:
+ # header "User-Agent", "Firefox"
+ def header(name, value)
+ if value.nil?
+ @headers.delete(name)
+ else
+ @headers[name] = value
+ end
+ end
+
+ # Set the username and password for HTTP Basic authorization, to be
+ # included in subsequent requests in the HTTP_AUTHORIZATION header.
+ #
+ # Example:
+ # basic_authorize "bryan", "secret"
+ def basic_authorize(username, password)
+ encoded_login = ["#{username}:#{password}"].pack("m*")
+ header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
+ end
+
+ alias_method :authorize, :basic_authorize
+
+ def digest_authorize(username, password)
+ @digest_username = username
+ @digest_password = password
+ end
+
+ # Rack::Test will not follow any redirects automatically. This method
+ # will follow the redirect returned in the last response. If the last
+ # response was not a redirect, an error will be raised.
+ def follow_redirect!
+ unless last_response.redirect?
+ raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
+ end
+
+ get(last_response["Location"])
+ end
+
+ private
+
+ def env_for(path, env)
+ uri = URI.parse(path)
+ uri.host ||= @default_host
+
+ env = default_env.merge(env)
+
+ env.update("HTTPS" => "on") if URI::HTTPS === uri
+ env["X-Requested-With"] = "XMLHttpRequest" if env[:xhr]
+
+ if (env[:method] == "POST" || env["REQUEST_METHOD"] == "POST") && !env.has_key?(:input)
+ env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
+
+ multipart = (Hash === env[:params]) &&
+ env[:params].any? { |_, v| UploadedFile === v }
+
+ if multipart
+ env[:input] = multipart_body(env.delete(:params))
+ env["CONTENT_LENGTH"] ||= env[:input].length.to_s
+ env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
+ else
+ env[:input] = params_to_string(env.delete(:params))
+ end
+ end
+
+ params = env[:params] || {}
+ params.update(parse_query(uri.query))
+
+ uri.query = requestify(params)
+
+ if env.has_key?(:cookie)
+ set_cookie(env.delete(:cookie), uri)
+ end
+
+ Rack::MockRequest.env_for(uri.to_s, env)
+ end
+
+ def process_request(uri, env)
+ uri = URI.parse(uri)
+ uri.host ||= @default_host
+
+ @rack_mock_session.request(uri, env)
+
+ if retry_with_digest_auth?(env)
+ auth_env = env.merge({
+ "HTTP_AUTHORIZATION" => digest_auth_header,
+ "rack-test.digest_auth_retry" => true
+ })
+ auth_env.delete('rack.request')
+ process_request(uri.path, auth_env)
+ else
+ yield last_response if block_given?
+
+ last_response
+ end
+ end
+
+ def digest_auth_header
+ challenge = last_response["WWW-Authenticate"].split(" ", 2).last
+ params = Rack::Auth::Digest::Params.parse(challenge)
+
+ params.merge!({
+ "username" => @digest_username,
+ "nc" => "00000001",
+ "cnonce" => "nonsensenonce",
+ "uri" => last_request.path_info,
+ "method" => last_request.env["REQUEST_METHOD"],
+ })
+
+ params["response"] = MockDigestRequest.new(params).response(@digest_password)
+
+ "Digest #{params}"
+ end
+
+ def retry_with_digest_auth?(env)
+ last_response.status == 401 &&
+ digest_auth_configured? &&
+ !env["rack-test.digest_auth_retry"]
+ end
+
+ def digest_auth_configured?
+ @digest_username
+ end
+
+ def default_env
+ { "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(@headers)
+ end
+
+ def params_to_string(params)
+ case params
+ when Hash then requestify(params)
+ when nil then ""
+ else params
+ end
+ end
+
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb
new file mode 100644
index 0000000000..d58c914c9b
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb
@@ -0,0 +1,169 @@
+require "uri"
+module Rack
+ module Test
+
+ class Cookie
+ include Rack::Utils
+
+ # :api: private
+ attr_reader :name, :value
+
+ # :api: private
+ def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
+ @default_host = default_host
+ uri ||= default_uri
+
+ # separate the name / value pair from the cookie options
+ @name_value_raw, options = raw.split(/[;,] */n, 2)
+
+ @name, @value = parse_query(@name_value_raw, ';').to_a.first
+ @options = parse_query(options, ';')
+
+ @options["domain"] ||= (uri.host || default_host)
+ @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
+ end
+
+ def replaces?(other)
+ [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
+ end
+
+ # :api: private
+ def raw
+ @name_value_raw
+ end
+
+ # :api: private
+ def empty?
+ @value.nil? || @value.empty?
+ end
+
+ # :api: private
+ def domain
+ @options["domain"]
+ end
+
+ def secure?
+ @options.has_key?("secure")
+ end
+
+ # :api: private
+ def path
+ @options["path"].strip || "/"
+ end
+
+ # :api: private
+ def expires
+ Time.parse(@options["expires"]) if @options["expires"]
+ end
+
+ # :api: private
+ def expired?
+ expires && expires < Time.now
+ end
+
+ # :api: private
+ def valid?(uri)
+ uri ||= default_uri
+
+ if uri.host.nil?
+ uri.host = @default_host
+ end
+
+ (!secure? || (secure? && uri.scheme == "https")) &&
+ uri.host =~ Regexp.new("#{Regexp.escape(domain)}$", Regexp::IGNORECASE) &&
+ uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
+ end
+
+ # :api: private
+ def matches?(uri)
+ ! expired? && valid?(uri)
+ end
+
+ # :api: private
+ def <=>(other)
+ # Orders the cookies from least specific to most
+ [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
+ end
+
+ protected
+
+ def default_uri
+ URI.parse("//" + @default_host + "/")
+ end
+
+ end
+
+ class CookieJar
+
+ # :api: private
+ def initialize(cookies = [], default_host = DEFAULT_HOST)
+ @default_host = default_host
+ @cookies = cookies
+ @cookies.sort!
+ end
+
+ def [](name)
+ cookies = hash_for(nil)
+ # TODO: Should be case insensitive
+ cookies[name] && cookies[name].value
+ end
+
+ def []=(name, value)
+ # TODO: needs proper escaping
+ merge("#{name}=#{value}")
+ end
+
+ def merge(raw_cookies, uri = nil)
+ return unless raw_cookies
+
+ raw_cookies.each_line do |raw_cookie|
+ cookie = Cookie.new(raw_cookie, uri, @default_host)
+ self << cookie if cookie.valid?(uri)
+ end
+ end
+
+ def <<(new_cookie)
+ @cookies.reject! do |existing_cookie|
+ new_cookie.replaces?(existing_cookie)
+ end
+
+ @cookies << new_cookie
+ @cookies.sort!
+ end
+
+ # :api: private
+ def for(uri)
+ hash_for(uri).values.map { |c| c.raw }.join(';')
+ end
+
+ def to_hash
+ cookies = {}
+
+ hash_for(nil).each do |name, cookie|
+ cookies[name] = cookie.value
+ end
+
+ return cookies
+ end
+
+ protected
+
+ def hash_for(uri = nil)
+ cookies = {}
+
+ # The cookies are sorted by most specific first. So, we loop through
+ # all the cookies in order and add it to a hash by cookie name if
+ # the cookie can be sent to the current URI. It's added to the hash
+ # so that when we are done, the cookies will be unique by name and
+ # we'll have grabbed the most specific to the URI.
+ @cookies.each do |cookie|
+ cookies[cookie.name] = cookie if cookie.matches?(uri)
+ end
+
+ return cookies
+ end
+
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb
new file mode 100644
index 0000000000..a191fa23d8
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb
@@ -0,0 +1,45 @@
+require "forwardable"
+
+module Rack
+ module Test
+ module Methods
+ extend Forwardable
+
+ def rack_test_session
+ @_rack_test_session ||= Rack::Test::Session.new(app)
+ end
+
+ def rack_mock_session
+ @_rack_mock_session ||= Rack::MockSession.new(app)
+ end
+
+ METHODS = [
+ :request,
+
+ # HTTP verbs
+ :get,
+ :post,
+ :put,
+ :delete,
+ :head,
+
+ # Redirects
+ :follow_redirect!,
+
+ # Header-related features
+ :header,
+ :set_cookie,
+ :clear_cookies,
+ :authorize,
+ :basic_authorize,
+ :digest_authorize,
+
+ # Expose the last request and response
+ :last_response,
+ :last_request
+ ]
+
+ def_delegators :rack_test_session, *METHODS
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb
new file mode 100644
index 0000000000..81c398ba51
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb
@@ -0,0 +1,27 @@
+module Rack
+ module Test
+
+ class MockDigestRequest
+ def initialize(params)
+ @params = params
+ end
+
+ def method_missing(sym)
+ if @params.has_key? k = sym.to_s
+ return @params[k]
+ end
+
+ super
+ end
+
+ def method
+ @params['method']
+ end
+
+ def response(password)
+ Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
+ end
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb
new file mode 100644
index 0000000000..239302fbe4
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb
@@ -0,0 +1,36 @@
+require "tempfile"
+
+module Rack
+ module Test
+
+ class UploadedFile
+ # The filename, *not* including the path, of the "uploaded" file
+ attr_reader :original_filename
+
+ # The content type of the "uploaded" file
+ attr_accessor :content_type
+
+ def initialize(path, content_type = "text/plain", binary = false)
+ raise "#{path} file does not exist" unless ::File.exist?(path)
+ @content_type = content_type
+ @original_filename = ::File.basename(path)
+ @tempfile = Tempfile.new(@original_filename)
+ @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
+ @tempfile.binmode if binary
+ FileUtils.copy_file(path, @tempfile.path)
+ end
+
+ def path
+ @tempfile.path
+ end
+
+ alias_method :local_path, :path
+
+ def method_missing(method_name, *args, &block) #:nodoc:
+ @tempfile.__send__(method_name, *args, &block)
+ end
+
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb
new file mode 100644
index 0000000000..d25b849709
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb
@@ -0,0 +1,75 @@
+module Rack
+ module Test
+
+ module Utils
+ include Rack::Utils
+
+ def requestify(value, prefix = nil)
+ case value
+ when Array
+ value.map do |v|
+ requestify(v, "#{prefix}[]")
+ end.join("&")
+ when Hash
+ value.map do |k, v|
+ requestify(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
+ end.join("&")
+ else
+ "#{prefix}=#{escape(value)}"
+ end
+ end
+
+ module_function :requestify
+
+ def multipart_requestify(params, first=true)
+ p = Hash.new
+
+ params.each do |key, value|
+ k = first ? key.to_s : "[#{key}]"
+
+ if Hash === value
+ multipart_requestify(value, false).each do |subkey, subvalue|
+ p[k + subkey] = subvalue
+ end
+ else
+ p[k] = value
+ end
+ end
+
+ return p
+ end
+
+ module_function :multipart_requestify
+
+ def multipart_body(params)
+ multipart_requestify(params).map do |key, value|
+ if value.respond_to?(:original_filename)
+ ::File.open(value.path, "rb") do |f|
+ f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
+
+ <<-EOF
+--#{MULTIPART_BOUNDARY}\r
+Content-Disposition: form-data; name="#{key}"; filename="#{escape(value.original_filename)}"\r
+Content-Type: #{value.content_type}\r
+Content-Length: #{::File.stat(value.path).size}\r
+\r
+#{f.read}\r
+EOF
+ end
+ else
+<<-EOF
+--#{MULTIPART_BOUNDARY}\r
+Content-Disposition: form-data; name="#{key}"\r
+\r
+#{value}\r
+EOF
+ end
+ end.join("")+"--#{MULTIPART_BOUNDARY}--\r"
+ end
+
+ module_function :multipart_body
+
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index e604c2a581..94138097e3 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -21,15 +21,10 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift activesupport_path if File.directory?(activesupport_path)
+require 'active_support'
+require 'active_support/core_ext/class/attribute_accessors'
require File.join(File.dirname(__FILE__), "action_pack")
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index efed19a21d..4ab568b44c 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/module/attr_internal'
+require 'active_support/core_ext/module/delegation'
+
module ActionView #:nodoc:
class ActionViewError < StandardError #:nodoc:
end
@@ -191,12 +194,14 @@ module ActionView #:nodoc:
ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
end
- attr_internal :request
+ attr_internal :request, :layout
delegate :controller_path, :to => :controller, :allow_nil => true
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
- :flash, :logger, :action_name, :controller_name, :to => :controller
+ :flash, :action_name, :controller_name, :to => :controller
+
+ delegate :logger, :to => :controller, :allow_nil => true
delegate :find_by_parts, :to => :view_paths
@@ -264,15 +269,16 @@ module ActionView #:nodoc:
nil
end
- private
- # Evaluates the local assigns and controller ivars, pushes them to the view.
- def _evaluate_assigns_and_ivars #:nodoc:
- unless @assigns_added
- @assigns.each { |key, value| instance_variable_set("@#{key}", value) }
- _copy_ivars_from_controller
- @assigns_added = true
- end
+ # Evaluates the local assigns and controller ivars, pushes them to the view.
+ def _evaluate_assigns_and_ivars #:nodoc:
+ unless @assigns_added
+ @assigns.each { |key, value| instance_variable_set("@#{key}", value) }
+ _copy_ivars_from_controller
+ @assigns_added = true
end
+ end
+
+ private
def _copy_ivars_from_controller #:nodoc:
if @controller
@@ -283,8 +289,11 @@ module ActionView #:nodoc:
end
def _set_controller_content_type(content_type) #:nodoc:
- if controller.respond_to?(:response)
- controller.response.content_type ||= content_type
+ # TODO: Remove this method when new base is switched
+ unless defined?(ActionController::Http)
+ if controller.respond_to?(:response)
+ controller.response.content_type ||= content_type
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb
index 7c0dfdab10..b4b9f6e34b 100644
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -1,5 +1,6 @@
require 'cgi'
require 'action_view/helpers/form_helper'
+require 'active_support/core_ext/class/attribute_accessors'
module ActionView
class Base
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 9e39536653..b4197479a0 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -124,7 +124,11 @@ module ActionView
# Use an alternate output buffer for the duration of the block.
# Defaults to a new empty string.
- def with_output_buffer(buf = '') #:nodoc:
+ def with_output_buffer(buf = nil) #:nodoc:
+ unless buf
+ buf = ''
+ buf.force_encoding(output_buffer.encoding) if buf.respond_to?(:force_encoding)
+ end
self.output_buffer, old_buffer = buf, output_buffer
yield
output_buffer
@@ -134,9 +138,12 @@ module ActionView
# Add the output buffer to the response body and start a new one.
def flush_output_buffer #:nodoc:
- if output_buffer && output_buffer != ''
+ if output_buffer && !output_buffer.empty?
response.body_parts << output_buffer
- self.output_buffer = ''
+ new = ''
+ new.force_encoding(output_buffer.encoding) if new.respond_to?(:force_encoding)
+ self.output_buffer = new
+ nil
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index a59829b23f..beef661a37 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -2,6 +2,7 @@ require 'cgi'
require 'action_view/helpers/date_helper'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/form_tag_helper'
+require 'active_support/core_ext/class/inheritable_attributes'
module ActionView
module Helpers
@@ -1039,8 +1040,8 @@ module ActionView
end
end
- class Base
- cattr_accessor :default_form_builder
- self.default_form_builder = ::ActionView::Helpers::FormBuilder
+ class << Base
+ attr_accessor :default_form_builder
end
-end \ No newline at end of file
+ Base.default_form_builder = ::ActionView::Helpers::FormBuilder
+end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 6b385ef77d..6adbab175f 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -230,6 +230,8 @@ module ActionView
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def options_for_select(container, selected = nil)
+ return container if String === container
+
container = container.to_a if Hash === container
selected, disabled = extract_selected_and_disabled(selected)
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index dea958deaf..999d5b34fc 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/float/rounding'
+
module ActionView
module Helpers #:nodoc:
# Provides methods for converting numbers into formatted strings.
@@ -246,6 +248,11 @@ module ActionView
# number_to_human_size(483989, :precision => 0) # => 473 KB
# number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
#
+ # Zeros after the decimal point are always stripped out, regardless of the
+ # specified precision:
+ # helper.number_to_human_size(1234567890123, :precision => 5) # => "1.12283 TB"
+ # helper.number_to_human_size(524288000, :precision=>5) # => "500 MB"
+ #
# You can still use <tt>number_to_human_size</tt> with the old API that accepts the
# +precision+ as its optional second parameter:
# number_to_human_size(1234567, 2) # => 1.18 MB
@@ -291,7 +298,7 @@ module ActionView
:precision => precision,
:separator => separator,
:delimiter => delimiter
- ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
+ ).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
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 6bad11e354..c0f5df3468 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -1,5 +1,6 @@
require 'set'
require 'active_support/json'
+require 'active_support/core_ext/object/extending'
module ActionView
module Helpers
@@ -572,6 +573,7 @@ module ActionView
# #include_helpers_from_context has nothing to overwrite.
class JavaScriptGenerator #:nodoc:
def initialize(context, &block) #:nodoc:
+ context._evaluate_assigns_and_ivars
@context, @lines = context, []
include_helpers_from_context
@context.with_output_buffer(@lines) do
@@ -686,7 +688,7 @@ module ActionView
# Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
# expression as an argument to another JavaScriptGenerator method.
def literal(code)
- ActiveSupport::JSON::Variable.new(code.to_s)
+ ::ActiveSupport::JSON::Variable.new(code.to_s)
end
# Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
@@ -973,7 +975,7 @@ module ActionView
def loop_on_multiple_args(method, ids)
record(ids.size>1 ?
"#{javascript_object_for(ids)}.each(#{method})" :
- "#{method}(#{ids.first.to_json})")
+ "#{method}(#{javascript_object_for(ids.first)})")
end
def page
@@ -997,7 +999,7 @@ module ActionView
end
def javascript_object_for(object)
- object.respond_to?(:to_json) ? object.to_json : object.inspect
+ ::ActiveSupport::JSON.encode(object)
end
def arguments_for_call(arguments, block = nil)
@@ -1139,7 +1141,7 @@ module ActionView
class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
def initialize(generator, id)
@id = id
- super(generator, "$(#{id.to_json})")
+ super(generator, "$(#{::ActiveSupport::JSON.encode(id)})")
end
# Allows access of element attributes through +attribute+. Examples:
@@ -1180,11 +1182,11 @@ module ActionView
# The JSON Encoder calls this to check for the +to_json+ method
# Since it's a blank slate object, I suppose it responds to anything.
- def respond_to?(method)
+ def respond_to?(*)
true
end
- def to_json(options = nil)
+ def rails_to_json(*)
@variable
end
@@ -1211,7 +1213,7 @@ module ActionView
enumerate :eachSlice, :variable => variable, :method_args => [number], :yield_args => %w(value index), :return => true, &block
else
add_variable_assignment!(variable)
- append_enumerable_function!("eachSlice(#{number.to_json});")
+ append_enumerable_function!("eachSlice(#{::ActiveSupport::JSON.encode(number)});")
end
end
@@ -1232,7 +1234,7 @@ module ActionView
def pluck(variable, property)
add_variable_assignment!(variable)
- append_enumerable_function!("pluck(#{property.to_json});")
+ append_enumerable_function!("pluck(#{::ActiveSupport::JSON.encode(property)});")
end
def zip(variable, *arguments, &block)
@@ -1296,7 +1298,7 @@ module ActionView
class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\
def initialize(generator, pattern)
- super(generator, "$$(#{pattern.to_json})")
+ super(generator, "$$(#{::ActiveSupport::JSON.encode(pattern)})")
end
end
end
diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
index e16935ea87..04af2781d7 100644
--- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
+++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
@@ -43,7 +43,7 @@ module ActionView
# You can change the behaviour with various options, see
# http://script.aculo.us for more documentation.
def visual_effect(name, element_id = false, js_options = {})
- element = element_id ? element_id.to_json : "element"
+ element = element_id ? ActiveSupport::JSON.encode(element_id) : "element"
js_options[:queue] = if js_options[:queue].is_a?(Hash)
'{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
@@ -138,7 +138,7 @@ module ActionView
end
def sortable_element_js(element_id, options = {}) #:nodoc:
- options[:with] ||= "Sortable.serialize(#{element_id.to_json})"
+ options[:with] ||= "Sortable.serialize(#{ActiveSupport::JSON.encode(element_id)})"
options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
@@ -149,7 +149,7 @@ module ActionView
options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment]
options[:only] = array_or_string_for_javascript(options[:only]) if options[:only]
- %(Sortable.create(#{element_id.to_json}, #{options_for_javascript(options)});)
+ %(Sortable.create(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
end
# Makes the element with the DOM ID specified by +element_id+ draggable.
@@ -164,7 +164,7 @@ module ActionView
end
def draggable_element_js(element_id, options = {}) #:nodoc:
- %(new Draggable(#{element_id.to_json}, #{options_for_javascript(options)});)
+ %(new Draggable(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
end
# Makes the element with the DOM ID specified by +element_id+ receive
@@ -219,7 +219,7 @@ module ActionView
# Confirmation happens during the onDrop callback, so it can be removed from the options
options.delete(:confirm) if options[:confirm]
- %(Droppables.add(#{element_id.to_json}, #{options_for_javascript(options)});)
+ %(Droppables.add(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index af8c4d5e21..66d7592874 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -9,7 +9,7 @@ module ActionView
include ERB::Util
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
- BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
+ BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attr| attr.to_sym })
# Returns an empty HTML tag of type +name+ which by default is XHTML
# compliant. Set +open+ to true to create an open tag compatible
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 573b99b96e..ad0733a7e1 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -34,12 +34,16 @@ module ActionView
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...").
+ # Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
#
# ==== Examples
#
# truncate("Once upon a time in a world far far away")
# # => Once upon a time in a world f...
#
+ # truncate("Once upon a time in a world far far away", :separator => ' ')
+ # # => Once upon a time in a world...
+ #
# truncate("Once upon a time in a world far far away", :length => 14)
# # => Once upon a...
#
@@ -71,7 +75,8 @@ module ActionView
if text
l = options[:length] - options[:omission].mb_chars.length
chars = text.mb_chars
- (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s
+ stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l
+ (chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s
end
end
@@ -535,7 +540,7 @@ module ActionView
link_attributes = html_options.stringify_keys
text.gsub(AUTO_LINK_RE) do
href = $&
- punctuation = ''
+ punctuation = []
left, right = $`, $'
# detect already linked URLs and URLs in the middle of a tag
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
@@ -543,17 +548,18 @@ module ActionView
href
else
# don't include trailing punctuation character as part of the URL
- if href.sub!(/[^\w\/-]$/, '') and punctuation = $& and opening = BRACKETS[punctuation]
- if href.scan(opening).size > href.scan(punctuation).size
- href << punctuation
- punctuation = ''
+ while href.sub!(/[^\w\/-]$/, '')
+ punctuation.push $&
+ if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
+ href << punctuation.pop
+ break
end
end
link_text = block_given?? yield(href) : href
href = 'http://' + href unless href.index('http') == 0
- content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation
+ content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('')
end
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 1d0279889c..95c56faf9c 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -2,8 +2,8 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
- cache = !Object.const_defined?(:Rails) || Rails.configuration.cache_classes
- Template::FileSystemPath.new(obj, :cache => cache)
+ cache = !defined?(Rails) || !Rails.respond_to?(:configuration) || Rails.configuration.cache_classes
+ Template::FileSystemPathWithFallback.new(obj, :cache => cache)
else
obj
end
@@ -33,19 +33,19 @@ module ActionView #:nodoc:
super(*objs.map { |obj| self.class.type_cast(obj) })
end
- def find_by_parts(path, extension = nil, prefix = nil, partial = false)
- template_path = path.sub(/^\//, '')
+ def find_by_parts(path, details = {}, prefix = nil, partial = false)
+ # template_path = path.sub(/^\//, '')
+ template_path = path
each do |load_path|
- if template = load_path.find_by_parts(template_path, extension, prefix, partial)
+ if template = load_path.find_by_parts(template_path, details, prefix, partial)
return template
end
end
-
- Template.new(path, self)
- rescue ActionView::MissingTemplate => e
- extension ||= []
- raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}.{#{extension.join(",")}}")
+
+ # TODO: Have a fallback absolute path?
+ extension = details[:formats] || []
+ raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path} - #{details.inspect} - partial: #{!!partial}")
end
def find_by_parts?(path, extension = nil, prefix = nil, partial = false)
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index e337dcb63b..eacf117bea 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -171,11 +171,21 @@ module ActionView
# <% end %>
module Partials
extend ActiveSupport::Memoizable
+ extend ActiveSupport::Concern
+
+ included do
+ attr_accessor :_partial
+ end
+
+ def _render_partial_from_controller(*args)
+ @assigns_added = false
+ _render_partial(*args)
+ end
def _render_partial(options = {}) #:nodoc:
options[:locals] ||= {}
- case path = partial = options[:partial]
+ case path = partial = options[:partial]
when *_array_like_objects
return _render_partial_collection(partial, options)
else
@@ -187,6 +197,7 @@ module ActionView
path = ActionController::RecordIdentifier.partial_path(object, controller_path)
end
_, _, prefix, object = parts = partial_parts(path, options)
+ parts[1] = {:formats => parts[1]}
template = find_by_parts(*parts)
_render_partial_object(template, options, (object unless object == true))
end
@@ -221,16 +232,7 @@ module ActionView
ensure
@_proc_for_layout = nil
end
-
- def _render_partial_with_layout(layout, options)
- if layout
- prefix = controller && !layout.include?("/") ? controller.controller_path : nil
- layout = find_by_parts(layout, formats, prefix, true)
- end
- content = _render_partial(options)
- return _render_content_with_layout(content, layout, options[:locals])
- end
-
+
def _deprecated_ivar_assign(template)
if respond_to?(:controller)
ivar = :"@#{template.variable_name}"
@@ -253,7 +255,7 @@ module ActionView
def _render_partial_with_layout(layout, options)
if layout
prefix = controller && !layout.include?("/") ? controller.controller_path : nil
- layout = find_by_parts(layout, formats, prefix, true)
+ layout = find_by_parts(layout, {:formats => formats}, prefix, true)
end
content = _render_partial(options)
return _render_content_with_layout(content, layout, options[:locals])
@@ -286,13 +288,17 @@ module ActionView
locals = (options[:locals] ||= {})
object ||= locals[:object] || locals[template.variable_name]
- _set_locals(object, locals, template, options)
+ _set_locals(object, locals, template, options)
+
+ self._partial = template
+
_render_template(template, locals)
end
end
def _set_locals(object, locals, template, options)
object ||= _deprecated_ivar_assign(template)
+
locals[:object] = locals[template.variable_name] = object
locals[options[:as]] = object if options[:as]
end
@@ -315,13 +321,16 @@ module ActionView
locals[template.counter_name] = index
index += 1
+
+ self._partial = template
+
_render_template(template, locals)
end.join(spacer)
end
def _pick_partial_template(partial_path) #:nodoc:
prefix = controller_path unless partial_path.include?('/')
- find_by_parts(partial_path, formats, prefix, true)
+ find_by_parts(partial_path, {:formats => formats}, prefix, true)
end
memoize :_pick_partial_template
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index a9b2acecd5..fe785e7b20 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -23,10 +23,10 @@ module ActionView
return _render_partial_with_layout(layout, options) if options.key?(:partial)
return _render_partial_with_block(layout, block, options) if block_given?
- layout = find_by_parts(layout, formats) if layout
+ layout = find_by_parts(layout, {:formats => formats}) if layout
if file = options[:file]
- template = find_by_parts(file, formats)
+ template = find_by_parts(file, {:formats => formats})
_render_template_with_layout(template, layout, :locals => options[:locals])
elsif inline = options[:inline]
_render_inline(inline, layout, options)
@@ -46,8 +46,8 @@ module ActionView
locals ||= {}
if controller && layout
- response.layout = layout.path_without_format_and_extension if controller.respond_to?(:response)
- logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger
+ @_layout = layout.identifier
+ logger.info("Rendering template within #{layout.identifier}") if logger
end
begin
@@ -76,7 +76,6 @@ module ActionView
end
end
rescue Exception => e
- raise e if template.is_a?(InlineTemplate) || !template.filename
if TemplateError === e
e.sub_template_of(template)
raise e
@@ -86,7 +85,9 @@ module ActionView
end
def _render_inline(inline, layout, options)
- content = _render_template(InlineTemplate.new(options[:inline], options[:type]), options[:locals] || {})
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ template = Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
+ content = _render_template(template, options[:locals] || {})
layout ? _render_content_with_layout(content, layout, options[:locals]) : content
end
@@ -94,9 +95,14 @@ module ActionView
layout ? _render_content_with_layout(text, layout, options[:locals]) : text
end
+ def _render_template_from_controller(*args)
+ @assigns_added = nil
+ _render_template_with_layout(*args)
+ end
+
def _render_template_with_layout(template, layout = nil, options = {}, partial = false)
if controller && logger
- logger.info("Rendering #{template.path_without_extension}" +
+ logger.info("Rendering #{template.identifier}" +
(options[:status] ? " (#{options[:status]})" : ''))
end
@@ -107,7 +113,7 @@ module ActionView
_render_template(template, options[:locals] || {})
end
- return content unless layout && !template.exempt_from_layout?
+ return content unless layout
_render_content_with_layout(content, layout, options[:locals] || {})
end
end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index 37cb1c7c6c..a06e80b294 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -12,7 +12,7 @@ module ActionView
end
def file_name
- @template.relative_path
+ @template.identifier
end
def message
@@ -30,7 +30,7 @@ module ActionView
def sub_template_message
if @sub_templates
"Trace of template inclusion: " +
- @sub_templates.collect { |template| template.relative_path }.join(", ")
+ @sub_templates.collect { |template| template.identifier }.join(", ")
else
""
end
diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb
index 672da0ed2b..3071c78174 100644
--- a/actionpack/lib/action_view/template/handler.rb
+++ b/actionpack/lib/action_view/template/handler.rb
@@ -1,3 +1,6 @@
+require "active_support/core_ext/class/inheritable_attributes"
+require "action_dispatch/http/mime_type"
+
# Legacy TemplateHandler stub
module ActionView
module TemplateHandlers #:nodoc:
@@ -19,6 +22,9 @@ module ActionView
end
class TemplateHandler
+ extlib_inheritable_accessor :default_format
+ self.default_format = Mime::HTML
+
def self.call(template)
"#{name}.new(self).render(template, local_assigns)"
end
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index fb85f28851..faf54b9fe5 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -16,6 +16,10 @@ module ActionView #:nodoc:
@@template_handlers = {}
@@default_template_handlers = nil
+
+ def self.extensions
+ @@template_handlers.keys
+ end
# Register a class that knows how to handle template files with the given
# extension. This can be used to implement new template types.
@@ -29,7 +33,7 @@ module ActionView #:nodoc:
end
def template_handler_extensions
- @@template_handlers.keys.map(&:to_s).sort
+ @@template_handlers.keys.map {|key| key.to_s }.sort
end
def registered_template_handler(extension)
@@ -42,7 +46,7 @@ module ActionView #:nodoc:
end
def handler_class_for_extension(extension)
- registered_template_handler(extension) || @@default_template_handlers
+ (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
end
end
end
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
index 788dc93326..f412228752 100644
--- a/actionpack/lib/action_view/template/handlers/builder.rb
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -5,6 +5,8 @@ module ActionView
class Builder < TemplateHandler
include Compilable
+ self.default_format = Mime::XML
+
def compile(template)
"_set_controller_content_type(Mime::XML);" +
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index a20b1b0cd3..d773df7d29 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -1,4 +1,5 @@
require 'erb'
+require 'active_support/core_ext/class/attribute_accessors'
module ActionView
module TemplateHandlers
@@ -12,12 +13,10 @@ module ActionView
cattr_accessor :erb_trim_mode
self.erb_trim_mode = '-'
- def compile(template)
- src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
+ self.default_format = Mime::HTML
- # Ruby 1.9 prepends an encoding to the source. However this is
- # useless because you can only set an encoding on the first line
- RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
+ def compile(template)
+ ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
end
end
end
diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb
index 802a79b3fc..a36744c2b7 100644
--- a/actionpack/lib/action_view/template/handlers/rjs.rb
+++ b/actionpack/lib/action_view/template/handlers/rjs.rb
@@ -3,11 +3,17 @@ module ActionView
class RJS < TemplateHandler
include Compilable
+ self.default_format = Mime::JS
+
def compile(template)
"@formats = [:html];" +
"controller.response.content_type ||= Mime::JS;" +
"update_page do |page|;#{template.source}\nend"
end
+
+ def default_format
+ Mime::JS
+ end
end
end
end
diff --git a/actionpack/lib/action_view/template/path.rb b/actionpack/lib/action_view/template/path.rb
index 9709549b70..478bf96c9a 100644
--- a/actionpack/lib/action_view/template/path.rb
+++ b/actionpack/lib/action_view/template/path.rb
@@ -1,23 +1,82 @@
+require "pathname"
+
module ActionView
class Template
+ # Abstract super class
class Path
- attr_reader :path, :paths
- delegate :hash, :inspect, :to => :path
-
def initialize(options)
- @cache = options[:cache]
+ @cache = options[:cache]
+ @cached = {}
+ end
+
+ # Normalizes the arguments and passes it on to find_template
+ def find_by_parts(*args)
+ find_all_by_parts(*args).first
+ end
+
+ def find_all_by_parts(name, details = {}, prefix = nil, partial = nil)
+ details[:locales] = [I18n.locale]
+ name = name.to_s.gsub(handler_matcher, '').split("/")
+ find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial)
+ end
+
+ private
+
+ # This is what child classes implement. No defaults are needed
+ # because Path guarentees that the arguments are present and
+ # normalized.
+ def find_templates(name, details, prefix, partial)
+ raise NotImplementedError
+ end
+
+ def valid_handlers
+ @valid_handlers ||= TemplateHandlers.extensions
+ end
+
+ def handler_matcher
+ @handler_matcher ||= begin
+ e = valid_handlers.join('|')
+ /\.(?:#{e})$/
+ end
+ end
+
+ def handler_glob
+ e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join
+ "{#{e}}"
+ end
+
+ def formats_glob
+ @formats_glob ||= begin
+ formats = Mime::SET.map { |m| m.symbol }
+ '{' + formats.map { |l| ".#{l}," }.join + '}'
+ end
+ end
+
+ def cached(key)
+ return yield unless @cache
+ return @cached[key] if @cached.key?(key)
+ @cached[key] = yield
+ end
+ end
+
+ class FileSystemPath < Path
+
+ def initialize(path, options = {})
+ raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
+ super(options)
+ @path = Pathname.new(path).expand_path
end
+ # TODO: This is the currently needed API. Make this suck less
+ # ==== <suck>
+ attr_reader :path
+
def to_s
- if defined?(RAILS_ROOT)
- path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
- else
- path.to_s
- end
+ path.to_s
end
def to_str
- path.to_str
+ path.to_s
end
def ==(path)
@@ -27,59 +86,65 @@ module ActionView
def eql?(path)
to_str == path.to_str
end
-
- def find_by_parts(name, extensions = nil, prefix = nil, partial = nil)
- path = prefix ? "#{prefix}/" : ""
-
- name = name.to_s.split("/")
- name[-1] = "_#{name[-1]}" if partial
+ # ==== </suck>
- path << name.join("/")
-
- template = nil
-
- Array(extensions).each do |extension|
- extensioned_path = extension ? "#{path}.#{extension}" : path
- break if (template = find_template(extensioned_path))
+ def find_templates(name, details, prefix, partial, root = "#{@path}/")
+ if glob = details_to_glob(name, details, prefix, partial, root)
+ cached(glob) do
+ Dir[glob].map do |path|
+ next if File.directory?(path)
+ source = File.read(path)
+ identifier = Pathname.new(path).expand_path.to_s
+
+ Template.new(source, identifier, *path_to_details(path))
+ end.compact
+ end
end
- template || find_template(path)
end
-
+
private
- def create_template(file)
- Template.new(file.split("#{self}/").last, self)
- end
- end
-
- class FileSystemPath < Path
- def initialize(path, options = {})
- raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
-
- super(options)
- @path, @paths = path, {}
+
+ # :api: plugin
+ def details_to_glob(name, details, prefix, partial, root)
+ path = ""
+ path << "#{prefix}/" unless prefix.empty?
+ path << (partial ? "_#{name}" : name)
+
+ extensions = ""
+ [:locales, :formats].each do |k|
+ extensions << if exts = details[k]
+ '{' + exts.map {|e| ".#{e},"}.join + '}'
+ else
+ k == :formats ? formats_glob : ''
+ end
+ end
- # **/*/** is a hax for symlinked directories
- load_templates("#{@path}/{**/*,**}/**") if @cache
+ "#{root}#{path}#{extensions}#{handler_glob}"
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[1] == '_'
+ details = (m[2]||"").split('.').reject { |e| e.empty? }
+ handler = Template.handler_class_for_extension(m[3])
- private
-
- def load_template(template)
- template.load!
- template.accessible_paths.each do |path|
- @paths[path] = template
+ format = Mime[details.last] && details.pop.to_sym
+ locale = details.last && details.pop.to_sym
+
+ return handler, :format => format, :locale => locale, :partial => partial
end
end
-
- def find_template(path)
- load_templates("#{@path}/#{path}{,.*}") unless @cache
- @paths[path]
- end
-
- def load_templates(glob)
- Dir[glob].each do |file|
- load_template(create_template(file)) unless File.directory?(file)
- end
+ end
+
+ class FileSystemPathWithFallback < FileSystemPath
+
+ def find_templates(name, details, prefix, partial)
+ templates = super
+ return super(name, details, prefix, partial, '') if templates.empty?
+ templates
end
end
diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb
index 0d2f201458..a9897258d2 100644
--- a/actionpack/lib/action_view/template/template.rb
+++ b/actionpack/lib/action_view/template/template.rb
@@ -1,187 +1,92 @@
+# encoding: utf-8
+# This is so that templates compiled in this file are UTF-8
+
+require 'set'
require "action_view/template/path"
-module ActionView #:nodoc:
+module ActionView
class Template
extend TemplateHandlers
- extend ActiveSupport::Memoizable
-
- module Loading
- def load!
- @cached = true
- # freeze
- end
- end
- include Loading
+ attr_reader :source, :identifier, :handler, :mime_type, :details
- include Renderable
-
- # Templates that are exempt from layouts
- @@exempt_from_layout = Set.new([/\.rjs$/])
-
- # Don't render layouts for templates with the given extensions.
- def self.exempt_from_layout(*extensions)
- regexps = extensions.collect do |extension|
- extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
+ def initialize(source, identifier, handler, details)
+ @source = source
+ @identifier = identifier
+ @handler = handler
+ @details = details
+
+ format = details.delete(:format) || begin
+ # TODO: Clean this up
+ handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
end
- @@exempt_from_layout.merge(regexps)
- end
-
- attr_accessor :template_path, :filename, :load_path, :base_path
- attr_accessor :locale, :name, :format, :extension
- delegate :to_s, :to => :path
-
- def initialize(template_path, load_paths = [])
- template_path = template_path.dup
- @load_path, @filename = find_full_path(template_path, load_paths)
- @base_path, @name, @locale, @format, @extension = split(template_path)
- @base_path.to_s.gsub!(/\/$/, '') # Push to split method
-
- # Extend with partial super powers
- extend RenderablePartial if @name =~ /^_/
+ @mime_type = Mime::Type.lookup_by_extension(format.to_s)
+ @details[:formats] = Array.wrap(format && format.to_sym)
end
- def accessible_paths
- paths = []
-
- if valid_extension?(extension)
- paths << path
- paths << path_without_extension
- if multipart?
- formats = format.split(".")
- paths << "#{path_without_format_and_extension}.#{formats.first}"
- paths << "#{path_without_format_and_extension}.#{formats.second}"
- end
- else
- # template without explicit template handler should only be reachable through its exact path
- paths << template_path
- end
-
- paths
- end
-
- def relative_path
- path = File.expand_path(filename)
- path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
- path
+ def render(view, locals, &blk)
+ method_name = compile(locals, view)
+ view.send(method_name, locals, &blk)
end
- memoize :relative_path
- def source
- File.read(filename)
+ # TODO: Figure out how to abstract this
+ def variable_name
+ identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
- memoize :source
-
- def exempt_from_layout?
- @@exempt_from_layout.any? { |exempted| path =~ exempted }
- end
-
- def path_without_extension
- [base_path, [name, locale, format].compact.join('.')].compact.join('/')
- end
- memoize :path_without_extension
- def path_without_format_and_extension
- [base_path, [name, locale].compact.join('.')].compact.join('/')
- end
- memoize :path_without_format_and_extension
-
- def path
- [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
- end
- memoize :path
-
- def mime_type
- Mime::Type.lookup_by_extension(format) if format && defined?(::Mime)
+ # TODO: Figure out how to abstract this
+ def counter_name
+ "#{variable_name}_counter".to_sym
end
- memoize :mime_type
- def multipart?
- format && format.include?('.')
- end
-
- def content_type
- format.gsub('.', '/')
- end
-
- private
-
- def format_and_extension
- (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
- end
- memoize :format_and_extension
-
- def mtime
- File.mtime(filename)
- end
- memoize :mtime
-
- def method_segment
- relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
- end
- memoize :method_segment
-
- def stale?
- File.mtime(filename) > mtime
- end
-
- def recompile?
- !@cached
+ # TODO: kill hax
+ def partial?
+ @details[:partial]
end
- def valid_extension?(extension)
- !Template.registered_template_handler(extension).nil?
- end
-
- def valid_locale?(locale)
- I18n.available_locales.include?(locale.to_sym)
- end
+ private
- def find_full_path(path, load_paths)
- load_paths = Array(load_paths) + [nil]
- load_paths.each do |load_path|
- file = load_path ? "#{load_path.to_str}/#{path}" : path
- return load_path, file if File.file?(file)
- end
- raise MissingTemplate.new(load_paths, path)
- end
+ def compile(locals, view)
+ method_name = build_method_name(locals)
+
+ return method_name if view.respond_to?(method_name)
+
+ locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
+
+ code = @handler.call(self)
+ encoding_comment = $1 if code.sub!(/\A(#.*coding.*)\n/, '')
+
+ source = <<-end_src
+ def #{method_name}(local_assigns)
+ old_output_buffer = output_buffer;#{locals_code};#{code}
+ ensure
+ self.output_buffer = old_output_buffer
+ end
+ end_src
- # Returns file split into an array
- # [base_path, name, locale, format, extension]
- def split(file)
- if m = file.to_s.match(/^(.*\/)?([^\.]+)\.(.*)$/)
- base_path = m[1]
- name = m[2]
- extensions = m[3]
+ if encoding_comment
+ source = "#{encoding_comment}\n#{source}"
+ line = -1
else
- return
+ line = 0
end
- locale = nil
- format = nil
- extension = nil
-
- if m = extensions.split(".")
- if valid_locale?(m[0]) && m[1] && valid_extension?(m[2]) # All three
- locale = m[0]
- format = m[1]
- extension = m[2]
- elsif m[0] && m[1] && valid_extension?(m[2]) # Multipart formats
- format = "#{m[0]}.#{m[1]}"
- extension = m[2]
- elsif valid_locale?(m[0]) && valid_extension?(m[1]) # locale and extension
- locale = m[0]
- extension = m[1]
- elsif valid_extension?(m[1]) # format and extension
- format = m[0]
- extension = m[1]
- elsif valid_extension?(m[0]) # Just extension
- extension = m[0]
- else # No extension
- format = m[0]
+ begin
+ ActionView::Base::CompiledTemplates.module_eval(source, identifier, line)
+ method_name
+ rescue Exception => e # errors from template code
+ if logger = (view && view.logger)
+ logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
+ logger.debug "Function body: #{source}"
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
end
- end
- [base_path, name, locale, format, extension]
+ raise ActionView::TemplateError.new(self, {}, e)
+ end
+ end
+
+ def build_method_name(locals)
+ # TODO: is locals.keys.hash reliably the same?
+ "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
end
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index f81174d707..fd57b1677e 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -1,9 +1,21 @@
module ActionView #:nodoc:
class TextTemplate < String #:nodoc:
+
+ def initialize(string, content_type = Mime[:html])
+ super(string.to_s)
+ @content_type = Mime[content_type]
+ end
+
+ def details
+ {:formats => [@content_type.to_sym]}
+ end
+
+ def identifier() self end
def render(*) self end
- def exempt_from_layout?() false end
-
+ def mime_type() @content_type end
+
+ def partial?() false end
end
end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index c8f204046b..7355af4192 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -7,19 +7,21 @@ module ActionView
@_rendered = { :template => nil, :partials => Hash.new(0) }
initialize_without_template_tracking(*args)
end
-
+
+ attr_internal :rendered
alias_method :_render_template_without_template_tracking, :_render_template
def _render_template(template, local_assigns = {})
- if template.respond_to?(:path) && !template.is_a?(InlineTemplate)
- @_rendered[:partials][template] += 1 if template.is_a?(RenderablePartial)
- @_rendered[:template] ||= template
+ if template.respond_to?(:identifier) && template.present?
+ @_rendered[:partials][template] += 1 if template.partial?
+ @_rendered[:template] ||= []
+ @_rendered[:template] << template
end
_render_template_without_template_tracking(template, local_assigns)
- end
+ end
end
class TestCase < ActiveSupport::TestCase
- include ActionController::TestCase::Assertions
+ include ActionDispatch::Assertions
include ActionController::TestProcess
class_inheritable_accessor :helper_class
diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb
index 918062c7db..9c028e7d1e 100644
--- a/actionpack/test/abstract_controller/abstract_controller_test.rb
+++ b/actionpack/test/abstract_controller/abstract_controller_test.rb
@@ -20,14 +20,14 @@ module AbstractController
class TestBasic < ActiveSupport::TestCase
test "dispatching works" do
result = Me.process(:index)
- assert_equal "Hello world", result.response_obj[:body]
+ assert_equal "Hello world", result.response_body
end
end
# Test Render mixin
# ====
class RenderingController < AbstractController::Base
- use Renderer
+ include Renderer
def _prefix() end
@@ -58,38 +58,38 @@ module AbstractController
end
def rendering_to_body
- render_to_body "naked_render.erb"
+ self.response_body = render_to_body :_template_name => "naked_render.erb"
end
def rendering_to_string
- render_to_string "naked_render.erb"
+ self.response_body = render_to_string :_template_name => "naked_render.erb"
end
end
class TestRenderer < ActiveSupport::TestCase
test "rendering templates works" do
result = Me2.process(:index)
- assert_equal "Hello from index.erb", result.response_obj[:body]
+ assert_equal "Hello from index.erb", result.response_body
end
test "rendering passes ivars to the view" do
result = Me2.process(:action_with_ivars)
- assert_equal "Hello from index_with_ivars.erb", result.response_obj[:body]
+ assert_equal "Hello from index_with_ivars.erb", result.response_body
end
test "rendering with no template name" do
result = Me2.process(:naked_render)
- assert_equal "Hello from naked_render.erb", result.response_obj[:body]
+ assert_equal "Hello from naked_render.erb", result.response_body
end
test "rendering to a rack body" do
result = Me2.process(:rendering_to_body)
- assert_equal "Hello from naked_render.erb", result.response_obj[:body]
+ assert_equal "Hello from naked_render.erb", result.response_body
end
test "rendering to a string" do
result = Me2.process(:rendering_to_string)
- assert_equal "Hello from naked_render.erb", result.response_obj[:body]
+ assert_equal "Hello from naked_render.erb", result.response_body
end
end
@@ -121,12 +121,12 @@ module AbstractController
class TestPrefixedViews < ActiveSupport::TestCase
test "templates are located inside their 'prefix' folder" do
result = Me3.process(:index)
- assert_equal "Hello from me3/index.erb", result.response_obj[:body]
+ assert_equal "Hello from me3/index.erb", result.response_body
end
test "templates included their format" do
result = Me3.process(:formatted)
- assert_equal "Hello from me3/formatted.html.erb", result.response_obj[:body]
+ assert_equal "Hello from me3/formatted.html.erb", result.response_body
end
end
@@ -134,26 +134,27 @@ module AbstractController
# ====
# self._layout is used when defined
class WithLayouts < PrefixedViews
- use Layouts
+ include Layouts
+
+ def self.inherited(klass)
+ klass._write_layout_method
+ super
+ end
private
def self.layout(formats)
begin
- view_paths.find_by_parts(name.underscore, formats, "layouts")
+ view_paths.find_by_parts(name.underscore, {:formats => formats}, "layouts")
rescue ActionView::MissingTemplate
begin
- view_paths.find_by_parts("application", formats, "layouts")
+ view_paths.find_by_parts("application", {:formats => formats}, "layouts")
rescue ActionView::MissingTemplate
end
end
end
-
- def _layout
- self.class.layout(formats)
- end
-
+
def render_to_body(options = {})
- options[:_layout] = options[:layout] || _layout
+ options[:_layout] = options[:layout] || _default_layout
super
end
end
@@ -173,12 +174,7 @@ module AbstractController
class TestLayouts < ActiveSupport::TestCase
test "layouts are included" do
result = Me4.process(:index)
- assert_equal "Me4 Enter : Hello from me4/index.erb : Exit", result.response_obj[:body]
- end
-
- test "it can fall back to the application layout" do
- result = Me5.process(:index)
- assert_equal "Application Enter : Hello from me5/index.erb : Exit", result.response_obj[:body]
+ assert_equal "Me4 Enter : Hello from me4/index.erb : Exit", result.response_body
end
end
@@ -205,17 +201,16 @@ module AbstractController
def fail() self.response_body = "fail" end
private
-
- def respond_to_action?(action_name)
- action_name != :fail
+
+ def method_for_action(action_name)
+ action_name.to_s != "fail" && action_name
end
-
end
class TestRespondToAction < ActiveSupport::TestCase
def assert_dispatch(klass, body = "success", action = :index)
- response = klass.process(action).response_obj[:body]
+ response = klass.process(action).response_body
assert_equal body, response
end
diff --git a/actionpack/test/abstract_controller/callbacks_test.rb b/actionpack/test/abstract_controller/callbacks_test.rb
index 5fce30f478..1de60868c3 100644
--- a/actionpack/test/abstract_controller/callbacks_test.rb
+++ b/actionpack/test/abstract_controller/callbacks_test.rb
@@ -4,7 +4,7 @@ module AbstractController
module Testing
class ControllerWithCallbacks < AbstractController::Base
- use AbstractController::Callbacks
+ include AbstractController::Callbacks
end
class Callback1 < ControllerWithCallbacks
@@ -22,7 +22,7 @@ module AbstractController
class TestCallbacks < ActiveSupport::TestCase
test "basic callbacks work" do
result = Callback1.process(:index)
- assert_equal "Hello world", result.response_obj[:body]
+ assert_equal "Hello world", result.response_body
end
end
@@ -53,7 +53,7 @@ module AbstractController
class TestCallbacks < ActiveSupport::TestCase
test "before_filter works" do
result = Callback2.process(:index)
- assert_equal "Hello world", result.response_obj[:body]
+ assert_equal "Hello world", result.response_body
end
test "after_filter works" do
@@ -84,7 +84,7 @@ module AbstractController
class TestCallbacks < ActiveSupport::TestCase
test "before_filter works with procs" do
result = Callback3.process(:index)
- assert_equal "Hello world", result.response_obj[:body]
+ assert_equal "Hello world", result.response_body
end
test "after_filter works with procs" do
@@ -119,12 +119,12 @@ module AbstractController
class TestCallbacks < ActiveSupport::TestCase
test "when :only is specified, a before filter is triggered on that action" do
result = CallbacksWithConditions.process(:index)
- assert_equal "Hello, World", result.response_obj[:body]
+ assert_equal "Hello, World", result.response_body
end
test "when :only is specified, a before filter is not triggered on other actions" do
result = CallbacksWithConditions.process(:sekrit_data)
- assert_equal "true", result.response_obj[:body]
+ assert_equal "true", result.response_body
end
test "when :except is specified, an after filter is not triggered on that action" do
@@ -159,12 +159,12 @@ module AbstractController
class TestCallbacks < ActiveSupport::TestCase
test "when :only is specified with an array, a before filter is triggered on that action" do
result = CallbacksWithArrayConditions.process(:index)
- assert_equal "Hello, World", result.response_obj[:body]
+ assert_equal "Hello, World", result.response_body
end
test "when :only is specified with an array, a before filter is not triggered on other actions" do
result = CallbacksWithArrayConditions.process(:sekrit_data)
- assert_equal "true", result.response_obj[:body]
+ assert_equal "true", result.response_body
end
test "when :except is specified with an array, an after filter is not triggered on that action" do
@@ -184,12 +184,31 @@ module AbstractController
class TestCallbacks < ActiveSupport::TestCase
test "when a callback is modified in a child with :only, it works for the :only action" do
result = ChangedConditions.process(:index)
- assert_equal "Hello world", result.response_obj[:body]
+ assert_equal "Hello world", result.response_body
end
test "when a callback is modified in a child with :only, it does not work for other actions" do
result = ChangedConditions.process(:not_index)
- assert_equal "", result.response_obj[:body]
+ assert_equal "", result.response_body
+ end
+ end
+
+ class SetsResponseBody < ControllerWithCallbacks
+ before_filter :set_body
+
+ def index
+ self.response_body = "Fail"
+ end
+
+ def set_body
+ self.response_body = "Success"
+ end
+ end
+
+ class TestHalting < ActiveSupport::TestCase
+ test "when a callback sets the response body, the action should not be invoked" do
+ result = SetsResponseBody.process(:index)
+ assert_equal "Success", result.response_body
end
end
diff --git a/actionpack/test/abstract_controller/helper_test.rb b/actionpack/test/abstract_controller/helper_test.rb
index 6284fa4f70..f91aefe606 100644
--- a/actionpack/test/abstract_controller/helper_test.rb
+++ b/actionpack/test/abstract_controller/helper_test.rb
@@ -4,8 +4,8 @@ module AbstractController
module Testing
class ControllerWithHelpers < AbstractController::Base
- use Renderer
- use Helpers
+ include Renderer
+ include Helpers
def render(string)
super(:_template_name => string)
@@ -35,7 +35,7 @@ module AbstractController
class TestHelpers < ActiveSupport::TestCase
def test_helpers
result = MyHelpers1.process(:index)
- assert_equal "Hello World : Included", result.response_obj[:body]
+ assert_equal "Hello World : Included", result.response_body
end
end
diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb
index 3d4570bfef..d3440c3de0 100644
--- a/actionpack/test/abstract_controller/layouts_test.rb
+++ b/actionpack/test/abstract_controller/layouts_test.rb
@@ -1,14 +1,15 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+require 'active_support/core_ext/class/removal'
module AbstractControllerTests
module Layouts
# Base controller for these tests
class Base < AbstractController::Base
- use AbstractController::Renderer
- use AbstractController::Layouts
-
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ include AbstractController::Renderer
+ include AbstractController::Layouts
+
+ self.view_paths = [ActionView::Template::FixturePath.new(
"layouts/hello.erb" => "With String <%= yield %>",
"layouts/hello_override.erb" => "With Override <%= yield %>",
"layouts/abstract_controller_tests/layouts/with_string_implied_child.erb" =>
@@ -152,12 +153,12 @@ module AbstractControllerTests
class TestBase < ActiveSupport::TestCase
test "when no layout is specified, and no default is available, render without a layout" do
result = Blank.process(:index)
- assert_equal "Hello blank!", result.response_obj[:body]
+ assert_equal "Hello blank!", result.response_body
end
test "when layout is specified as a string, render with that layout" do
result = WithString.process(:index)
- assert_equal "With String Hello string!", result.response_obj[:body]
+ assert_equal "With String Hello string!", result.response_body
end
test "when layout is specified as a string, but the layout is missing, raise an exception" do
@@ -166,22 +167,22 @@ module AbstractControllerTests
test "when layout is specified as false, do not use a layout" do
result = WithFalseLayout.process(:index)
- assert_equal "Hello false!", result.response_obj[:body]
+ assert_equal "Hello false!", result.response_body
end
test "when layout is specified as nil, do not use a layout" do
result = WithNilLayout.process(:index)
- assert_equal "Hello nil!", result.response_obj[:body]
+ assert_equal "Hello nil!", result.response_body
end
test "when layout is specified as a symbol, call the requested method and use the layout returned" do
result = WithSymbol.process(:index)
- assert_equal "OMGHI2U Hello symbol!", result.response_obj[:body]
+ assert_equal "OMGHI2U Hello symbol!", result.response_body
end
test "when layout is specified as a symbol and the method returns nil, don't use a layout" do
result = WithSymbolReturningNil.process(:index)
- assert_equal "Hello nilz!", result.response_obj[:body]
+ assert_equal "Hello nilz!", result.response_body
end
test "when the layout is specified as a symbol and the method doesn't exist, raise an exception" do
@@ -194,28 +195,28 @@ module AbstractControllerTests
test "when a child controller does not have a layout, use the parent controller layout" do
result = WithStringChild.process(:index)
- assert_equal "With String Hello string!", result.response_obj[:body]
+ assert_equal "With String Hello string!", result.response_body
end
test "when a child controller has specified a layout, use that layout and not the parent controller layout" do
result = WithStringOverriddenChild.process(:index)
- assert_equal "With Override Hello string!", result.response_obj[:body]
+ assert_equal "With Override Hello string!", result.response_body
end
test "when a child controller has an implied layout, use that layout and not the parent controller layout" do
result = WithStringImpliedChild.process(:index)
- assert_equal "With Implied Hello string!", result.response_obj[:body]
+ assert_equal "With Implied Hello string!", result.response_body
end
test "when a child controller specifies layout nil, do not use the parent layout" do
result = WithNilChild.process(:index)
- assert_equal "Hello string!", result.response_obj[:body]
+ assert_equal "Hello string!", result.response_body
end
test "when a grandchild has no layout specified, the child has an implied layout, and the " \
"parent has specified a layout, use the child controller layout" do
result = WithChildOfImplied.process(:index)
- assert_equal "With Implied Hello string!", result.response_obj[:body]
+ assert_equal "With Implied Hello string!", result.response_body
end
test "raises an exception when specifying layout true" do
diff --git a/actionpack/test/abstract_controller/test_helper.rb b/actionpack/test/abstract_controller/test_helper.rb
index b9248c6bbd..915054f7fb 100644
--- a/actionpack/test/abstract_controller/test_helper.rb
+++ b/actionpack/test/abstract_controller/test_helper.rb
@@ -2,11 +2,14 @@ $:.unshift(File.dirname(__FILE__) + '/../../lib')
$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
$:.unshift(File.dirname(__FILE__) + '/../lib')
+require 'rubygems'
require 'test/unit'
require 'active_support'
require 'active_support/test_case'
-require 'action_controller'
+require 'action_controller/abstract'
+require 'action_view'
require 'action_view/base'
+require 'action_dispatch'
require 'fixture_template'
begin
@@ -16,10 +19,3 @@ begin
rescue LoadError
# Debugging disabled. `gem install ruby-debug` to enable.
end
-
-require 'action_controller/abstract'
-# require 'action_controller/abstract/base'
-# require 'action_controller/abstract/renderer'
-# require 'action_controller/abstract/layouts'
-# require 'action_controller/abstract/callbacks'
-# require 'action_controller/abstract/helpers' \ No newline at end of file
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index b07e6e5f3a..c71da7fa6c 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -1,3 +1,7 @@
+if ENV['new_base']
+ puts *caller
+ raise 'new_base/abstract_unit already loaded'
+end
$:.unshift(File.dirname(__FILE__) + '/../lib')
$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
@@ -23,6 +27,8 @@ require 'action_controller'
require 'action_controller/testing/process'
require 'action_view/test_case'
+$tags[:old_base] = true
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
@@ -34,7 +40,7 @@ ActionController::Base.session_store = nil
# Register danish language for testing
I18n.backend.store_translations 'da', {}
I18n.backend.store_translations 'pt-BR', {}
-ORIGINAL_LOCALES = I18n.available_locales.map(&:to_s).sort
+ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb
index 34f18806a2..9ac33b3417 100644
--- a/actionpack/test/activerecord/active_record_store_test.rb
+++ b/actionpack/test/activerecord/active_record_store_test.rb
@@ -13,6 +13,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
end
def set_session_value
+ raise "missing session!" unless session
session[:foo] = params[:foo] || "bar"
head :ok
end
@@ -21,15 +22,20 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
render :text => "foo: #{session[:foo].inspect}"
end
+ def set_cookie_and_get_session_value
+ cookies["kittens"] = { :value => "fluffy" }
+ render :text => "foo: #{session[:foo].inspect}"
+ end
+
def get_session_id
session[:foo]
render :text => "#{request.session_options[:id]}"
end
def call_reset_session
- session[:bar]
+ session[:foo]
reset_session
- session[:bar] = "baz"
+ session[:foo] = "baz"
head :ok
end
@@ -77,6 +83,23 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
end
end
+ def test_getting_session_value_does_not_set_cookie
+ with_test_route_set do
+ get '/get_session_value'
+ assert_response :success
+ assert_equal "", headers["Set-Cookie"]
+ end
+ end
+
+ def test_getting_session_value_and_setting_a_cookie_doesnt_delete_all_cookies
+ with_test_route_set do
+ get '/set_cookie_and_get_session_value'
+ assert_response :success
+ assert_equal 'foo: nil', response.body
+ assert_equal({"kittens" => "fluffy"}, response.cookies)
+ end
+ end
+
def test_setting_session_value_after_session_reset
with_test_route_set do
get '/set_session_value'
@@ -90,7 +113,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest
get '/get_session_value'
assert_response :success
- assert_equal 'foo: nil', response.body
+ assert_equal 'foo: "baz"', response.body
get '/get_session_id'
assert_response :success
diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb
index 35139fbb7f..b9f5be2361 100644
--- a/actionpack/test/activerecord/polymorphic_routes_test.rb
+++ b/actionpack/test/activerecord/polymorphic_routes_test.rb
@@ -1,4 +1,5 @@
require 'active_record_unit'
+require 'fixtures/project'
class Task < ActiveRecord::Base
set_table_name 'projects'
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 96f7a42c9b..24686ab4b6 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'action_controller/vendor/html-scanner'
# a controller class to facilitate the tests
class ActionPackAssertionsController < ActionController::Base
@@ -12,6 +13,9 @@ class ActionPackAssertionsController < ActionController::Base
# a standard template
def hello_xml_world() render :template => "test/hello_xml_world"; end
+ # a standard partial
+ def partial() render :partial => 'test/partial'; end
+
# a redirect to an internal location
def redirect_internal() redirect_to "/nothing"; end
@@ -292,47 +296,73 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
# make sure that the template objects exist
def test_template_objects_alive
process :assign_this
- assert !@response.has_template_object?('hi')
- assert @response.has_template_object?('howdy')
+ assert !@controller.template.instance_variable_get(:"@hi")
+ assert @controller.template.instance_variable_get(:"@howdy")
end
# make sure we don't have template objects when we shouldn't
def test_template_object_missing
process :nothing
- assert_nil @response.template_objects['howdy']
+ assert_nil @controller.template.assigns['howdy']
end
# check the empty flashing
def test_flash_me_naked
process :flash_me_naked
- assert !@response.has_flash?
- assert !@response.has_flash_with_contents?
+ assert_deprecated do
+ assert !@response.has_flash?
+ assert !@response.has_flash_with_contents?
+ end
end
# check if we have flash objects
def test_flash_haves
process :flash_me
- assert @response.has_flash?
- assert @response.has_flash_with_contents?
- assert @response.has_flash_object?('hello')
+ assert_deprecated do
+ assert @response.has_flash?
+ assert @response.has_flash_with_contents?
+ assert @response.has_flash_object?('hello')
+ end
end
# ensure we don't have flash objects
def test_flash_have_nots
process :nothing
- assert !@response.has_flash?
- assert !@response.has_flash_with_contents?
- assert_nil @response.flash['hello']
+ assert_deprecated do
+ assert !@response.has_flash?
+ assert !@response.has_flash_with_contents?
+ assert_nil @response.flash['hello']
+ end
+ end
+
+ def test_assert_template_with_partial
+ get :partial
+ assert_template :partial => '_partial'
+ end
+
+ def test_assert_template_with_nil
+ get :nothing
+ assert_template nil
+ end
+
+ def test_assert_template_with_string
+ get :hello_world
+ assert_template 'hello_world'
+ end
+
+ def test_assert_template_with_symbol
+ get :hello_world
+ assert_template :hello_world
end
# check if we were rendered by a file-based template?
def test_rendered_action
process :nothing
- assert_nil @response.rendered[:template]
+ assert_nil @controller.template.rendered[:template]
process :hello_world
- assert @response.rendered[:template]
- assert 'hello_world', @response.rendered[:template].to_s
+ assert @controller.template.rendered[:template]
+ assert 'hello_world', @controller.template.rendered[:template].to_s
end
# check the redirection location
@@ -378,10 +408,12 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
def test_redirect_url_match
process :redirect_external
assert @response.redirect?
- assert @response.redirect_url_match?("rubyonrails")
- assert @response.redirect_url_match?(/rubyonrails/)
- assert !@response.redirect_url_match?("phpoffrails")
- assert !@response.redirect_url_match?(/perloffrails/)
+ assert_deprecated do
+ assert @response.redirect_url_match?("rubyonrails")
+ assert @response.redirect_url_match?(/rubyonrails/)
+ assert !@response.redirect_url_match?("phpoffrails")
+ assert !@response.redirect_url_match?(/perloffrails/)
+ end
end
# check for a redirection
@@ -413,7 +445,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
assert_equal "Mr. David", @response.body
end
-
def test_assert_redirection_fails_with_incorrect_controller
process :redirect_to_controller
assert_raise(ActiveSupport::TestCase::Assertion) do
@@ -481,7 +512,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
get :index
assert_response :success
flunk 'Expected non-success response'
- rescue ActiveSupport::TestCase::Assertion => e
+ rescue RuntimeError => e
assert e.message.include?('FAIL')
end
@@ -506,9 +537,11 @@ class ActionPackHeaderTest < ActionController::TestCase
end
def test_rendering_xml_respects_content_type
- @response.headers['type'] = 'application/pdf'
- process :hello_xml_world
- assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type'])
+ pending do
+ @response.headers['type'] = 'application/pdf'
+ process :hello_xml_world
+ assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type'])
+ end
end
def test_render_text_with_custom_content_type
diff --git a/actionpack/test/controller/addresses_render_test.rb b/actionpack/test/controller/addresses_render_test.rb
index 2f092b6731..2d2a2745b0 100644
--- a/actionpack/test/controller/addresses_render_test.rb
+++ b/actionpack/test/controller/addresses_render_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'logger'
class Address
def Address.count(conditions = nil, join = nil)
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index f4517d06c4..3a4cdb81d9 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'logger'
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
# Provide some controller to run the tests on.
@@ -27,6 +28,7 @@ class EmptyController < ActionController::Base
end
class NonEmptyController < ActionController::Base
def public_action
+ render :nothing => true
end
hide_action :hidden_action
@@ -51,6 +53,7 @@ end
class DefaultUrlOptionsController < ActionController::Base
def default_url_options_action
+ render :nothing => true
end
def default_url_options(options = nil)
@@ -114,7 +117,7 @@ class PerformActionTest < ActionController::TestCase
end
def method_missing(method, *args)
- @logged << args.first
+ @logged << args.first.to_s
end
end
@@ -151,11 +154,8 @@ class PerformActionTest < ActionController::TestCase
def test_get_on_hidden_should_fail
use_controller NonEmptyController
- get :hidden_action
- assert_response 404
-
- get :another_hidden_action
- assert_response 404
+ assert_raise(ActionController::UnknownAction) { get :hidden_action }
+ assert_raise(ActionController::UnknownAction) { get :another_hidden_action }
end
def test_namespaced_action_should_log_module_name
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index b61a58dd09..c286976315 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -1,5 +1,6 @@
require 'fileutils'
require 'abstract_unit'
+require 'active_record_unit'
CACHE_DIR = 'test_cache'
# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
@@ -47,6 +48,8 @@ class PageCachingTest < ActionController::TestCase
super
ActionController::Base.perform_caching = true
+ ActionController::Routing::Routes.clear!
+
ActionController::Routing::Routes.draw do |map|
map.main '', :controller => 'posts'
map.formatted_posts 'posts.:format', :controller => 'posts'
@@ -110,7 +113,7 @@ class PageCachingTest < ActionController::TestCase
end
def test_should_cache_ok_at_custom_path
- @request.stubs(:path).returns("/index.html")
+ @request.request_uri = "/index.html"
get :ok
assert_response :ok
assert File.exist?("#{FILE_STORE_PATH}/index.html")
@@ -148,11 +151,15 @@ class PageCachingTest < ActionController::TestCase
end
class ActionCachingTestController < ActionController::Base
+ rescue_from(Exception) { head 500 }
+ rescue_from(ActiveRecord::RecordNotFound) { head :not_found }
+
caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
caches_action :show, :cache_path => 'http://test.host/custom/show'
caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
caches_action :with_layout
caches_action :layout_false, :layout => false
+ caches_action :record_not_found, :four_oh_four, :simple_runtime_error
layout 'talk_from_action.erb'
@@ -175,6 +182,18 @@ class ActionCachingTestController < ActionController::Base
render :text => @cache_this, :layout => true
end
+ def record_not_found
+ raise ActiveRecord::RecordNotFound, "oops!"
+ end
+
+ def four_oh_four
+ render :text => "404'd!", :status => 404
+ end
+
+ def simple_runtime_error
+ raise "oops!"
+ end
+
alias_method :show, :index
alias_method :edit, :index
alias_method :destroy, :index
@@ -345,7 +364,7 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
reset!
- @request.set_REQUEST_URI "/action_caching_test/expire.xml"
+ @request.request_uri = "/action_caching_test/expire.xml"
get :expire, :format => :xml
reset!
@@ -458,6 +477,27 @@ class ActionCacheTest < ActionController::TestCase
assert_response :success
end
+ def test_record_not_found_returns_404_for_multiple_requests
+ get :record_not_found
+ assert_response 404
+ get :record_not_found
+ assert_response 404
+ end
+
+ def test_four_oh_four_returns_404_for_multiple_requests
+ get :four_oh_four
+ assert_response 404
+ get :four_oh_four
+ assert_response 404
+ end
+
+ def test_simple_runtime_error_returns_500_for_multiple_requests
+ get :simple_runtime_error
+ assert_response 500
+ get :simple_runtime_error
+ assert_response 500
+ end
+
private
def content_to_cache
assigns(:cache_this)
diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb
index 9a0976ca9f..06a5af6b32 100644
--- a/actionpack/test/controller/capture_test.rb
+++ b/actionpack/test/controller/capture_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'logger'
class CaptureController < ActionController::Base
def self.controller_name; "test"; end
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index 7377546631..d622ac1e85 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -1,24 +1,29 @@
require 'abstract_unit'
class ContentTypeController < ActionController::Base
+ # :ported:
def render_content_type_from_body
response.content_type = Mime::RSS
render :text => "hello world!"
end
+ # :ported:
def render_defaults
render :text => "hello world!"
end
+ # :ported:
def render_content_type_from_render
render :text => "hello world!", :content_type => Mime::RSS
end
+ # :ported:
def render_charset_from_body
response.charset = "utf-16"
render :text => "hello world!"
end
+ # :ported:
def render_nil_charset_from_body
response.charset = nil
render :text => "hello world!"
@@ -60,6 +65,7 @@ class ContentTypeTest < ActionController::TestCase
@controller.logger = Logger.new(nil)
end
+ # :ported:
def test_render_defaults
get :render_defaults
assert_equal "utf-8", @response.charset
@@ -74,24 +80,28 @@ class ContentTypeTest < ActionController::TestCase
ContentTypeController.default_charset = "utf-8"
end
+ # :ported:
def test_content_type_from_body
get :render_content_type_from_body
assert_equal "application/rss+xml", @response.content_type
assert_equal "utf-8", @response.charset
end
+ # :ported:
def test_content_type_from_render
get :render_content_type_from_render
assert_equal "application/rss+xml", @response.content_type
assert_equal "utf-8", @response.charset
end
+ # :ported:
def test_charset_from_body
get :render_charset_from_body
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-16", @response.charset
end
+ # :ported:
def test_nil_charset_from_body
get :render_nil_charset_from_body
assert_equal Mime::HTML, @response.content_type
@@ -138,12 +148,13 @@ class AcceptBasedContentTypeTest < ActionController::TestCase
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 = false
+ ActionController::Base.use_accept_header = @_old_accept_header
end
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index c861982698..7199da3441 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -4,44 +4,48 @@ class CookieTest < ActionController::TestCase
class TestController < ActionController::Base
def authenticate
cookies["user_name"] = "david"
+ head :ok
end
def set_with_with_escapable_characters
cookies["that & guy"] = "foo & bar => baz"
+ head :ok
end
def authenticate_for_fourteen_days
cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
+ head :ok
end
def authenticate_for_fourteen_days_with_symbols
cookies[:user_name] = { :value => "david", :expires => Time.utc(2005, 10, 10,5) }
+ head :ok
end
def set_multiple_cookies
cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
cookies["login"] = "XJ-122"
+ head :ok
end
def access_frozen_cookies
cookies["will"] = "work"
+ head :ok
end
def logout
cookies.delete("user_name")
+ head :ok
end
def delete_cookie_with_path
cookies.delete("user_name", :path => '/beaten')
- render :text => "hello world"
+ head :ok
end
def authenticate_with_http_only
cookies["user_name"] = { :value => "david", :httponly => true }
- end
-
- def rescue_action(e)
- raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output
+ head :ok
end
end
@@ -54,39 +58,38 @@ class CookieTest < ActionController::TestCase
def test_setting_cookie
get :authenticate
- assert_equal ["user_name=david; path=/"], @response.headers["Set-Cookie"]
+ assert_cookie_header "user_name=david; path=/"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_with_escapable_characters
get :set_with_with_escapable_characters
- assert_equal ["that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/"], @response.headers["Set-Cookie"]
+ assert_cookie_header "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/"
assert_equal({"that & guy" => "foo & bar => baz"}, @response.cookies)
end
def test_setting_cookie_for_fourteen_days
get :authenticate_for_fourteen_days
- assert_equal ["user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_for_fourteen_days_with_symbols
get :authenticate_for_fourteen_days_with_symbols
- assert_equal ["user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_with_http_only
get :authenticate_with_http_only
- assert_equal ["user_name=david; path=/; HttpOnly"], @response.headers["Set-Cookie"]
+ assert_cookie_header "user_name=david; path=/; HttpOnly"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_multiple_cookies
get :set_multiple_cookies
assert_equal 2, @response.cookies.size
- assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", @response.headers["Set-Cookie"][0]
- assert_equal "login=XJ-122; path=/", @response.headers["Set-Cookie"][1]
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/"
assert_equal({"login" => "XJ-122", "user_name" => "david"}, @response.cookies)
end
@@ -96,7 +99,7 @@ class CookieTest < ActionController::TestCase
def test_expiring_cookie
get :logout
- assert_equal ["user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
assert_equal({"user_name" => nil}, @response.cookies)
end
@@ -117,6 +120,22 @@ class CookieTest < ActionController::TestCase
def test_delete_cookie_with_path
get :delete_cookie_with_path
- assert_equal ["user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ end
+
+ def test_cookies_persist_throughout_request
+ get :authenticate
+ cookies = @controller.send(:cookies)
+ assert_equal 'david', cookies['user_name']
end
+
+ private
+ def assert_cookie_header(expected)
+ header = @response.headers["Set-Cookie"]
+ if header.respond_to?(:to_str)
+ assert_equal expected, header
+ else
+ assert_equal expected.split("\n"), header
+ end
+ end
end
diff --git a/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb b/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
index dd69a63020..0c02afea36 100644
--- a/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
+++ b/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
@@ -15,12 +15,6 @@ class DeprecatedBaseMethodsTest < ActionController::TestCase
tests Target
- def test_log_error_silences_deprecation_warnings
- get :raises_name_error
- rescue => e
- assert_not_deprecated { @controller.send :log_error, e }
- end
-
if defined? Test::Unit::Error
def test_assertion_failed_error_silences_deprecation_warnings
get :raises_name_error
diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb
index 569d698a03..9fae1fcf63 100644
--- a/actionpack/test/controller/dispatcher_test.rb
+++ b/actionpack/test/controller/dispatcher_test.rb
@@ -6,20 +6,20 @@ class DispatcherTest < Test::Unit::TestCase
def setup
ENV['REQUEST_METHOD'] = 'GET'
- Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
- middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb"))
- middleware.instance_eval(File.read(middlewares))
- end
-
# Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
- Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+ ActionDispatch::Callbacks.instance_variable_set("@prepare_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+ ActionDispatch::Callbacks.instance_variable_set("@before_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+ ActionDispatch::Callbacks.instance_variable_set("@after_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
+ @old_router, Dispatcher.router = Dispatcher.router, mock()
+ Dispatcher.router.stubs(:call).returns([200, {}, 'response'])
+ Dispatcher.router.stubs(:reload)
Dispatcher.stubs(:require_dependency)
end
def teardown
+ Dispatcher.router = @old_router
+ @dispatcher = nil
ENV.delete 'REQUEST_METHOD'
end
@@ -29,12 +29,12 @@ class DispatcherTest < Test::Unit::TestCase
end
def test_reloads_routes_before_dispatch_if_in_loading_mode
- ActionController::Routing::Routes.expects(:reload).once
+ Dispatcher.router.expects(:reload).once
dispatch(false)
end
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
- ActionController::Routing::Routes.expects(:reload).never
+ Dispatcher.router.expects(:reload).never
ActiveSupport::Dependencies.expects(:clear).never
dispatch
@@ -45,19 +45,6 @@ class DispatcherTest < Test::Unit::TestCase
def log_failsafe_exception(status, exception); end
end
- def test_failsafe_response
- Dispatcher.any_instance.expects(:_call).raises('b00m')
- ActionDispatch::Failsafe.any_instance.expects(:log_failsafe_exception)
-
- assert_nothing_raised do
- assert_equal [
- 500,
- {"Content-Type" => "text/html"},
- "<html><body><h1>500 Internal Server Error</h1></body></html>"
- ], dispatch
- end
- end
-
def test_prepare_callbacks
a = b = c = nil
Dispatcher.to_prepare { |*args| a = b = c = 1 }
@@ -68,7 +55,7 @@ class DispatcherTest < Test::Unit::TestCase
assert_nil a || b || c
# Run callbacks
- Dispatcher.run_prepare_callbacks
+ dispatch
assert_equal 1, a
assert_equal 2, b
@@ -85,16 +72,22 @@ class DispatcherTest < Test::Unit::TestCase
Dispatcher.to_prepare(:unique_id) { |*args| a = b = 1 }
Dispatcher.to_prepare(:unique_id) { |*args| a = 2 }
- Dispatcher.run_prepare_callbacks
+ dispatch
assert_equal 2, a
assert_equal nil, b
end
private
def dispatch(cache_classes = true)
- ActionController::Routing::RouteSet.any_instance.stubs(:call).returns([200, {}, 'response'])
+ ActionController::Dispatcher.prepare_each_request = false
Dispatcher.define_dispatcher_callbacks(cache_classes)
- Dispatcher.new.call({'rack.input' => StringIO.new('')})
+ Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
+ middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb"))
+ middleware.instance_eval(File.read(middlewares))
+ end
+
+ @dispatcher ||= Dispatcher.new
+ @dispatcher.call({'rack.input' => StringIO.new(''), 'action_dispatch.show_exceptions' => false})
end
def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb
index 0b259a7980..8c9e4f81de 100644
--- a/actionpack/test/controller/filter_params_test.rb
+++ b/actionpack/test/controller/filter_params_test.rb
@@ -1,13 +1,30 @@
require 'abstract_unit'
class FilterParamController < ActionController::Base
+ def payment
+ head :ok
+ end
end
-class FilterParamTest < Test::Unit::TestCase
- def setup
- @controller = FilterParamController.new
+class FilterParamTest < ActionController::TestCase
+ tests FilterParamController
+
+ class MockLogger
+ attr_reader :logged
+ attr_accessor :level
+
+ def initialize
+ @level = Logger::DEBUG
+ end
+
+ def method_missing(method, *args)
+ @logged ||= []
+ @logged << args.first
+ end
end
+ setup :set_logger
+
def test_filter_parameters
assert FilterParamController.respond_to?(:filter_parameter_logging)
assert !@controller.respond_to?(:filter_parameters)
@@ -46,4 +63,26 @@ class FilterParamTest < Test::Unit::TestCase
assert !FilterParamController.action_methods.include?('filter_parameters')
assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) }
end
+
+ def test_filter_parameters_inside_logs
+ FilterParamController.filter_parameter_logging(:lifo, :amount)
+
+ get :payment, :lifo => 'Pratik', :amount => '420', :step => '1'
+
+ filtered_params_logs = logs.detect {|l| l =~ /\AParameters/ }
+
+ assert filtered_params_logs.index('"amount"=>"[FILTERED]"')
+ assert filtered_params_logs.index('"lifo"=>"[FILTERED]"')
+ assert filtered_params_logs.index('"step"=>"1"')
+ end
+
+ private
+
+ def set_logger
+ @controller.logger = MockLogger.new
+ end
+
+ def logs
+ @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
+ end
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index e83fde2349..b0b7274690 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -1,7 +1,37 @@
require 'abstract_unit'
+require 'active_support/core_ext/symbol'
+
+class ActionController::Base
+ class << self
+ %w(append_around_filter prepend_after_filter prepend_around_filter prepend_before_filter skip_after_filter skip_before_filter skip_filter).each do |pending|
+ define_method(pending) do |*args|
+ $stderr.puts "#{pending} unimplemented: #{args.inspect}"
+ end unless method_defined?(pending)
+ end
+
+ if defined?(ActionController::Http)
+ def before_filters
+ filters = _process_action_callbacks.select { |c| c.kind == :before }
+ filters.map! { |c| c.instance_variable_get(:@raw_filter) }
+ end
+ end
+ end
+
+ if defined?(ActionController::Http)
+ def assigns(key = nil)
+ assigns = {}
+ instance_variable_names.each do |ivar|
+ next if ActionController::Base.protected_instance_variables.include?(ivar)
+ assigns[ivar[1..-1]] = instance_variable_get(ivar)
+ end
+
+ key.nil? ? assigns : assigns[key.to_s]
+ end
+ end
+end
+
+class FilterTest < ActionController::TestCase
-# FIXME: crashes Ruby 1.9
-class FilterTest < Test::Unit::TestCase
class TestController < ActionController::Base
before_filter :ensure_login
after_filter :clean_up
@@ -139,14 +169,6 @@ class FilterTest < Test::Unit::TestCase
before_filter :clean_up_tmp, :if => Proc.new { |c| false }
end
- class EmptyFilterChainController < TestController
- self.filter_chain.clear
- def show
- @action_executed = true
- render :text => "yawp!"
- end
- end
-
class PrependingController < TestController
prepend_before_filter :wonderful_life
# skip_before_filter :fire_flash
@@ -165,16 +187,21 @@ class FilterTest < Test::Unit::TestCase
def index
render :text => 'ok'
end
-
+
def public
+ render :text => 'ok'
end
end
-
+
class SkippingAndReorderingController < TestController
skip_before_filter :ensure_login
before_filter :find_record
before_filter :ensure_login
+ def index
+ render :text => 'ok'
+ end
+
private
def find_record
@ran_filter ||= []
@@ -448,11 +475,6 @@ class FilterTest < Test::Unit::TestCase
assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
end
- def test_empty_filter_chain
- assert_equal 0, EmptyFilterChainController.filter_chain.size
- assert test_process(EmptyFilterChainController).template.assigns['action_executed']
- end
-
def test_added_filter_to_inheritance_graph
assert_equal [ :ensure_login ], TestController.before_filters
end
@@ -466,88 +488,109 @@ class FilterTest < Test::Unit::TestCase
end
def test_running_filters
- assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"]
+ test_process(PrependingController)
+ assert_equal %w( wonderful_life ensure_login ), assigns["ran_filter"]
end
def test_running_filters_with_proc
- assert test_process(ProcController).template.assigns["ran_proc_filter"]
+ test_process(ProcController)
+ assert assigns["ran_proc_filter"]
end
def test_running_filters_with_implicit_proc
- assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"]
+ test_process(ImplicitProcController)
+ assert assigns["ran_proc_filter"]
end
def test_running_filters_with_class
- assert test_process(AuditController).template.assigns["was_audited"]
+ test_process(AuditController)
+ assert assigns["was_audited"]
end
def test_running_anomolous_yet_valid_condition_filters
- response = test_process(AnomolousYetValidConditionController)
- assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
- assert response.template.assigns["ran_class_filter"]
- assert response.template.assigns["ran_proc_filter1"]
- assert response.template.assigns["ran_proc_filter2"]
+ test_process(AnomolousYetValidConditionController)
+ assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert assigns["ran_class_filter"]
+ assert assigns["ran_proc_filter1"]
+ assert assigns["ran_proc_filter2"]
- response = test_process(AnomolousYetValidConditionController, "show_without_filter")
- assert_equal nil, response.template.assigns["ran_filter"]
- assert !response.template.assigns["ran_class_filter"]
- assert !response.template.assigns["ran_proc_filter1"]
- assert !response.template.assigns["ran_proc_filter2"]
+ test_process(AnomolousYetValidConditionController, "show_without_filter")
+ assert_equal nil, assigns["ran_filter"]
+ assert !assigns["ran_class_filter"]
+ assert !assigns["ran_proc_filter1"]
+ assert !assigns["ran_proc_filter2"]
end
def test_running_conditional_options
- response = test_process(ConditionalOptionsFilter)
- assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
+ test_process(ConditionalOptionsFilter)
+ assert_equal %w( ensure_login ), assigns["ran_filter"]
end
def test_running_collection_condition_filters
- assert_equal %w( ensure_login ), test_process(ConditionalCollectionFilterController).template.assigns["ran_filter"]
- assert_equal nil, test_process(ConditionalCollectionFilterController, "show_without_filter").template.assigns["ran_filter"]
- assert_equal nil, test_process(ConditionalCollectionFilterController, "another_action").template.assigns["ran_filter"]
+ test_process(ConditionalCollectionFilterController)
+ assert_equal %w( ensure_login ), assigns["ran_filter"]
+ test_process(ConditionalCollectionFilterController, "show_without_filter")
+ assert_equal nil, assigns["ran_filter"]
+ test_process(ConditionalCollectionFilterController, "another_action")
+ assert_equal nil, assigns["ran_filter"]
end
def test_running_only_condition_filters
- assert_equal %w( ensure_login ), test_process(OnlyConditionSymController).template.assigns["ran_filter"]
- assert_equal nil, test_process(OnlyConditionSymController, "show_without_filter").template.assigns["ran_filter"]
+ test_process(OnlyConditionSymController)
+ assert_equal %w( ensure_login ), assigns["ran_filter"]
+ test_process(OnlyConditionSymController, "show_without_filter")
+ assert_equal nil, assigns["ran_filter"]
- assert test_process(OnlyConditionProcController).template.assigns["ran_proc_filter"]
- assert !test_process(OnlyConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
+ test_process(OnlyConditionProcController)
+ assert assigns["ran_proc_filter"]
+ test_process(OnlyConditionProcController, "show_without_filter")
+ assert !assigns["ran_proc_filter"]
- assert test_process(OnlyConditionClassController).template.assigns["ran_class_filter"]
- assert !test_process(OnlyConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
+ test_process(OnlyConditionClassController)
+ assert assigns["ran_class_filter"]
+ test_process(OnlyConditionClassController, "show_without_filter")
+ assert !assigns["ran_class_filter"]
end
def test_running_except_condition_filters
- assert_equal %w( ensure_login ), test_process(ExceptConditionSymController).template.assigns["ran_filter"]
- assert_equal nil, test_process(ExceptConditionSymController, "show_without_filter").template.assigns["ran_filter"]
+ test_process(ExceptConditionSymController)
+ assert_equal %w( ensure_login ), assigns["ran_filter"]
+ test_process(ExceptConditionSymController, "show_without_filter")
+ assert_equal nil, assigns["ran_filter"]
- assert test_process(ExceptConditionProcController).template.assigns["ran_proc_filter"]
- assert !test_process(ExceptConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
+ test_process(ExceptConditionProcController)
+ assert assigns["ran_proc_filter"]
+ test_process(ExceptConditionProcController, "show_without_filter")
+ assert !assigns["ran_proc_filter"]
- assert test_process(ExceptConditionClassController).template.assigns["ran_class_filter"]
- assert !test_process(ExceptConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
+ test_process(ExceptConditionClassController)
+ assert assigns["ran_class_filter"]
+ test_process(ExceptConditionClassController, "show_without_filter")
+ assert !assigns["ran_class_filter"]
end
def test_running_before_and_after_condition_filters
- assert_equal %w( ensure_login clean_up_tmp), test_process(BeforeAndAfterConditionController).template.assigns["ran_filter"]
- assert_equal nil, test_process(BeforeAndAfterConditionController, "show_without_filter").template.assigns["ran_filter"]
+ test_process(BeforeAndAfterConditionController)
+ assert_equal %w( ensure_login clean_up_tmp), assigns["ran_filter"]
+ test_process(BeforeAndAfterConditionController, "show_without_filter")
+ assert_equal nil, assigns["ran_filter"]
end
def test_around_filter
- controller = test_process(AroundFilterController)
- assert controller.template.assigns["before_ran"]
- assert controller.template.assigns["after_ran"]
+ test_process(AroundFilterController)
+ assert assigns["before_ran"]
+ assert assigns["after_ran"]
end
def test_before_after_class_filter
- controller = test_process(BeforeAfterClassFilterController)
- assert controller.template.assigns["before_ran"]
- assert controller.template.assigns["after_ran"]
+ test_process(BeforeAfterClassFilterController)
+ assert assigns["before_ran"]
+ assert assigns["after_ran"]
end
def test_having_properties_in_around_filter
- controller = test_process(AroundFilterController)
- assert_equal "before and after", controller.template.assigns["execution_log"]
+ test_process(AroundFilterController)
+ assert_equal "before and after", assigns["execution_log"]
end
def test_prepending_and_appending_around_filter
@@ -560,7 +603,7 @@ class FilterTest < Test::Unit::TestCase
def test_rendering_breaks_filtering_chain
response = test_process(RenderingController)
assert_equal "something else", response.body
- assert !response.template.assigns["ran_action"]
+ assert !assigns["ran_action"]
end
def test_filters_with_mixed_specialization_run_in_order
@@ -579,47 +622,59 @@ class FilterTest < Test::Unit::TestCase
%w(foo bar baz).each do |action|
request = ActionController::TestRequest.new
request.query_parameters[:choose] = action
- response = DynamicDispatchController.process(request, ActionController::TestResponse.new)
+ response = DynamicDispatchController.action.call(request.env).last
assert_equal action, response.body
end
end
def test_running_prepended_before_and_after_filter
- assert_equal 3, PrependingBeforeAndAfterController.filter_chain.length
- response = test_process(PrependingBeforeAndAfterController)
- assert_equal %w( before_all between_before_all_and_after_all after_all ), response.template.assigns["ran_filter"]
+ test_process(PrependingBeforeAndAfterController)
+ assert_equal %w( before_all between_before_all_and_after_all after_all ), assigns["ran_filter"]
end
-
+
def test_skipping_and_limiting_controller
- assert_equal %w( ensure_login ), test_process(SkippingAndLimitedController, "index").template.assigns["ran_filter"]
- assert_nil test_process(SkippingAndLimitedController, "public").template.assigns["ran_filter"]
+ test_process(SkippingAndLimitedController, "index")
+ assert_equal %w( ensure_login ), assigns["ran_filter"]
+ test_process(SkippingAndLimitedController, "public")
+ assert_nil assigns["ran_filter"]
end
def test_skipping_and_reordering_controller
- assert_equal %w( find_record ensure_login ), test_process(SkippingAndReorderingController, "index").template.assigns["ran_filter"]
+ test_process(SkippingAndReorderingController, "index")
+ assert_equal %w( find_record ensure_login ), assigns["ran_filter"]
end
def test_conditional_skipping_of_filters
- assert_nil test_process(ConditionalSkippingController, "login").template.assigns["ran_filter"]
- assert_equal %w( ensure_login find_user ), test_process(ConditionalSkippingController, "change_password").template.assigns["ran_filter"]
+ test_process(ConditionalSkippingController, "login")
+ assert_nil assigns["ran_filter"]
+ test_process(ConditionalSkippingController, "change_password")
+ assert_equal %w( ensure_login find_user ), assigns["ran_filter"]
- assert_nil test_process(ConditionalSkippingController, "login").template.controller.instance_variable_get("@ran_after_filter")
- assert_equal %w( clean_up ), test_process(ConditionalSkippingController, "change_password").template.controller.instance_variable_get("@ran_after_filter")
+ test_process(ConditionalSkippingController, "login")
+ assert_nil @controller.template.controller.instance_variable_get("@ran_after_filter")
+ test_process(ConditionalSkippingController, "change_password")
+ assert_equal %w( clean_up ), @controller.template.controller.instance_variable_get("@ran_after_filter")
end
def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
- assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
- assert_nil test_process(ChildOfConditionalParentController, 'another_action').template.assigns['ran_filter']
+ test_process(ChildOfConditionalParentController)
+ assert_equal %w( conditional_in_parent conditional_in_parent ), assigns['ran_filter']
+ test_process(ChildOfConditionalParentController, 'another_action')
+ assert_nil assigns['ran_filter']
end
def test_condition_skipping_of_filters_when_siblings_also_have_conditions
- assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter'], "1"
- assert_equal nil, test_process(AnotherChildOfConditionalParentController).template.assigns['ran_filter']
- assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
+ test_process(ChildOfConditionalParentController)
+ assert_equal %w( conditional_in_parent conditional_in_parent ), assigns['ran_filter'], "1"
+ test_process(AnotherChildOfConditionalParentController)
+ assert_equal nil, assigns['ran_filter']
+ test_process(ChildOfConditionalParentController)
+ assert_equal %w( conditional_in_parent conditional_in_parent ), assigns['ran_filter']
end
def test_changing_the_requirements
- assert_equal nil, test_process(ChangingTheRequirementsController, "go_wild").template.assigns['ran_filter']
+ test_process(ChangingTheRequirementsController, "go_wild")
+ assert_equal nil, assigns['ran_filter']
end
def test_a_rescuing_around_filter
@@ -634,11 +689,11 @@ class FilterTest < Test::Unit::TestCase
private
def test_process(controller, action = "show")
- ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
- request = ActionController::TestRequest.new
- request.action = action
- controller = controller.new if controller.is_a?(Class)
- controller.process_with_test(request, ActionController::TestResponse.new)
+ @controller = controller.is_a?(Class) ? controller.new : controller
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ process(action)
end
end
@@ -772,18 +827,9 @@ class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
skip_filter :after
end
-class YieldingAroundFiltersTest < Test::Unit::TestCase
+class YieldingAroundFiltersTest < ActionController::TestCase
include PostsController::AroundExceptions
- def test_filters_registering
- assert_equal 1, ControllerWithFilterMethod.filter_chain.size
- assert_equal 1, ControllerWithFilterClass.filter_chain.size
- assert_equal 1, ControllerWithFilterInstance.filter_chain.size
- assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
- assert_equal 6, ControllerWithNestedFilters.filter_chain.size
- assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
- end
-
def test_base
controller = PostsController
assert_nothing_raised { test_process(controller,'no_raise') }
@@ -819,9 +865,9 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
end
def test_with_proc
- controller = test_process(ControllerWithProcFilter,'no_raise')
- assert controller.template.assigns['before']
- assert controller.template.assigns['after']
+ test_process(ControllerWithProcFilter,'no_raise')
+ assert assigns['before']
+ assert assigns['after']
end
def test_nested_filters
@@ -841,13 +887,13 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
end
def test_filter_order_with_all_filter_types
- controller = test_process(ControllerWithAllTypesOfFilters,'no_raise')
- assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after',controller.template.assigns['ran_filter'].join(' ')
+ test_process(ControllerWithAllTypesOfFilters,'no_raise')
+ assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after', assigns['ran_filter'].join(' ')
end
def test_filter_order_with_skip_filter_method
- controller = test_process(ControllerWithTwoLessFilters,'no_raise')
- assert_equal 'before around (before yield) around (after yield)',controller.template.assigns['ran_filter'].join(' ')
+ test_process(ControllerWithTwoLessFilters,'no_raise')
+ assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ')
end
def test_first_filter_in_multiple_before_filter_chain_halts
@@ -876,10 +922,7 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
protected
def test_process(controller, action = "show")
- ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
- request = ActionController::TestRequest.new
- request.action = action
- controller = controller.new if controller.is_a?(Class)
- controller.process_with_test(request, ActionController::TestResponse.new)
+ @controller = controller.is_a?(Class) ? controller.new : controller
+ process(action)
end
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index d8a892811e..c448f36cb3 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -60,6 +60,7 @@ class FlashTest < ActionController::TestCase
def std_action
@flash_copy = {}.update(flash)
+ render :nothing => true
end
def filter_halting_action
@@ -79,64 +80,84 @@ class FlashTest < ActionController::TestCase
get :set_flash
get :use_flash
- assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
- assert_equal "hello", @response.template.assigns["flashy"]
+ assert_equal "hello", assigns["flash_copy"]["that"]
+ assert_equal "hello", assigns["flashy"]
get :use_flash
- assert_nil @response.template.assigns["flash_copy"]["that"], "On second flash"
+ assert_nil assigns["flash_copy"]["that"], "On second flash"
end
def test_keep_flash
get :set_flash
get :use_flash_and_keep_it
- assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
- assert_equal "hello", @response.template.assigns["flashy"]
+ assert_equal "hello", assigns["flash_copy"]["that"]
+ assert_equal "hello", assigns["flashy"]
get :use_flash
- assert_equal "hello", @response.template.assigns["flash_copy"]["that"], "On second flash"
+ assert_equal "hello", assigns["flash_copy"]["that"], "On second flash"
get :use_flash
- assert_nil @response.template.assigns["flash_copy"]["that"], "On third flash"
+ assert_nil assigns["flash_copy"]["that"], "On third flash"
end
def test_flash_now
get :set_flash_now
- assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
- assert_equal "bar" , @response.template.assigns["flash_copy"]["foo"]
- assert_equal "hello", @response.template.assigns["flashy"]
+ assert_equal "hello", assigns["flash_copy"]["that"]
+ assert_equal "bar" , assigns["flash_copy"]["foo"]
+ assert_equal "hello", assigns["flashy"]
get :attempt_to_use_flash_now
- assert_nil @response.template.assigns["flash_copy"]["that"]
- assert_nil @response.template.assigns["flash_copy"]["foo"]
- assert_nil @response.template.assigns["flashy"]
+ assert_nil assigns["flash_copy"]["that"]
+ assert_nil assigns["flash_copy"]["foo"]
+ assert_nil assigns["flashy"]
end
def test_update_flash
get :set_flash
get :use_flash_and_update_it
- assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
- assert_equal "hello again", @response.template.assigns["flash_copy"]["this"]
+ assert_equal "hello", assigns["flash_copy"]["that"]
+ assert_equal "hello again", assigns["flash_copy"]["this"]
get :use_flash
- assert_nil @response.template.assigns["flash_copy"]["that"], "On second flash"
- assert_equal "hello again", @response.template.assigns["flash_copy"]["this"], "On second flash"
+ assert_nil assigns["flash_copy"]["that"], "On second flash"
+ assert_equal "hello again", assigns["flash_copy"]["this"], "On second flash"
end
-
+
def test_flash_after_reset_session
get :use_flash_after_reset_session
- assert_equal "hello", @response.template.assigns["flashy_that"]
- assert_equal "good-bye", @response.template.assigns["flashy_this"]
- assert_nil @response.template.assigns["flashy_that_reset"]
+ assert_equal "hello", assigns["flashy_that"]
+ assert_equal "good-bye", assigns["flashy_this"]
+ assert_nil assigns["flashy_that_reset"]
end
+ def test_does_not_set_the_session_if_the_flash_is_empty
+ get :std_action
+ assert_nil session["flash"]
+ end
+
def test_sweep_after_halted_filter_chain
get :std_action
- assert_nil @response.template.assigns["flash_copy"]["foo"]
+ assert_nil assigns["flash_copy"]["foo"]
get :filter_halting_action
- assert_equal "bar", @response.template.assigns["flash_copy"]["foo"]
+ assert_equal "bar", assigns["flash_copy"]["foo"]
get :std_action # follow redirection
- assert_equal "bar", @response.template.assigns["flash_copy"]["foo"]
+ assert_equal "bar", assigns["flash_copy"]["foo"]
get :std_action
- assert_nil @response.template.assigns["flash_copy"]["foo"]
+ assert_nil assigns["flash_copy"]["foo"]
+ end
+
+ def test_keep_and_discard_return_values
+ flash = ActionController::Flash::FlashHash.new
+ flash.update(:foo => :foo_indeed, :bar => :bar_indeed)
+
+ assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed
+ assert_nil flash.discard(:unknown) # non existant key passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard()) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil)) # nothing passed
+
+ assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed
+ assert_nil flash.keep(:unknown) # non existant key passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep()) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed
end
end
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index 5f36461b89..5b9feb3630 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/kernel/reporting'
ActionController::Base.helpers_dir = File.dirname(__FILE__) + '/../fixtures/helpers'
@@ -26,7 +27,7 @@ module Fun
end
end
-class ApplicationController < ActionController::Base
+class AllHelpersController < ActionController::Base
helper :all
end
@@ -101,24 +102,32 @@ class HelperTest < Test::Unit::TestCase
assert master_helper_methods.include?('delegate_attr=')
end
- def test_helper_for_nested_controller
+ def call_controller(klass, action)
request = ActionController::TestRequest.new
- response = ActionController::TestResponse.new
- request.action = 'render_hello_world'
+ klass.action(action).call(request.env)
+ end
- assert_equal 'hello: Iz guuut!', Fun::GamesController.process(request, response).body
+ def test_helper_for_nested_controller
+ assert_equal 'hello: Iz guuut!',
+ call_controller(Fun::GamesController, "render_hello_world").last.body
+ # request = ActionController::TestRequest.new
+ #
+ # resp = Fun::GamesController.action(:render_hello_world).call(request.env)
+ # assert_equal 'hello: Iz guuut!', resp.last.body
end
def test_helper_for_acronym_controller
- request = ActionController::TestRequest.new
- response = ActionController::TestResponse.new
- request.action = 'test'
-
- assert_equal 'test: baz', Fun::PdfController.process(request, response).body
+ assert_equal "test: baz", call_controller(Fun::PdfController, "test").last.body
+ #
+ # request = ActionController::TestRequest.new
+ # response = ActionController::TestResponse.new
+ # request.action = 'test'
+ #
+ # assert_equal 'test: baz', Fun::PdfController.process(request, response).body
end
def test_all_helpers
- methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s)
+ methods = AllHelpersController.master_helper_module.instance_methods.map(&:to_s)
# abc_helper.rb
assert methods.include?('bare_a')
@@ -145,7 +154,7 @@ class HelperTest < Test::Unit::TestCase
end
def test_helper_proxy
- methods = ApplicationController.helpers.methods.map(&:to_s)
+ methods = AllHelpersController.helpers.methods.map(&:to_s)
# ActionView
assert methods.include?('pluralize')
@@ -204,6 +213,11 @@ class IsolatedHelpersTest < Test::Unit::TestCase
end
end
+ def call_controller(klass, action)
+ request = ActionController::TestRequest.new
+ klass.action(action).call(request.env)
+ end
+
def setup
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@@ -211,14 +225,14 @@ class IsolatedHelpersTest < Test::Unit::TestCase
end
def test_helper_in_a
- assert_raise(NameError) { A.process(@request, @response) }
+ assert_raise(ActionView::TemplateError) { call_controller(A, "index") }
end
def test_helper_in_b
- assert_equal 'B', B.process(@request, @response).body
+ assert_equal 'B', call_controller(B, "index").last.body
end
def test_helper_in_c
- assert_equal 'C', C.process(@request, @response).body
+ assert_equal 'C', call_controller(C, "index").last.body
end
end
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index 00789eea38..15a11395bb 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -38,6 +38,15 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
tests DummyDigestController
+ 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"
+ end
+
+ teardown do
+ ActionController::Base.session_options[:secret] = @old_secret
+ end
+
AUTH_HEADERS.each do |header|
test "successful authentication with #{header.downcase}" do
@request.env[header] = encode_credentials(:username => 'lifo', :password => 'world')
@@ -111,8 +120,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "authentication request with valid credential and nil session" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
- # session_id = "" in functional test, but is +nil+ in real life
- @request.session.session_id = nil
get :display
assert_response :success
@@ -151,25 +158,38 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
assert_equal 'Definitely Maybe', @response.body
end
+ test "authentication request with _method" do
+ @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please', :method => :post)
+ @request.env['rack.methodoverride.original_method'] = 'POST'
+ put :display
+
+ assert_response :success
+ assert assigns(:logged_in)
+ assert_equal 'Definitely Maybe', @response.body
+ end
+
private
def encode_credentials(options)
options.reverse_merge!(:nc => "00000001", :cnonce => "0a4f113b", :password_is_ha1 => false)
password = options.delete(:password)
- # Set in /initializers/session_store.rb. Used as secret in generating nonce
- # to prevent tampering of timestamp
- ActionController::Base.session_options[:secret] = "session_options_secret"
+ # Perform unauthenticated request to retrieve digest parameters to use on subsequent request
+ method = options.delete(:method) || 'GET'
- # Perform unauthenticated GET to retrieve digest parameters to use on subsequent request
- get :index
+ case method.to_s.upcase
+ when 'GET'
+ get :index
+ when 'POST'
+ post :index
+ end
assert_response :unauthorized
credentials = decode_credentials(@response.headers['WWW-Authenticate'])
credentials.merge!(options)
credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}")
- ActionController::HttpAuthentication::Digest.encode_credentials("GET", credentials, password, options[:password_is_ha1])
+ ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
end
def decode_credentials(header)
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index e39a934c24..197ba0c69c 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'action_controller/vendor/html-scanner'
class SessionTest < Test::Unit::TestCase
StubApp = lambda { |env|
@@ -30,7 +31,7 @@ class SessionTest < Test::Unit::TestCase
def test_request_via_redirect_uses_given_method
path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
- @session.expects(:put).with(path, args, headers)
+ @session.expects(:process).with(:put, path, args, headers)
@session.stubs(:redirect?).returns(false)
@session.request_via_redirect(:put, path, args, headers)
end
@@ -90,16 +91,6 @@ class SessionTest < Test::Unit::TestCase
assert_equal '/show', @session.url_for(options)
end
- def test_redirect_bool_with_status_in_300s
- @session.stubs(:status).returns 301
- assert @session.redirect?
- end
-
- def test_redirect_bool_with_status_in_200s
- @session.stubs(:status).returns 200
- assert !@session.redirect?
- end
-
def test_get
path = "/index"; params = "blah"; headers = {:location => 'blah'}
@session.expects(:process).with(:get,path,params,headers)
@@ -133,8 +124,8 @@ class SessionTest < Test::Unit::TestCase
def test_xml_http_request_get
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
- "X-Requested-With" => "XMLHttpRequest",
- "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:get,path,params,headers_after_xhr)
@session.xml_http_request(:get,path,params,headers)
@@ -143,8 +134,8 @@ class SessionTest < Test::Unit::TestCase
def test_xml_http_request_post
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
- "X-Requested-With" => "XMLHttpRequest",
- "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:post,path,params,headers_after_xhr)
@session.xml_http_request(:post,path,params,headers)
@@ -153,8 +144,8 @@ class SessionTest < Test::Unit::TestCase
def test_xml_http_request_put
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
- "X-Requested-With" => "XMLHttpRequest",
- "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:put,path,params,headers_after_xhr)
@session.xml_http_request(:put,path,params,headers)
@@ -163,8 +154,8 @@ class SessionTest < Test::Unit::TestCase
def test_xml_http_request_delete
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
- "X-Requested-With" => "XMLHttpRequest",
- "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:delete,path,params,headers_after_xhr)
@session.xml_http_request(:delete,path,params,headers)
@@ -173,17 +164,17 @@ class SessionTest < Test::Unit::TestCase
def test_xml_http_request_head
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
- "X-Requested-With" => "XMLHttpRequest",
- "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
+ "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:head,path,params,headers_after_xhr)
@session.xml_http_request(:head,path,params,headers)
end
def test_xml_http_request_override_accept
- path = "/index"; params = "blah"; headers = {:location => 'blah', "Accept" => "application/xml"}
+ path = "/index"; params = "blah"; headers = {:location => 'blah', "HTTP_ACCEPT" => "application/xml"}
headers_after_xhr = headers.merge(
- "X-Requested-With" => "XMLHttpRequest"
+ "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"
)
@session.expects(:process).with(:post,path,params,headers_after_xhr)
@session.xml_http_request(:post,path,params,headers)
@@ -263,7 +254,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest
assert_response 200
assert_response :success
assert_response :ok
- assert_equal({}, cookies)
+ assert_equal({}, cookies.to_hash)
assert_equal "OK", body
assert_equal "OK", response.body
assert_kind_of HTML::Document, html_document
@@ -279,7 +270,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest
assert_response 201
assert_response :success
assert_response :created
- assert_equal({}, cookies)
+ assert_equal({}, cookies.to_hash)
assert_equal "Created", body
assert_equal "Created", response.body
assert_kind_of HTML::Document, html_document
@@ -297,7 +288,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest
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)
+ assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies.to_hash)
assert_equal "Gone", response.body
end
end
@@ -337,7 +328,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest
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 "", request.env["QUERY_STRING"]
+ assert_equal "foo=bar", request.env["QUERY_STRING"]
assert_equal 'foo=bar', request.query_string
assert_equal 'bar', request.parameters['foo']
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index f2721e274d..cb9bdf57bb 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -9,8 +9,11 @@ ActionView::Template::register_template_handler :mab,
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ]
+require "fixture_template"
+
class LayoutTest < ActionController::Base
def self.controller_path; 'views' end
+ def self._implied_layout_name; to_s.underscore.gsub(/_controller$/, '') ; end
self.view_paths = ActionController::Base.view_paths.dup
end
@@ -35,6 +38,15 @@ end
class MultipleExtensions < LayoutTest
end
+if defined?(ActionController::Http)
+ LayoutTest._write_layout_method
+ ProductController._write_layout_method
+ ItemController._write_layout_method
+ ThirdPartyTemplateLibraryController._write_layout_method
+ MultipleExtensions._write_layout_method
+ ControllerNameSpace::NestedController._write_layout_method
+end
+
class LayoutAutoDiscoveryTest < ActionController::TestCase
def setup
super
@@ -56,23 +68,19 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_third_party_template_library_auto_discovers_layout
@controller = ThirdPartyTemplateLibraryController.new
get :hello
- assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout(true).to_s
- assert_equal 'layouts/third_party_template_library', @response.layout
assert_response :success
- assert_equal 'Mab', @response.body
+ assert_equal 'layouts/third_party_template_library.mab', @response.body
end
- def test_namespaced_controllers_auto_detect_layouts
+ def test_namespaced_controllers_auto_detect_layouts1
@controller = ControllerNameSpace::NestedController.new
get :hello
- assert_equal 'layouts/controller_name_space/nested', @controller.active_layout(true).to_s
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end
- def test_namespaced_controllers_auto_detect_layouts
+ def test_namespaced_controllers_auto_detect_layouts2
@controller = MultipleExtensions.new
get :hello
- assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout(true).to_s
assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
end
end
@@ -116,70 +124,75 @@ class RendersNoLayoutController < LayoutTest
end
class LayoutSetInResponseTest < ActionController::TestCase
+ include ActionView::TemplateHandlers
+
def test_layout_set_when_using_default_layout
@controller = DefaultLayoutController.new
get :hello
- assert_equal 'layouts/layout_test', @response.layout
+ assert @controller.template.layout.include?('layouts/layout_test')
end
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
- assert_equal 'layouts/item', @response.layout
+ assert @controller.template.layout.include?('layouts/item')
end
def test_layout_only_exception_when_included
@controller = OnlyLayoutController.new
get :hello
- assert_equal 'layouts/item', @response.layout
+ assert @controller.template.layout.include?('layouts/item')
end
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
- assert_equal nil, @response.layout
+ assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
end
def test_layout_except_exception_when_included
@controller = ExceptLayoutController.new
get :hello
- assert_equal 'layouts/item', @response.layout
+ assert @controller.template.layout.include?('layouts/item')
end
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
- assert_equal nil, @response.layout
+ assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
end
def test_layout_set_when_using_render
@controller = SetsLayoutInRenderController.new
get :hello
- assert_equal 'layouts/third_party_template_library', @response.layout
+ assert @controller.template.layout.include?('layouts/third_party_template_library')
end
def test_layout_is_not_set_when_none_rendered
@controller = RendersNoLayoutController.new
get :hello
- assert_nil @response.layout
+ assert_nil @controller.template.layout
end
- def test_exempt_from_layout_honored_by_render_template
- ActionController::Base.exempt_from_layout :rhtml
- @controller = RenderWithTemplateOptionController.new
+ for_tag(:old_base) do
+ # exempt_from_layout is deprecated
+ def test_exempt_from_layout_honored_by_render_template
+ ActionController::Base.exempt_from_layout :erb
+ @controller = RenderWithTemplateOptionController.new
- get :hello
- assert_equal "alt/hello.rhtml", @response.body.strip
+ get :hello
+ assert_equal "alt/hello.rhtml", @response.body.strip
- ensure
- ActionController::Base.exempt_from_layout.delete(/\.rhtml$/)
+ ensure
+ ActionController::Base.exempt_from_layout.delete(ERB)
+ end
end
def test_layout_is_picked_from_the_controller_instances_view_path
pending do
@controller = PrependsViewPathController.new
get :hello
- assert_equal 'layouts/alt', @response.layout
+ assert_equal 'layouts/alt', @controller.template.layout
end
end
@@ -203,8 +216,7 @@ end
class LayoutExceptionRaised < ActionController::TestCase
def test_exception_raised_when_layout_file_not_found
@controller = SetsNonExistentLayoutFile.new
- get :hello
- assert_kind_of ActionView::MissingTemplate, @response.template.instance_eval { @exception }
+ assert_raise(ActionView::MissingTemplate) { get :hello }
end
end
@@ -232,7 +244,7 @@ unless RUBY_PLATFORM =~ /(:?mswin|mingw|bccwin)/
@controller = LayoutSymlinkedTest.new
get :hello
assert_response 200
- assert_equal "layouts/symlinked/symlinked_layout", @response.layout
+ assert @controller.template.layout.include?("layouts/symlinked/symlinked_layout")
end
end
end
diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb
index 1f3ff4ef52..a7ed1b8665 100644
--- a/actionpack/test/controller/logging_test.rb
+++ b/actionpack/test/controller/logging_test.rb
@@ -11,6 +11,11 @@ class LoggingTest < ActionController::TestCase
class MockLogger
attr_reader :logged
+ attr_accessor :level
+
+ def initialize
+ @level = Logger::DEBUG
+ end
def method_missing(method, *args)
@logged ||= []
@@ -30,7 +35,7 @@ class LoggingTest < ActionController::TestCase
end
def test_logging_with_parameters
- get :show, :id => 10
+ get :show, :id => '10'
assert_equal 3, logs.size
params = logs.detect {|l| l =~ /Parameters/ }
@@ -44,6 +49,6 @@ class LoggingTest < ActionController::TestCase
end
def logs
- @logs ||= @controller.logger.logged.compact.map {|l| l.strip}
+ @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
end
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 7cd5145a2f..56b49251c6 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -375,9 +375,11 @@ class MimeControllerTest < ActionController::TestCase
end
def test_rjs_type_skips_layout
- @request.accept = "text/javascript"
- get :all_types_with_layout
- assert_equal 'RJS for all_types_with_layout', @response.body
+ pending(:new_base) do
+ @request.accept = "text/javascript"
+ get :all_types_with_layout
+ assert_equal 'RJS for all_types_with_layout', @response.body
+ end
end
def test_html_type_with_layout
@@ -437,7 +439,7 @@ class MimeControllerTest < ActionController::TestCase
@controller.instance_eval do
def render(*args)
unless args.empty?
- @action = args.first[:action]
+ @action = args.first[:action] || action_name
end
response.body = "#{@action} - #{@template.formats}"
end
@@ -490,14 +492,15 @@ class PostController < AbstractPostController
end
end
- protected
- def with_iphone
- Mime::Type.register_alias("text/html", :iphone)
- request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
- yield
- ensure
- Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
- end
+protected
+
+ def with_iphone
+ Mime::Type.register_alias("text/html", :iphone)
+ request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
+ yield
+ ensure
+ Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
+ end
end
class SuperPostController < PostController
@@ -509,6 +512,11 @@ class SuperPostController < PostController
end
end
+if defined?(ActionController::Http)
+ PostController._write_layout_method
+ SuperPostController._write_layout_method
+end
+
class MimeControllerLayoutsTest < ActionController::TestCase
tests PostController
@@ -526,14 +534,16 @@ class MimeControllerLayoutsTest < ActionController::TestCase
assert_equal 'Hello iPhone', @response.body
end
- def test_format_with_inherited_layouts
- @controller = SuperPostController.new
+ for_tag(:old_base) do
+ def test_format_with_inherited_layouts
+ @controller = SuperPostController.new
- get :index
- assert_equal 'Super Firefox', @response.body
+ get :index
+ assert_equal 'Super Firefox', @response.body
- @request.accept = "text/iphone"
- get :index
- assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
+ @request.accept = "text/iphone"
+ get :index
+ assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
+ end
end
end
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 91e21db854..13247f2d08 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -233,11 +233,6 @@ class RedirectTest < ActionController::TestCase
assert_redirected_to Workshop.new(5, true)
end
- def test_redirect_with_partial_params
- get :module_redirect
- assert_redirected_to :action => 'hello_world'
- end
-
def test_redirect_to_nil
assert_raise(ActionController::ActionControllerError) do
get :redirect_to_nil
diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb
new file mode 100644
index 0000000000..d02fd3fd4c
--- /dev/null
+++ b/actionpack/test/controller/render_js_test.rb
@@ -0,0 +1,39 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+require 'pathname'
+
+class TestController < ActionController::Base
+ protect_from_forgery
+
+ def render_vanilla_js_hello
+ render :js => "alert('hello')"
+ end
+
+ def greeting
+ # let's just rely on the template
+ end
+
+ def partial
+ render :partial => 'partial'
+ end
+end
+
+class RenderTest < ActionController::TestCase
+ tests TestController
+
+ def test_render_vanilla_js
+ get :render_vanilla_js_hello
+ assert_equal "alert('hello')", @response.body
+ assert_equal "text/javascript", @response.content_type
+ end
+
+ def test_render_with_default_from_accept_header
+ xhr :get, :greeting
+ assert_equal "$(\"body\").visualEffect(\"highlight\");", @response.body
+ end
+
+ def test_should_render_js_partial
+ xhr :get, :partial, :format => 'js'
+ assert_equal 'partial js', @response.body
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
new file mode 100644
index 0000000000..233b2dfd89
--- /dev/null
+++ b/actionpack/test/controller/render_json_test.rb
@@ -0,0 +1,80 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+require 'pathname'
+
+class TestController < ActionController::Base
+ protect_from_forgery
+
+ def render_json_nil
+ render :json => nil
+ end
+
+ def render_json_hello_world
+ render :json => ActiveSupport::JSON.encode(:hello => 'world')
+ end
+
+ def render_json_hello_world_with_callback
+ render :json => ActiveSupport::JSON.encode(:hello => 'world'), :callback => 'alert'
+ end
+
+ def render_json_with_custom_content_type
+ render :json => ActiveSupport::JSON.encode(:hello => 'world'), :content_type => 'text/javascript'
+ end
+
+ def render_symbol_json
+ render :json => ActiveSupport::JSON.encode(:hello => 'world')
+ end
+
+ def render_json_with_render_to_string
+ render :json => {:hello => render_to_string(:partial => 'partial')}
+ end
+end
+
+class RenderTest < ActionController::TestCase
+ tests TestController
+
+ def setup
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ super
+ @controller.logger = Logger.new(nil)
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_render_json_nil
+ get :render_json_nil
+ assert_equal 'null', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_render_json
+ get :render_json_hello_world
+ assert_equal '{"hello":"world"}', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_render_json_with_callback
+ get :render_json_hello_world_with_callback
+ assert_equal 'alert({"hello":"world"})', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_render_json_with_custom_content_type
+ get :render_json_with_custom_content_type
+ assert_equal '{"hello":"world"}', @response.body
+ assert_equal 'text/javascript', @response.content_type
+ end
+
+ def test_render_symbol_json
+ get :render_symbol_json
+ assert_equal '{"hello":"world"}', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_render_json_with_render_to_string
+ get :render_json_with_render_to_string
+ assert_equal '{"hello":"partial html"}', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb
new file mode 100644
index 0000000000..05645e47fa
--- /dev/null
+++ b/actionpack/test/controller/render_other_test.rb
@@ -0,0 +1,238 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+require 'pathname'
+
+class TestController < ActionController::Base
+ protect_from_forgery
+ layout :determine_layout
+
+ module RenderTestHelper
+ def rjs_helper_method_from_module
+ page.visual_effect :highlight
+ end
+ end
+
+ helper RenderTestHelper
+ helper do
+ def rjs_helper_method(value)
+ page.visual_effect :highlight, value
+ end
+ end
+
+ def enum_rjs_test
+ render :update do |page|
+ page.select('.product').each do |value|
+ page.rjs_helper_method_from_module
+ page.rjs_helper_method(value)
+ page.sortable(value, :url => { :action => "order" })
+ page.draggable(value)
+ end
+ end
+ end
+
+ def render_explicit_html_template
+ end
+
+ def render_custom_code_rjs
+ render :update, :status => 404 do |page|
+ page.replace :foo, :partial => 'partial'
+ end
+ end
+
+ def render_implicit_html_template
+ end
+
+ def render_js_with_explicit_template
+ @project_id = 4
+ render :template => 'test/delete_with_js'
+ end
+
+ def render_js_with_explicit_action_template
+ @project_id = 4
+ render :action => 'delete_with_js'
+ end
+
+ def delete_with_js
+ @project_id = 4
+ end
+
+ def update_page
+ render :update do |page|
+ page.replace_html 'balance', '$37,000,000.00'
+ page.visual_effect :highlight, 'balance'
+ end
+ end
+
+ def update_page_with_instance_variables
+ @money = '$37,000,000.00'
+ @div_id = 'balance'
+ render :update do |page|
+ page.replace_html @div_id, @money
+ page.visual_effect :highlight, @div_id
+ end
+ end
+
+ def update_page_with_view_method
+ render :update do |page|
+ page.replace_html 'person', pluralize(2, 'person')
+ end
+ end
+
+ def partial_as_rjs
+ render :update do |page|
+ page.replace :foo, :partial => 'partial'
+ end
+ end
+
+ def respond_to_partial_as_rjs
+ respond_to do |format|
+ format.js do
+ render :update do |page|
+ page.replace :foo, :partial => 'partial'
+ end
+ end
+ end
+ end
+
+ def render_alternate_default
+ # For this test, the method "default_render" is overridden:
+ @alternate_default_render = lambda do
+ render :update do |page|
+ page.replace :foo, :partial => 'partial'
+ end
+ end
+ end
+
+private
+ def default_render
+ if @alternate_default_render
+ @alternate_default_render.call
+ else
+ super
+ end
+ end
+
+ def determine_layout
+ case action_name
+ when "hello_world", "layout_test", "rendering_without_layout",
+ "rendering_nothing_on_layout", "render_text_hello_world",
+ "render_text_hello_world_with_layout",
+ "hello_world_with_layout_false",
+ "partial_only", "partial_only_with_layout",
+ "accessing_params_in_template",
+ "accessing_params_in_template_with_layout",
+ "render_with_explicit_template",
+ "render_with_explicit_string_template",
+ "update_page", "update_page_with_instance_variables"
+
+ "layouts/standard"
+ when "action_talk_to_layout", "layout_overriding_layout"
+ "layouts/talk_from_action"
+ when "render_implicit_html_template_from_xhr_request"
+ (request.xhr? ? 'layouts/xhr' : 'layouts/standard')
+ end
+ end
+end
+
+class RenderTest < ActionController::TestCase
+ tests TestController
+
+ def setup
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ super
+ @controller.logger = Logger.new(nil)
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_enum_rjs_test
+ ActiveSupport::SecureRandom.stubs(:base64).returns("asdf")
+ get :enum_rjs_test
+ body = %{
+ $$(".product").each(function(value, index) {
+ new Effect.Highlight(element,{});
+ new Effect.Highlight(value,{});
+ Sortable.create(value, {onUpdate:function(){new Ajax.Request('/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value) + '&authenticity_token=' + encodeURIComponent('asdf')})}});
+ new Draggable(value, {});
+ });
+ }.gsub(/^ /, '').strip
+ assert_equal body, @response.body
+ end
+
+ def test_explicitly_rendering_an_html_template_with_implicit_html_template_renders_should_be_possible_from_an_rjs_template
+ [:js, "js"].each do |format|
+ assert_nothing_raised do
+ get :render_explicit_html_template, :format => format
+ assert_equal %(document.write("Hello world\\n");), @response.body
+ end
+ end
+ end
+
+ def test_render_custom_code_rjs
+ get :render_custom_code_rjs
+ assert_response 404
+ assert_equal %(Element.replace("foo", "partial html");), @response.body
+ end
+
+ def test_render_in_an_rjs_template_should_pick_html_templates_when_available
+ [:js, "js"].each do |format|
+ assert_nothing_raised do
+ get :render_implicit_html_template, :format => format
+ assert_equal %(document.write("Hello world\\n");), @response.body
+ end
+ end
+ end
+
+ def test_render_rjs_template_explicitly
+ get :render_js_with_explicit_template
+ assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+ end
+
+ def test_rendering_rjs_action_explicitly
+ get :render_js_with_explicit_action_template
+ assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+ end
+
+ def test_render_rjs_with_default
+ get :delete_with_js
+ assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+ end
+
+ def test_update_page
+ get :update_page
+ assert_template nil
+ assert_equal 'text/javascript; charset=utf-8', @response.headers['Content-Type']
+ assert_equal 2, @response.body.split($/).length
+ end
+
+ def test_update_page_with_instance_variables
+ get :update_page_with_instance_variables
+ assert_template nil
+ assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
+ assert_match /balance/, @response.body
+ assert_match /\$37/, @response.body
+ end
+
+ def test_update_page_with_view_method
+ get :update_page_with_view_method
+ assert_template nil
+ assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
+ assert_match /2 people/, @response.body
+ end
+
+ def test_should_render_html_formatted_partial_with_rjs
+ xhr :get, :partial_as_rjs
+ assert_equal %(Element.replace("foo", "partial html");), @response.body
+ end
+
+ def test_should_render_html_formatted_partial_with_rjs_and_js_format
+ xhr :get, :respond_to_partial_as_rjs
+ assert_equal %(Element.replace("foo", "partial html");), @response.body
+ end
+
+ def test_should_render_with_alternate_default_render
+ xhr :get, :render_alternate_default
+ assert_equal %(Element.replace("foo", "partial html");), @response.body
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 9a34bcebe6..9e42d1738a 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1,8 +1,10 @@
require 'abstract_unit'
require 'controller/fake_models'
+require 'pathname'
module Fun
class GamesController < ActionController::Base
+ # :ported:
def hello_world
end
end
@@ -79,6 +81,7 @@ class TestController < ActionController::Base
fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ])
end
+ # :ported:
def render_hello_world
render :template => "test/hello_world"
end
@@ -93,23 +96,28 @@ class TestController < ActionController::Base
render :template => "test/hello_world"
end
+ # :ported: compatibility
def render_hello_world_with_forward_slash
render :template => "/test/hello_world"
end
+ # :ported:
def render_template_in_top_directory
render :template => 'shared'
end
+ # :deprecated:
def render_template_in_top_directory_with_slash
render :template => '/shared'
end
+ # :ported:
def render_hello_world_from_variable
@person = "david"
render :text => "hello #{@person}"
end
+ # :ported:
def render_action_hello_world
render :action => "hello_world"
end
@@ -122,10 +130,12 @@ class TestController < ActionController::Base
render :action => :hello_world
end
+ # :ported:
def render_text_hello_world
render :text => "hello world"
end
+ # :ported:
def render_text_hello_world_with_layout
@variable_for_layout = ", I'm here!"
render :text => "hello world", :layout => true
@@ -135,18 +145,21 @@ class TestController < ActionController::Base
render :layout => false
end
+ # :ported:
def render_file_with_instance_variables
@secret = 'in the sauce'
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb')
render :file => path
end
+ # :ported:
def render_file_as_string_with_instance_variables
@secret = 'in the sauce'
path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb'))
render path
end
+ # :ported:
def render_file_not_using_full_path
@secret = 'in the sauce'
render :file => 'test/render_file_with_ivar'
@@ -193,52 +206,30 @@ class TestController < ActionController::Base
render :inline => "<%= controller_name %>"
end
- def render_json_hello_world
- render :json => {:hello => 'world'}.to_json
- end
-
- def render_json_hello_world_with_callback
- render :json => {:hello => 'world'}.to_json, :callback => 'alert'
- end
-
- def render_json_with_custom_content_type
- render :json => {:hello => 'world'}.to_json, :content_type => 'text/javascript'
- end
-
- def render_symbol_json
- render :json => {:hello => 'world'}.to_json
- end
-
- def render_json_with_render_to_string
- render :json => {:hello => render_to_string(:partial => 'partial')}
- end
-
+ # :ported:
def render_custom_code
render :text => "hello world", :status => 404
end
- def render_custom_code_rjs
- render :update, :status => 404 do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
-
+ # :ported:
def render_text_with_nil
render :text => nil
end
+ # :ported:
def render_text_with_false
render :text => false
end
+ # :ported:
def render_nothing_with_appendix
render :text => "appended"
end
- def render_vanilla_js_hello
- render :js => "alert('hello')"
- end
-
+ # This test is testing 3 things:
+ # render :file in AV :ported:
+ # render :template in AC :ported:
+ # setting content type
def render_xml_hello
@name = "David"
render :template => "test/hello"
@@ -249,10 +240,6 @@ class TestController < ActionController::Base
render "test/hello"
end
- def render_xml_with_custom_content_type
- render :xml => "<blah/>", :content_type => "application/atomsvc+xml"
- end
-
def render_line_offset
render :inline => '<% raise %>', :locals => {:foo => 'bar'}
end
@@ -265,22 +252,27 @@ class TestController < ActionController::Base
# let's just rely on the template
end
+ # :ported:
def blank_response
render :text => ' '
end
+ # :ported:
def layout_test
render :action => "hello_world"
end
+ # :ported:
def builder_layout_test
render :action => "hello", :layout => "layouts/builder"
end
-
+
+ # :move: test this in ActionView
def builder_partial_test
render :action => "hello_world_container"
end
+ # :ported:
def partials_list
@test_unchanged = 'hello'
@customers = [ Customer.new("david"), Customer.new("mary") ]
@@ -306,12 +298,6 @@ class TestController < ActionController::Base
:locals => { :local_name => name }
end
- def render_implicit_html_template
- end
-
- def render_explicit_html_template
- end
-
def render_implicit_html_template_from_xhr_request
end
@@ -386,6 +372,7 @@ class TestController < ActionController::Base
render :layout => true, :inline => "Hello: <%= params[:name] %>"
end
+ # :ported:
def render_with_explicit_template
render :template => "test/hello_world"
end
@@ -394,10 +381,12 @@ class TestController < ActionController::Base
render "test/hello_world"
end
+ # :ported:
def render_with_explicit_template_with_locals
render :template => "test/render_file_with_locals", :locals => { :secret => 'area51' }
end
+ # :ported:
def double_render
render :text => "hello"
render :text => "world"
@@ -429,78 +418,24 @@ class TestController < ActionController::Base
render :action => "potential_conflicts"
end
+ # :deprecated:
+ # Tests being able to pick a .builder template over a .erb
+ # For instance, being able to have hello.xml.builder and hello.xml.erb
+ # and select one via "hello.builder" or "hello.erb"
def hello_world_from_rxml_using_action
render :action => "hello_world_from_rxml.builder"
end
+ # :deprecated:
def hello_world_from_rxml_using_template
render :template => "test/hello_world_from_rxml.builder"
end
- module RenderTestHelper
- def rjs_helper_method_from_module
- page.visual_effect :highlight
- end
- end
-
- helper RenderTestHelper
- helper do
- def rjs_helper_method(value)
- page.visual_effect :highlight, value
- end
- end
-
- def enum_rjs_test
- render :update do |page|
- page.select('.product').each do |value|
- page.rjs_helper_method_from_module
- page.rjs_helper_method(value)
- page.sortable(value, :url => { :action => "order" })
- page.draggable(value)
- end
- end
- end
-
- def delete_with_js
- @project_id = 4
- end
-
- def render_js_with_explicit_template
- @project_id = 4
- render :template => 'test/delete_with_js'
- end
-
- def render_js_with_explicit_action_template
- @project_id = 4
- render :action => 'delete_with_js'
- end
-
- def update_page
- render :update do |page|
- page.replace_html 'balance', '$37,000,000.00'
- page.visual_effect :highlight, 'balance'
- end
- end
-
- def update_page_with_instance_variables
- @money = '$37,000,000.00'
- @div_id = 'balance'
- render :update do |page|
- page.replace_html @div_id, @money
- page.visual_effect :highlight, @div_id
- end
- end
-
- def update_page_with_view_method
- render :update do |page|
- page.replace_html 'person', pluralize(2, 'person')
- end
- end
-
def action_talk_to_layout
# Action template sets variable that's picked up by layout
end
+ # :addressed:
def render_text_with_assigns
@hello = "world"
render :text => "foo"
@@ -539,25 +474,6 @@ class TestController < ActionController::Base
head :forbidden, :x_custom_header => "something"
end
- def render_with_location
- render :xml => "<hello/>", :location => "http://example.com", :status => 201
- end
-
- def render_with_object_location
- customer = Customer.new("Some guy", 1)
- render :xml => "<customer/>", :location => customer_url(customer), :status => :created
- end
-
- def render_with_to_xml
- to_xmlable = Class.new do
- def to_xml
- "<i-am-xml/>"
- end
- end.new
-
- render :xml => to_xmlable
- end
-
def render_using_layout_around_block
render :action => "using_layout_around_block"
end
@@ -574,22 +490,6 @@ class TestController < ActionController::Base
render :partial => 'partial.html.erb'
end
- def partial_as_rjs
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
-
- def respond_to_partial_as_rjs
- respond_to do |format|
- format.js do
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
- end
- end
-
def partial
render :partial => 'partial'
end
@@ -725,9 +625,7 @@ class TestController < ActionController::Base
"accessing_params_in_template_with_layout",
"render_with_explicit_template",
"render_with_explicit_string_template",
- "render_js_with_explicit_template",
- "render_js_with_explicit_action_template",
- "delete_with_js", "update_page", "update_page_with_instance_variables"
+ "update_page", "update_page_with_instance_variables"
"layouts/standard"
when "action_talk_to_layout", "layout_overriding_layout"
@@ -750,6 +648,7 @@ class RenderTest < ActionController::TestCase
@request.host = "www.nextangle.com"
end
+ # :ported:
def test_simple_show
get :hello_world
assert_response 200
@@ -758,11 +657,13 @@ class RenderTest < ActionController::TestCase
assert_equal "<html>Hello world!</html>", @response.body
end
+ # :ported:
def test_renders_default_template_for_missing_action
get :'hyphen-ated'
assert_template 'test/hyphen-ated'
end
+ # :ported:
def test_render
get :render_hello_world
assert_template "test/hello_world"
@@ -772,7 +673,7 @@ class RenderTest < ActionController::TestCase
begin
get :render_line_offset
flunk "the action should have raised an exception"
- rescue RuntimeError => exc
+ rescue StandardError => exc
line = exc.backtrace.first
assert(line =~ %r{:(\d+):})
assert_equal "1", $1,
@@ -780,129 +681,118 @@ class RenderTest < ActionController::TestCase
end
end
+ # :ported: compatibility
def test_render_with_forward_slash
get :render_hello_world_with_forward_slash
assert_template "test/hello_world"
end
+ # :ported:
def test_render_in_top_directory
get :render_template_in_top_directory
assert_template "shared"
assert_equal "Elastica", @response.body
end
+ # :ported:
def test_render_in_top_directory_with_slash
get :render_template_in_top_directory_with_slash
assert_template "shared"
assert_equal "Elastica", @response.body
end
+ # :ported:
def test_render_from_variable
get :render_hello_world_from_variable
assert_equal "hello david", @response.body
end
+ # :ported:
def test_render_action
get :render_action_hello_world
assert_template "test/hello_world"
end
+ # :ported:
def test_render_action_hello_world_as_string
get :render_action_hello_world_as_string
assert_equal "Hello world!", @response.body
assert_template "test/hello_world"
end
+ # :ported:
def test_render_action_with_symbol
get :render_action_hello_world_with_symbol
assert_template "test/hello_world"
end
+ # :ported:
def test_render_text
get :render_text_hello_world
assert_equal "hello world", @response.body
end
+ # :ported:
def test_do_with_render_text_and_layout
get :render_text_hello_world_with_layout
assert_equal "<html>hello world, I'm here!</html>", @response.body
end
+ # :ported:
def test_do_with_render_action_and_layout_false
get :hello_world_with_layout_false
assert_equal 'Hello world!', @response.body
end
+ # :ported:
def test_render_file_with_instance_variables
get :render_file_with_instance_variables
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :ported:
def test_render_file_as_string_with_instance_variables
get :render_file_as_string_with_instance_variables
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :ported:
def test_render_file_not_using_full_path
get :render_file_not_using_full_path
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :ported:
def test_render_file_not_using_full_path_with_dot_in_path
get :render_file_not_using_full_path_with_dot_in_path
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :ported:
def test_render_file_using_pathname
get :render_file_using_pathname
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :ported:
def test_render_file_with_locals
get :render_file_with_locals
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :ported:
def test_render_file_as_string_with_locals
get :render_file_as_string_with_locals
assert_equal "The secret is in the sauce\n", @response.body
end
+ # :assessed:
def test_render_file_from_template
get :render_file_from_template
assert_equal "The secret is in the sauce\n", @response.body
end
- def test_render_json
- get :render_json_hello_world
- assert_equal '{"hello": "world"}', @response.body
- assert_equal 'application/json', @response.content_type
- end
-
- def test_render_json_with_callback
- get :render_json_hello_world_with_callback
- assert_equal 'alert({"hello": "world"})', @response.body
- assert_equal 'application/json', @response.content_type
- end
-
- def test_render_json_with_custom_content_type
- get :render_json_with_custom_content_type
- assert_equal '{"hello": "world"}', @response.body
- assert_equal 'text/javascript', @response.content_type
- end
-
- def test_render_symbol_json
- get :render_symbol_json
- assert_equal '{"hello": "world"}', @response.body
- assert_equal 'application/json', @response.content_type
- end
-
- def test_render_json_with_render_to_string
- get :render_json_with_render_to_string
- assert_equal '{"hello": "partial html"}', @response.body
- assert_equal 'application/json', @response.content_type
- end
-
+ # :ported:
def test_render_custom_code
get :render_custom_code
assert_response 404
@@ -910,37 +800,37 @@ class RenderTest < ActionController::TestCase
assert_equal 'hello world', @response.body
end
- def test_render_custom_code_rjs
- get :render_custom_code_rjs
- assert_response 404
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
+ # :ported:
def test_render_text_with_nil
get :render_text_with_nil
assert_response 200
assert_equal ' ', @response.body
end
+ # :ported:
def test_render_text_with_false
get :render_text_with_false
assert_equal 'false', @response.body
end
+ # :ported:
def test_render_nothing_with_appendix
get :render_nothing_with_appendix
assert_response 200
assert_equal 'appended', @response.body
end
+ # :ported:
def test_attempt_to_access_object_method
assert_raise(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
end
+ # :ported:
def test_private_methods
assert_raise(ActionController::UnknownAction, "No action responded to [determine_layout]") { get :determine_layout }
end
+ # :ported:
def test_access_to_request_in_view
get :accessing_request_in_template
assert_equal "Hello: www.nextangle.com", @response.body
@@ -951,58 +841,45 @@ class RenderTest < ActionController::TestCase
assert_equal "Logger", @response.body
end
+ # :ported:
def test_access_to_action_name_in_view
get :accessing_action_name_in_template
assert_equal "accessing_action_name_in_template", @response.body
end
+ # :ported:
def test_access_to_controller_name_in_view
get :accessing_controller_name_in_template
assert_equal "test", @response.body # name is explicitly set to 'test' inside the controller.
end
- def test_render_vanilla_js
- get :render_vanilla_js_hello
- assert_equal "alert('hello')", @response.body
- assert_equal "text/javascript", @response.content_type
- end
-
+ # :ported:
def test_render_xml
get :render_xml_hello
assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
assert_equal "application/xml", @response.content_type
end
+ # :ported:
def test_render_xml_as_string_template
get :render_xml_hello_as_string_template
assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
assert_equal "application/xml", @response.content_type
end
+ # :ported:
def test_render_xml_with_default
get :greeting
assert_equal "<p>This is grand!</p>\n", @response.body
end
+ # :move: test in AV
def test_render_xml_with_partial
get :builder_partial_test
assert_equal "<test>\n <hello/>\n</test>\n", @response.body
end
- def test_enum_rjs_test
- ActiveSupport::SecureRandom.stubs(:base64).returns("asdf")
- get :enum_rjs_test
- body = %{
- $$(".product").each(function(value, index) {
- new Effect.Highlight(element,{});
- new Effect.Highlight(value,{});
- Sortable.create(value, {onUpdate:function(){new Ajax.Request('/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value) + '&authenticity_token=' + encodeURIComponent('asdf')})}});
- new Draggable(value, {});
- });
- }.gsub(/^ /, '').strip
- assert_equal body, @response.body
- end
-
+ # :ported:
def test_layout_rendering
get :layout_test
assert_equal "<html>Hello world!</html>", @response.body
@@ -1033,6 +910,7 @@ class RenderTest < ActionController::TestCase
assert_template "test/hello_world"
end
+ # :ported:
def test_nested_rendering
@controller = Fun::GamesController.new
get :hello_world
@@ -1049,29 +927,10 @@ class RenderTest < ActionController::TestCase
assert_equal "Goodbye, Local David", @response.body
end
- def test_render_in_an_rjs_template_should_pick_html_templates_when_available
- [:js, "js"].each do |format|
- assert_nothing_raised do
- get :render_implicit_html_template, :format => format
- assert_equal %(document.write("Hello world\\n");), @response.body
- end
- end
- end
-
- def test_explicitly_rendering_an_html_template_with_implicit_html_template_renders_should_be_possible_from_an_rjs_template
- [:js, "js"].each do |format|
- assert_nothing_raised do
- get :render_explicit_html_template, :format => format
- assert_equal %(document.write("Hello world\\n");), @response.body
- end
- end
- end
-
def test_should_implicitly_render_html_template_from_xhr_request
- pending do
- xhr :get, :render_implicit_html_template_from_xhr_request
- assert_equal "XHR!\nHello HTML!", @response.body
- end
+ pending
+ # xhr :get, :render_implicit_html_template_from_xhr_request
+ # assert_equal "XHR!\nHello HTML!", @response.body
end
def test_should_implicitly_render_js_template_without_layout
@@ -1086,11 +945,6 @@ class RenderTest < ActionController::TestCase
assert_equal 'formatted html erb', @response.body
end
- def test_should_render_formatted_xml_erb_template
- get :formatted_xml_erb, :format => :xml
- assert_equal '<test>passed formatted xml erb</test>', @response.body
- end
-
def test_should_render_formatted_html_erb_template
get :formatted_xml_erb
assert_equal '<test>passed formatted html erb</test>', @response.body
@@ -1102,31 +956,6 @@ class RenderTest < ActionController::TestCase
assert_equal '<test>passed formatted html erb</test>', @response.body
end
- def test_should_render_xml_but_keep_custom_content_type
- get :render_xml_with_custom_content_type
- assert_equal "application/atomsvc+xml", @response.content_type
- end
-
- def test_render_with_default_from_accept_header
- xhr :get, :greeting
- assert_equal "$(\"body\").visualEffect(\"highlight\");", @response.body
- end
-
- def test_render_rjs_with_default
- get :delete_with_js
- assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
- end
-
- def test_render_rjs_template_explicitly
- get :render_js_with_explicit_template
- assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
- end
-
- def test_rendering_rjs_action_explicitly
- get :render_js_with_explicit_action_template
- assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
- end
-
def test_layout_test_with_different_layout
get :layout_test_with_different_layout
assert_equal "<html>Hello world!</html>", @response.body
@@ -1193,6 +1022,7 @@ class RenderTest < ActionController::TestCase
assert_equal "<html>Hello world!</html>", @response.body
end
+ # :ported:
def test_double_render
assert_raise(ActionController::DoubleRenderError) { get :double_render }
end
@@ -1221,38 +1051,18 @@ class RenderTest < ActionController::TestCase
assert_equal "<title>Talking to the layout</title>\nAction was here!", @response.body
end
+ # :addressed:
def test_render_text_with_assigns
get :render_text_with_assigns
assert_equal "world", assigns["hello"]
end
+ # :ported:
def test_template_with_locals
get :render_with_explicit_template_with_locals
assert_equal "The secret is area51\n", @response.body
end
- def test_update_page
- get :update_page
- assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers['Content-Type']
- assert_equal 2, @response.body.split($/).length
- end
-
- def test_update_page_with_instance_variables
- get :update_page_with_instance_variables
- assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
- assert_match /balance/, @response.body
- assert_match /\$37/, @response.body
- end
-
- def test_update_page_with_view_method
- get :update_page_with_view_method
- assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
- assert_match /2 people/, @response.body
- end
-
def test_yield_content_for
assert_not_deprecated { get :yield_content_for }
assert_equal "<title>Putting stuff in the title!</title>\n\nGreat stuff!\n", @response.body
@@ -1282,15 +1092,15 @@ class RenderTest < ActionController::TestCase
def test_head_with_symbolic_status
get :head_with_symbolic_status, :status => "ok"
- assert_equal "200 OK", @response.status
+ assert_equal 200, @response.status
assert_response :ok
get :head_with_symbolic_status, :status => "not_found"
- assert_equal "404 Not Found", @response.status
+ assert_equal 404, @response.status
assert_response :not_found
get :head_with_symbolic_status, :status => "no_content"
- assert_equal "204 No Content", @response.status
+ assert_equal 204, @response.status
assert !@response.headers.include?('Content-Length')
assert_response :no_content
@@ -1311,7 +1121,7 @@ class RenderTest < ActionController::TestCase
def test_head_with_string_status
get :head_with_string_status, :status => "404 Eat Dirt"
assert_equal 404, @response.response_code
- assert_equal "Eat Dirt", @response.message
+ assert_equal "Not Found", @response.message
assert_response :not_found
end
@@ -1323,31 +1133,6 @@ class RenderTest < ActionController::TestCase
assert_response :forbidden
end
- def test_rendering_with_location_should_set_header
- get :render_with_location
- assert_equal "http://example.com", @response.headers["Location"]
- end
-
- def test_rendering_xml_should_call_to_xml_if_possible
- get :render_with_to_xml
- assert_equal "<i-am-xml/>", @response.body
- end
-
- def test_rendering_with_object_location_should_set_header_with_url_for
- ActionController::Routing::Routes.draw do |map|
- map.resources :customers
- map.connect ':controller/:action/:id'
- end
-
- get :render_with_object_location
- assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"]
- end
-
- def test_should_use_implicit_content_type
- get :implicit_content_type, :format => 'atom'
- assert_equal Mime::ATOM, @response.content_type
- end
-
def test_using_layout_around_block
get :render_using_layout_around_block
assert_equal "Before (David)\nInside from block\nAfter", @response.body
@@ -1378,26 +1163,6 @@ class RenderTest < ActionController::TestCase
assert_equal 'partial html', @response.body
end
- def test_should_render_html_formatted_partial_with_rjs
- xhr :get, :partial_as_rjs
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
- def test_should_render_html_formatted_partial_with_rjs_and_js_format
- xhr :get, :respond_to_partial_as_rjs
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
- def test_should_render_js_partial
- xhr :get, :partial, :format => 'js'
- assert_equal 'partial js', @response.body
- end
-
- def test_should_render_with_alternate_default_render
- xhr :get, :render_alternate_default
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
def test_partial_only_with_layout
get :partial_only_with_layout
assert_equal "<html>only partial</html>", @response.body
@@ -1579,7 +1344,7 @@ class EtagRenderTest < ActionController::TestCase
def test_render_against_etag_request_should_304_when_match
@request.if_none_match = etag_for("hello david")
get :render_hello_world_from_variable
- assert_equal "304 Not Modified", @response.status
+ assert_equal 304, @response.status.to_i
assert @response.body.empty?
end
@@ -1592,13 +1357,13 @@ class EtagRenderTest < ActionController::TestCase
def test_render_against_etag_request_should_200_when_no_match
@request.if_none_match = etag_for("hello somewhere else")
get :render_hello_world_from_variable
- assert_equal "200 OK", @response.status
+ assert_equal 200, @response.status.to_i
assert !@response.body.empty?
end
def test_render_should_not_set_etag_when_last_modified_has_been_specified
get :render_hello_world_with_last_modified_set
- assert_equal "200 OK", @response.status
+ assert_equal 200, @response.status.to_i
assert_not_nil @response.last_modified
assert_nil @response.etag
assert @response.body.present?
@@ -1612,11 +1377,12 @@ class EtagRenderTest < ActionController::TestCase
@request.if_none_match = expected_etag
get :render_hello_world_from_variable
- assert_equal "304 Not Modified", @response.status
+ assert_equal 304, @response.status.to_i
+ @response = ActionController::TestResponse.new
@request.if_none_match = "\"diftag\""
get :render_hello_world_from_variable
- assert_equal "200 OK", @response.status
+ assert_equal 200, @response.status.to_i
end
def render_with_404_shouldnt_have_etag
@@ -1684,7 +1450,7 @@ class LastModifiedRenderTest < ActionController::TestCase
def test_request_not_modified
@request.if_modified_since = @last_modified
get :conditional_hello
- assert_equal "304 Not Modified", @response.status
+ assert_equal 304, @response.status.to_i
assert @response.body.blank?, @response.body
assert_equal @last_modified, @response.headers['Last-Modified']
end
@@ -1699,7 +1465,7 @@ class LastModifiedRenderTest < ActionController::TestCase
def test_request_modified
@request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
get :conditional_hello
- assert_equal "200 OK", @response.status
+ assert_equal 200, @response.status.to_i
assert !@response.body.blank?
assert_equal @last_modified, @response.headers['Last-Modified']
end
@@ -1735,7 +1501,7 @@ class RenderingLoggingTest < ActionController::TestCase
@controller.logger = MockLogger.new
get :layout_test
logged = @controller.logger.logged.find_all {|l| l =~ /render/i }
- assert_equal "Rendering test/hello_world", logged[0]
- assert_equal "Rendering template within layouts/standard", logged[1]
+ assert logged[0] =~ %r{Rendering.*test/hello_world}
+ assert logged[1] =~ %r{Rendering template within.*layouts/standard}
end
end
diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb
new file mode 100644
index 0000000000..052b4f0b52
--- /dev/null
+++ b/actionpack/test/controller/render_xml_test.rb
@@ -0,0 +1,81 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+require 'pathname'
+
+class TestController < ActionController::Base
+ protect_from_forgery
+
+ def render_with_location
+ render :xml => "<hello/>", :location => "http://example.com", :status => 201
+ end
+
+ def render_with_object_location
+ customer = Customer.new("Some guy", 1)
+ render :xml => "<customer/>", :location => customer_url(customer), :status => :created
+ end
+
+ def render_with_to_xml
+ to_xmlable = Class.new do
+ def to_xml
+ "<i-am-xml/>"
+ end
+ end.new
+
+ render :xml => to_xmlable
+ end
+
+ def formatted_xml_erb
+ end
+
+ def render_xml_with_custom_content_type
+ render :xml => "<blah/>", :content_type => "application/atomsvc+xml"
+ end
+end
+
+class RenderTest < ActionController::TestCase
+ tests TestController
+
+ def setup
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ super
+ @controller.logger = Logger.new(nil)
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_rendering_with_location_should_set_header
+ get :render_with_location
+ assert_equal "http://example.com", @response.headers["Location"]
+ end
+
+ def test_rendering_xml_should_call_to_xml_if_possible
+ get :render_with_to_xml
+ assert_equal "<i-am-xml/>", @response.body
+ end
+
+ def test_rendering_with_object_location_should_set_header_with_url_for
+ ActionController::Routing::Routes.draw do |map|
+ map.resources :customers
+ map.connect ':controller/:action/:id'
+ end
+
+ get :render_with_object_location
+ assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"]
+ end
+
+ def test_should_render_formatted_xml_erb_template
+ get :formatted_xml_erb, :format => :xml
+ assert_equal '<test>passed formatted xml erb</test>', @response.body
+ end
+
+ def test_should_render_xml_but_keep_custom_content_type
+ get :render_xml_with_custom_content_type
+ assert_equal "application/atomsvc+xml", @response.content_type
+ end
+
+ def test_should_use_implicit_content_type
+ get :implicit_content_type, :format => 'atom'
+ assert_equal Mime::ATOM, @response.content_type
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/request/test_request_test.rb b/actionpack/test/controller/request/test_request_test.rb
index 81551b4ba7..0a39feb7fe 100644
--- a/actionpack/test/controller/request/test_request_test.rb
+++ b/actionpack/test/controller/request/test_request_test.rb
@@ -10,26 +10,27 @@ class ActionController::TestRequestTest < ActiveSupport::TestCase
def test_test_request_has_session_options_initialized
assert @request.session_options
end
-
- Rack::Session::Abstract::ID::DEFAULT_OPTIONS.each_key do |option|
- test "test_rack_default_session_options_#{option}_exists_in_session_options_and_is_default" do
- assert_equal(Rack::Session::Abstract::ID::DEFAULT_OPTIONS[option],
- @request.session_options[option],
+
+ ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS.each_key do |option|
+ test "rack default session options #{option} exists in session options and is default" do
+ assert_equal(ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS[option],
+ @request.session_options[option],
"Missing rack session default option #{option} in request.session_options")
end
- test "test_rack_default_session_options_#{option}_exists_in_session_options" do
- assert(@request.session_options.has_key?(option),
+
+ test "rack default session options #{option} exists in session options" do
+ assert(@request.session_options.has_key?(option),
"Missing rack session option #{option} in request.session_options")
end
end
-
+
def test_session_id_exists_by_default
assert_not_nil(@request.session_options[:id])
end
-
+
def test_session_id_different_on_each_call
- prev_id =
+ prev_id =
assert_not_equal(@request.session_options[:id], ActionController::TestRequest.new.session_options[:id])
end
-
+
end \ No newline at end of file
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 894420a910..490a4ff3b3 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -1,5 +1,19 @@
require 'abstract_unit'
+module ActionDispatch
+ class ShowExceptions
+ private
+ def public_path
+ "#{FIXTURE_LOAD_PATH}/public"
+ end
+
+ # Silence logger
+ def logger
+ nil
+ end
+ end
+end
+
class RescueController < ActionController::Base
class NotAuthorized < StandardError
end
@@ -138,213 +152,88 @@ class RescueController < ActionController::Base
end
end
-class RescueControllerTest < ActionController::TestCase
- FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze
-
- def setup
- super
- set_all_requests_local
- populate_exception_object
- end
-
- def set_all_requests_local
- RescueController.consider_all_requests_local = true
- @request.remote_addr = '1.2.3.4'
- @request.host = 'example.com'
- end
-
- def populate_exception_object
- begin
- raise 'foo'
- rescue => @exception
- end
- end
-
- def test_rescue_exceptions_raised_by_filters
- with_rails_root FIXTURE_PUBLIC do
- with_all_requests_local false do
- get :before_filter_raises
- end
- end
+class ExceptionInheritanceRescueController < ActionController::Base
- assert_response :internal_server_error
+ class ParentException < StandardError
end
- def test_rescue_action_locally_if_all_requests_local
- @controller.expects(:local_request?).never
- @controller.expects(:rescue_action_locally).with(@exception)
- @controller.expects(:rescue_action_in_public).never
-
- with_all_requests_local do
- @controller.send :rescue_action, @exception
- end
+ class ChildException < ParentException
end
- def test_rescue_action_locally_if_remote_addr_is_localhost
- @controller.expects(:local_request?).returns(true)
- @controller.expects(:rescue_action_locally).with(@exception)
- @controller.expects(:rescue_action_in_public).never
-
- with_all_requests_local false do
- @controller.send :rescue_action, @exception
- end
+ class GrandchildException < ChildException
end
- def test_rescue_action_in_public_otherwise
- @controller.expects(:local_request?).returns(false)
- @controller.expects(:rescue_action_locally).never
- @controller.expects(:rescue_action_in_public).with(@exception)
+ rescue_from ChildException, :with => lambda { head :ok }
+ rescue_from ParentException, :with => lambda { head :created }
+ rescue_from GrandchildException, :with => lambda { head :no_content }
- with_all_requests_local false do
- @controller.send :rescue_action, @exception
- end
+ def raise_parent_exception
+ raise ParentException
end
- def test_rescue_action_in_public_with_localized_error_file
- # Change locale
- old_locale = I18n.locale
- I18n.locale = :da
-
- with_rails_root FIXTURE_PUBLIC do
- with_all_requests_local false do
- get :raises
- end
- end
-
- assert_response :internal_server_error
- body = File.read("#{FIXTURE_PUBLIC}/public/500.da.html")
- assert_equal body, @response.body
- ensure
- I18n.locale = old_locale
+ def raise_child_exception
+ raise ChildException
end
- def test_rescue_action_in_public_with_error_file
- with_rails_root FIXTURE_PUBLIC do
- with_all_requests_local false do
- get :raises
- end
- end
-
- assert_response :internal_server_error
- body = File.read("#{FIXTURE_PUBLIC}/public/500.html")
- assert_equal body, @response.body
+ def raise_grandchild_exception
+ raise GrandchildException
end
+end
- def test_rescue_action_in_public_without_error_file
- with_rails_root '/tmp' do
- with_all_requests_local false do
- get :raises
- end
- end
-
- assert_response :internal_server_error
- assert_equal ' ', @response.body
+class ExceptionInheritanceRescueControllerTest < ActionController::TestCase
+ def test_bottom_first
+ get :raise_grandchild_exception
+ assert_response :no_content
end
- def test_rescue_unknown_action_in_public_with_error_file
- with_rails_root FIXTURE_PUBLIC do
- with_all_requests_local false do
- get :foobar_doesnt_exist
- end
- end
-
- assert_response :not_found
- body = File.read("#{FIXTURE_PUBLIC}/public/404.html")
- assert_equal body, @response.body
+ def test_inheritance_works
+ get :raise_child_exception
+ assert_response :created
end
+end
- def test_rescue_unknown_action_in_public_without_error_file
- with_rails_root '/tmp' do
- with_all_requests_local false do
- get :foobar_doesnt_exist
- end
- end
-
- assert_response :not_found
- assert_equal ' ', @response.body
+class ControllerInheritanceRescueController < ExceptionInheritanceRescueController
+ class FirstExceptionInChildController < StandardError
end
- def test_rescue_missing_template_in_public
- with_rails_root FIXTURE_PUBLIC do
- with_all_requests_local true do
- get :missing_template
- end
- end
-
- assert_response :internal_server_error
- assert @response.body.include?('missing_template'), "Response should include the template name."
+ class SecondExceptionInChildController < StandardError
end
- def test_rescue_action_locally
- get :raises
- assert_response :internal_server_error
- assert_template 'diagnostics.erb'
- assert @response.body.include?('RescueController#raises'), "Response should include controller and action."
- assert @response.body.include?("don't panic"), "Response should include exception message."
- end
+ rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone }
- def test_local_request_when_remote_addr_is_localhost
- @controller.expects(:request).returns(@request).at_least_once
- with_remote_addr '127.0.0.1' do
- assert @controller.send(:local_request?)
- end
+ def raise_first_exception_in_child_controller
+ raise FirstExceptionInChildController
end
- def test_local_request_when_remote_addr_isnt_locahost
- @controller.expects(:request).returns(@request)
- with_remote_addr '1.2.3.4' do
- assert !@controller.send(:local_request?)
- end
+ def raise_second_exception_in_child_controller
+ raise SecondExceptionInChildController
end
+end
- def test_rescue_responses
- responses = ActionController::Base.rescue_responses
-
- assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses.default
- assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses[Exception.new]
-
- assert_equal :not_found, responses[ActionController::RoutingError.name]
- assert_equal :not_found, responses[ActionController::UnknownAction.name]
- assert_equal :not_found, responses['ActiveRecord::RecordNotFound']
- assert_equal :conflict, responses['ActiveRecord::StaleObjectError']
- assert_equal :unprocessable_entity, responses['ActiveRecord::RecordInvalid']
- assert_equal :unprocessable_entity, responses['ActiveRecord::RecordNotSaved']
- assert_equal :method_not_allowed, responses['ActionController::MethodNotAllowed']
- assert_equal :not_implemented, responses['ActionController::NotImplemented']
+class ControllerInheritanceRescueControllerTest < ActionController::TestCase
+ def test_first_exception_in_child_controller
+ get :raise_first_exception_in_child_controller
+ assert_response :gone
end
- def test_rescue_templates
- templates = ActionController::Base.rescue_templates
-
- assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates.default
- assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates[Exception.new]
-
- assert_equal 'missing_template', templates[ActionView::MissingTemplate.name]
- assert_equal 'routing_error', templates[ActionController::RoutingError.name]
- assert_equal 'unknown_action', templates[ActionController::UnknownAction.name]
- assert_equal 'template_error', templates[ActionView::TemplateError.name]
+ def test_second_exception_in_child_controller
+ get :raise_second_exception_in_child_controller
+ assert_response :gone
end
- def test_not_implemented
- with_all_requests_local false do
- with_rails_public_path(".") do
- head :not_implemented
- end
- end
- assert_response :not_implemented
- assert_equal "GET, PUT", @response.headers['Allow']
+ def test_exception_in_parent_controller
+ get :raise_parent_exception
+ assert_response :created
end
+end
- def test_method_not_allowed
- with_all_requests_local false do
- with_rails_public_path(".") do
- get :method_not_allowed
- end
- end
- assert_response :method_not_allowed
- assert_equal "GET, HEAD, PUT", @response.headers['Allow']
+class ApplicationController < ActionController::Base
+ rescue_from ActionController::RoutingError do
+ render :text => 'no way'
end
+end
+class RescueControllerTest < ActionController::TestCase
def test_rescue_handler
get :not_authorized
assert_response :forbidden
@@ -399,133 +288,6 @@ class RescueControllerTest < ActionController::TestCase
get :resource_unavailable_raise_as_string
assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
end
-
- protected
- def with_all_requests_local(local = true)
- old_local, ActionController::Base.consider_all_requests_local =
- ActionController::Base.consider_all_requests_local, local
- yield
- ensure
- ActionController::Base.consider_all_requests_local = old_local
- end
-
- def with_remote_addr(addr)
- old_remote_addr, @request.remote_addr = @request.remote_addr, addr
- yield
- ensure
- @request.remote_addr = old_remote_addr
- end
-
- def with_rails_public_path(rails_root)
- old_rails = Object.const_get(:Rails) rescue nil
- mod = Object.const_set(:Rails, Module.new)
- (class << mod; self; end).instance_eval do
- define_method(:public_path) { "#{rails_root}/public" }
- end
- yield
- ensure
- Object.module_eval { remove_const(:Rails) } if defined?(Rails)
- Object.const_set(:Rails, old_rails) if old_rails
- end
-
- def with_rails_root(path = nil,&block)
- old_rails_root = RAILS_ROOT if defined?(RAILS_ROOT)
- if path
- silence_warnings { Object.const_set(:RAILS_ROOT, path) }
- else
- Object.remove_const(:RAILS_ROOT) rescue nil
- end
-
- with_rails_public_path(path, &block)
-
- ensure
- if old_rails_root
- silence_warnings { Object.const_set(:RAILS_ROOT, old_rails_root) }
- else
- Object.remove_const(:RAILS_ROOT) rescue nil
- end
- end
-end
-
-class ExceptionInheritanceRescueController < ActionController::Base
-
- class ParentException < StandardError
- end
-
- class ChildException < ParentException
- end
-
- class GrandchildException < ChildException
- end
-
- rescue_from ChildException, :with => lambda { head :ok }
- rescue_from ParentException, :with => lambda { head :created }
- rescue_from GrandchildException, :with => lambda { head :no_content }
-
- def raise_parent_exception
- raise ParentException
- end
-
- def raise_child_exception
- raise ChildException
- end
-
- def raise_grandchild_exception
- raise GrandchildException
- end
-end
-
-class ExceptionInheritanceRescueControllerTest < ActionController::TestCase
- def test_bottom_first
- get :raise_grandchild_exception
- assert_response :no_content
- end
-
- def test_inheritance_works
- get :raise_child_exception
- assert_response :created
- end
-end
-
-class ControllerInheritanceRescueController < ExceptionInheritanceRescueController
- class FirstExceptionInChildController < StandardError
- end
-
- class SecondExceptionInChildController < StandardError
- end
-
- rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone }
-
- def raise_first_exception_in_child_controller
- raise FirstExceptionInChildController
- end
-
- def raise_second_exception_in_child_controller
- raise SecondExceptionInChildController
- end
-end
-
-class ControllerInheritanceRescueControllerTest < ActionController::TestCase
- def test_first_exception_in_child_controller
- get :raise_first_exception_in_child_controller
- assert_response :gone
- end
-
- def test_second_exception_in_child_controller
- get :raise_second_exception_in_child_controller
- assert_response :gone
- end
-
- def test_exception_in_parent_controller
- get :raise_parent_exception
- assert_response :created
- end
-end
-
-class ApplicationController < ActionController::Base
- rescue_from ActionController::RoutingError do
- render :text => 'no way'
- end
end
class RescueTest < ActionController::IntegrationTest
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index c807e71cd7..30ab110ef7 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -120,6 +120,14 @@ class ResourcesTest < ActionController::TestCase
end
end
+ def test_irregular_id_requirements_should_get_passed_to_member_actions
+ expected_options = {:controller => 'messages', :action => 'custom', :id => '1.1.1'}
+
+ with_restful_routing(:messages, :member => {:custom => :get}, :requirements => {:id => /[0-9]\.[0-9]\.[0-9]/}) do
+ assert_recognizes(expected_options, :path => 'messages/1.1.1/custom', :method => :get)
+ end
+ end
+
def test_with_path_prefix
with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do
assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index ef56119751..1694fc8f3e 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'controller/fake_controllers'
+require 'active_support/dependencies'
class MilestonesController < ActionController::Base
def index() head :ok end
@@ -89,6 +90,11 @@ class StaticSegmentTest < Test::Unit::TestCase
assert_equal 'Hello World', s.interpolation_chunk
end
+ def test_value_should_not_be_double_unescaped
+ s = ROUTING::StaticSegment.new('%D0%9A%D0%B0%D1%80%D1%82%D0%B0') # Карта
+ assert_equal '%D0%9A%D0%B0%D1%80%D1%82%D0%B0', s.interpolation_chunk
+ end
+
def test_regexp_chunk_should_escape_specials
s = ROUTING::StaticSegment.new('Hello*World')
assert_equal 'Hello\*World', s.regexp_chunk
@@ -1923,7 +1929,7 @@ class RouteSetTest < Test::Unit::TestCase
end
end
- request.path = "/people"
+ request.request_uri = "/people"
request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("index", request.path_parameters[:action])
@@ -1945,7 +1951,7 @@ class RouteSetTest < Test::Unit::TestCase
}
request.recycle!
- request.path = "/people/5"
+ request.request_uri = "/people/5"
request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("show", request.path_parameters[:action])
@@ -2047,7 +2053,7 @@ class RouteSetTest < Test::Unit::TestCase
end
end
- request.path = "/people/5"
+ request.request_uri = "/people/5"
request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("show", request.path_parameters[:action])
@@ -2059,7 +2065,7 @@ class RouteSetTest < Test::Unit::TestCase
assert_equal("update", request.path_parameters[:action])
request.recycle!
- request.path = "/people/5.png"
+ request.request_uri = "/people/5.png"
request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("show", request.path_parameters[:action])
diff --git a/actionpack/test/controller/selector_test.rb b/actionpack/test/controller/selector_test.rb
index 9d0613d1e2..5a5dc840b5 100644
--- a/actionpack/test/controller/selector_test.rb
+++ b/actionpack/test/controller/selector_test.rb
@@ -5,6 +5,7 @@
require 'abstract_unit'
require 'controller/fake_controllers'
+require 'action_controller/vendor/html-scanner'
class SelectorTest < Test::Unit::TestCase
#
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 2e14a0a32c..0bc0eb2df6 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -11,12 +11,17 @@ class SendFileController < ActionController::Base
layout "layouts/standard" # to make sure layouts don't interfere
attr_writer :options
- def options() @options ||= {} end
+ def options
+ @options ||= {}
+ end
- def file() send_file(file_path, options) end
- def data() send_data(file_data, options) end
+ def file
+ send_file(file_path, options)
+ end
- def rescue_action(e) raise end
+ def data
+ send_data(file_data, options)
+ end
end
class SendFileTest < ActionController::TestCase
@@ -40,17 +45,19 @@ class SendFileTest < ActionController::TestCase
assert_equal file_data, response.body
end
- def test_file_stream
- response = nil
- assert_nothing_raised { response = process('file') }
- assert_not_nil response
- assert_kind_of Proc, response.body_parts
-
- require 'stringio'
- output = StringIO.new
- output.binmode
- assert_nothing_raised { response.body_parts.call(response, output) }
- assert_equal file_data, output.string
+ for_tag(:old_base) do
+ def test_file_stream
+ 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
+ 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
@@ -149,13 +156,13 @@ class SendFileTest < ActionController::TestCase
define_method "test_send_#{method}_status" do
@controller.options = { :stream => false, :status => 500 }
assert_nothing_raised { assert_not_nil process(method) }
- assert_equal '500 Internal Server Error', @response.status
+ assert_equal 500, @response.status
end
define_method "test_default_send_#{method}_status" do
@controller.options = { :stream => false }
assert_nothing_raised { assert_not_nil process(method) }
- assert_equal ActionController::DEFAULT_RENDER_STATUS_CODE, @response.status
+ assert_equal 200, @response.status
end
end
end
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index 124e259ef6..9e88188b9a 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -184,12 +184,6 @@ XML
assert_equal Hash.new, @controller.session.to_hash
end
- def test_session_is_cleared_from_response_after_reset_session
- process :set_session
- process :reset_the_session
- assert_equal Hash.new, @response.session.to_hash
- end
-
def test_session_is_cleared_from_request_after_reset_session
process :set_session
process :reset_the_session
@@ -207,7 +201,7 @@ XML
end
def test_process_with_request_uri_with_params_with_explicit_uri
- @request.set_REQUEST_URI "/explicit/uri"
+ @request.request_uri = "/explicit/uri"
process :test_uri, :id => 7
assert_equal "/explicit/uri", @response.body
end
@@ -218,7 +212,7 @@ XML
end
def test_process_with_query_string_with_explicit_uri
- @request.set_REQUEST_URI "/explicit/uri?q=test?extra=question"
+ @request.request_uri = "/explicit/uri?q=test?extra=question"
process :test_query_string
assert_equal "q=test?extra=question", @response.body
end
@@ -620,7 +614,9 @@ XML
def test_binary_content_works_with_send_file
get :test_send_file
- assert_nothing_raised(NoMethodError) { @response.binary_content }
+ assert_deprecated do
+ assert_nothing_raised(NoMethodError) { @response.binary_content }
+ end
end
protected
@@ -635,33 +631,6 @@ XML
end
end
-class CleanBacktraceTest < ActionController::TestCase
- def test_should_reraise_the_same_object
- exception = ActiveSupport::TestCase::Assertion.new('message')
- clean_backtrace { raise exception }
- rescue Exception => caught
- assert_equal exception.object_id, caught.object_id
- assert_equal exception.message, caught.message
- end
-
- def test_should_clean_assertion_lines_from_backtrace
- path = File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller/testing")
- exception = ActiveSupport::TestCase::Assertion.new('message')
- exception.set_backtrace ["#{path}/abc", "#{path}/assertions/def"]
- clean_backtrace { raise exception }
- rescue Exception => caught
- assert_equal ["#{path}/abc"], caught.backtrace
- end
-
- def test_should_only_clean_assertion_failure_errors
- clean_backtrace do
- raise "can't touch this", [File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller/assertions/abc")]
- end
- rescue => caught
- assert !caught.backtrace.empty?
- end
-end
-
class InferringClassNameTest < ActionController::TestCase
def test_determine_controller_class
assert_equal ContentController, determine_class("ContentControllerTest")
diff --git a/actionpack/test/controller/verification_test.rb b/actionpack/test/controller/verification_test.rb
index 418a81baa8..d568030e41 100644
--- a/actionpack/test/controller/verification_test.rb
+++ b/actionpack/test/controller/verification_test.rb
@@ -103,17 +103,15 @@ class VerificationTest < ActionController::TestCase
end
protected
- def rescue_action(e) raise end
- def unconditional_redirect
- redirect_to :action => "unguarded"
- end
+ def unconditional_redirect
+ redirect_to :action => "unguarded"
+ end
end
- def setup
- @controller = TestController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ tests TestController
+
+ setup do
ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'test', :action => 'foo'
end
@@ -184,7 +182,7 @@ class VerificationTest < ActionController::TestCase
def test_unguarded_without_params
get :unguarded
- assert_equal "", @response.body
+ assert @response.body.blank?
end
def test_guarded_in_session_with_prereqs
diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb
index 1539f8f506..c732d1c910 100644
--- a/actionpack/test/controller/view_paths_test.rb
+++ b/actionpack/test/controller/view_paths_test.rb
@@ -20,9 +20,9 @@ class ViewLoadPathsTest < ActionController::TestCase
layout 'test/sub'
def hello_world; render(:template => 'test/hello_world'); end
end
-
+
def setup
- TestController.view_paths = nil
+ # TestController.view_paths = nil
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@@ -42,30 +42,39 @@ class ViewLoadPathsTest < ActionController::TestCase
ActiveSupport::Deprecation.behavior = @old_behavior
end
+ def expand(array)
+ array.map {|x| File.expand_path(x)}
+ end
+
+ def assert_paths(*paths)
+ controller = paths.first.is_a?(Class) ? paths.shift : @controller
+ assert_equal expand(paths), controller.view_paths.map { |p| p.to_s }
+ end
+
def test_template_load_path_was_set_correctly
- assert_equal [FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
+ assert_paths FIXTURE_LOAD_PATH
end
def test_controller_appends_view_path_correctly
@controller.append_view_path 'foo'
- assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths.map(&:to_s)
+ assert_paths(FIXTURE_LOAD_PATH, "foo")
@controller.append_view_path(%w(bar baz))
- assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s)
-
+ assert_paths(FIXTURE_LOAD_PATH, "foo", "bar", "baz")
+
@controller.append_view_path(FIXTURE_LOAD_PATH)
- assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
+ assert_paths(FIXTURE_LOAD_PATH, "foo", "bar", "baz", FIXTURE_LOAD_PATH)
end
def test_controller_prepends_view_path_correctly
@controller.prepend_view_path 'baz'
- assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
+ assert_paths("baz", FIXTURE_LOAD_PATH)
@controller.prepend_view_path(%w(foo bar))
- assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
+ assert_paths "foo", "bar", "baz", FIXTURE_LOAD_PATH
@controller.prepend_view_path(FIXTURE_LOAD_PATH)
- assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
+ assert_paths FIXTURE_LOAD_PATH, "foo", "bar", "baz", FIXTURE_LOAD_PATH
end
def test_template_appends_view_path_correctly
@@ -73,11 +82,11 @@ class ViewLoadPathsTest < ActionController::TestCase
class_view_paths = TestController.view_paths
@controller.append_view_path 'foo'
- assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths.map(&:to_s)
+ assert_paths FIXTURE_LOAD_PATH, "foo"
@controller.append_view_path(%w(bar baz))
- assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s)
- assert_equal class_view_paths, TestController.view_paths
+ assert_paths FIXTURE_LOAD_PATH, "foo", "bar", "baz"
+ assert_paths TestController, *class_view_paths
end
def test_template_prepends_view_path_correctly
@@ -85,11 +94,11 @@ class ViewLoadPathsTest < ActionController::TestCase
class_view_paths = TestController.view_paths
@controller.prepend_view_path 'baz'
- assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
+ assert_paths "baz", FIXTURE_LOAD_PATH
@controller.prepend_view_path(%w(foo bar))
- assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s)
- assert_equal class_view_paths, TestController.view_paths
+ assert_paths "foo", "bar", "baz", FIXTURE_LOAD_PATH
+ assert_paths TestController, *class_view_paths
end
def test_view_paths
@@ -130,12 +139,12 @@ class ViewLoadPathsTest < ActionController::TestCase
A.view_paths = ['a/path']
- assert_equal ['a/path'], A.view_paths.map(&:to_s)
- assert_equal A.view_paths, B.view_paths
- assert_equal original_load_paths, C.view_paths
-
+ assert_paths A, "a/path"
+ assert_paths A, *B.view_paths
+ assert_paths C, *original_load_paths
+
C.view_paths = []
assert_nothing_raised { C.view_paths << 'c/path' }
- assert_equal ['c/path'], C.view_paths.map(&:to_s)
+ assert_paths C, "c/path"
end
end
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index e89d6bb960..9bf8da7276 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -34,7 +34,7 @@ class WebServiceTest < ActionController::IntegrationTest
def test_check_parameters
with_test_route_set do
get "/"
- assert_equal '', @controller.response.body
+ assert @controller.response.body.blank?
end
end
@@ -163,7 +163,7 @@ class WebServiceTest < ActionController::IntegrationTest
with_test_route_set do
ActionController::Base.param_parsers[Mime::XML] = :xml_simple
assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} }
- assert_equal "", @controller.response.body
+ assert @controller.response.body.blank?
end
end
diff --git a/actionpack/test/dispatch/rack_test.rb b/actionpack/test/dispatch/rack_test.rb
index 9fad4b22ee..94eba2a24f 100644
--- a/actionpack/test/dispatch/rack_test.rb
+++ b/actionpack/test/dispatch/rack_test.rb
@@ -201,93 +201,3 @@ class RackRequestNeedsRewoundTest < BaseRackTest
assert_equal 0, request.body.pos
end
end
-
-class RackResponseTest < BaseRackTest
- def setup
- super
- @response = ActionDispatch::Response.new
- end
-
- test "simple output" do
- @response.body = "Hello, World!"
- @response.prepare!
-
- status, headers, body = @response.to_a
- assert_equal 200, status
- assert_equal({
- "Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "private, max-age=0, must-revalidate",
- "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
- "Set-Cookie" => "",
- "Content-Length" => "13"
- }, headers)
-
- parts = []
- body.each { |part| parts << part }
- assert_equal ["Hello, World!"], parts
- end
-
- def test_utf8_output
- @response.body = [1090, 1077, 1089, 1090].pack("U*")
- @response.prepare!
-
- status, headers, body = @response.to_a
- assert_equal 200, status
- assert_equal({
- "Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "private, max-age=0, must-revalidate",
- "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"',
- "Set-Cookie" => "",
- "Content-Length" => "8"
- }, headers)
- end
-
- def test_streaming_block
- @response.body = Proc.new do |response, output|
- 5.times { |n| output.write(n) }
- end
- @response.prepare!
-
- status, headers, body = @response.to_a
- assert_equal 200, status
- assert_equal({
- "Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "no-cache",
- "Set-Cookie" => ""
- }, headers)
-
- parts = []
- body.each { |part| parts << part.to_s }
- assert_equal ["0", "1", "2", "3", "4"], parts
- end
-end
-
-class RackResponseHeadersTest < BaseRackTest
- def setup
- super
- @response = ActionDispatch::Response.new
- @response.status = "200 OK"
- end
-
- test "content type" do
- [204, 304].each do |c|
- @response.status = c.to_s
- assert !response_headers.has_key?("Content-Type"), "#{c} should not have Content-Type header"
- end
-
- [200, 302, 404, 500].each do |c|
- @response.status = c.to_s
- assert response_headers.has_key?("Content-Type"), "#{c} did not have Content-Type header"
- end
- end
-
- test "status" do
- assert !response_headers.has_key?('Status')
- end
-
- private
- def response_headers
- @response.prepare!
- @response.to_a[1]
- end
-end
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 88b81dc493..9e008a9ae8 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -94,9 +94,8 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
assert_equal %w(files foo), params.keys.sort
assert_equal 'bar', params['foo']
- # Ruby CGI doesn't handle multipart/mixed for us.
+ # Rack doesn't handle multipart/mixed for us.
files = params['files']
- assert_kind_of String, files
files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
assert_equal 19756, files.size
end
@@ -133,46 +132,6 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
end
end
- # The lint wrapper is used in integration tests
- # instead of a normal StringIO class
- InputWrapper = Rack::Lint::InputWrapper
-
- test "parses unwindable stream" do
- InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE)
- params = parse_multipart('large_text_file')
- assert_equal %w(file foo), params.keys.sort
- assert_equal 'bar', params['foo']
- end
-
- test "uploads and reads file with unwindable input" do
- InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE)
-
- with_test_routing do
- post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain")
- assert_equal "File: Hello", response.body
- end
- end
-
- test "passes through rack middleware and uploads file" do
- with_muck_middleware do
- with_test_routing do
- post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain")
- assert_equal "File: Hello", response.body
- end
- end
- end
-
- test "passes through rack middleware and uploads file with unwindable input" do
- InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE)
-
- with_muck_middleware do
- with_test_routing do
- post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain")
- assert_equal "File: Hello", response.body
- end
- end
- end
-
private
def fixture(name)
File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
@@ -199,25 +158,4 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
yield
end
end
-
- class MuckMiddleware
- def initialize(app)
- @app = app
- end
-
- def call(env)
- env['rack.input'].read
- env['rack.input'].rewind
- @app.call(env)
- end
- end
-
- def with_muck_middleware
- original_middleware = ActionController::Dispatcher.middleware
- middleware = original_middleware.dup
- middleware.insert_after ActionDispatch::RewindableInput, MuckMiddleware
- ActionController::Dispatcher.middleware = middleware
- yield
- ActionController::Dispatcher.middleware = original_middleware
- end
end
diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
index 8de4a83d76..7167cdafac 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -126,45 +126,7 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest
assert_parses expected, query
end
- test "passes through rack middleware and parses params" do
- with_muck_middleware do
- assert_parses({ "a" => { "b" => "c" } }, "a[b]=c")
- end
- end
-
- # The lint wrapper is used in integration tests
- # instead of a normal StringIO class
- InputWrapper = Rack::Lint::InputWrapper
-
- test "passes through rack middleware and parses params with unwindable input" do
- InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE)
- with_muck_middleware do
- assert_parses({ "a" => { "b" => "c" } }, "a[b]=c")
- end
- end
-
private
- class MuckMiddleware
- def initialize(app)
- @app = app
- end
-
- def call(env)
- env['rack.input'].read
- env['rack.input'].rewind
- @app.call(env)
- end
- end
-
- def with_muck_middleware
- original_middleware = ActionController::Dispatcher.middleware
- middleware = original_middleware.dup
- middleware.insert_after ActionDispatch::RewindableInput, MuckMiddleware
- ActionController::Dispatcher.middleware = middleware
- yield
- ActionController::Dispatcher.middleware = original_middleware
- end
-
def with_test_routing
with_routing do |set|
set.draw do |map|
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index d57a2a611f..3a85db8aa5 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -307,7 +307,7 @@ class RequestTest < ActiveSupport::TestCase
test "restrict method hacking" do
[:get, :put, :delete].each do |method|
request = stub_request 'REQUEST_METHOD' => method.to_s.upcase,
- 'action_controller.request.request_parameters' => { :_method => 'put' }
+ 'action_dispatch.request.request_parameters' => { :_method => 'put' }
assert_equal method, request.method
end
end
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
new file mode 100644
index 0000000000..2ddc6cb2b5
--- /dev/null
+++ b/actionpack/test/dispatch/response_test.rb
@@ -0,0 +1,130 @@
+require 'abstract_unit'
+
+class ResponseTest < ActiveSupport::TestCase
+ def setup
+ @response = ActionDispatch::Response.new
+ end
+
+ test "simple output" do
+ @response.body = "Hello, World!"
+ @response.prepare!
+
+ status, headers, body = @response.to_a
+ assert_equal 200, status
+ assert_equal({
+ "Content-Type" => "text/html; charset=utf-8",
+ "Cache-Control" => "private, max-age=0, must-revalidate",
+ "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
+ "Set-Cookie" => "",
+ "Content-Length" => "13"
+ }, headers)
+
+ parts = []
+ body.each { |part| parts << part }
+ assert_equal ["Hello, World!"], parts
+ end
+
+ test "utf8 output" do
+ @response.body = [1090, 1077, 1089, 1090].pack("U*")
+ @response.prepare!
+
+ status, headers, body = @response.to_a
+ assert_equal 200, status
+ assert_equal({
+ "Content-Type" => "text/html; charset=utf-8",
+ "Cache-Control" => "private, max-age=0, must-revalidate",
+ "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"',
+ "Set-Cookie" => "",
+ "Content-Length" => "8"
+ }, headers)
+ end
+
+ test "streaming block" do
+ @response.body = Proc.new do |response, output|
+ 5.times { |n| output.write(n) }
+ end
+ @response.prepare!
+
+ status, headers, body = @response.to_a
+ assert_equal 200, status
+ assert_equal({
+ "Content-Type" => "text/html; charset=utf-8",
+ "Cache-Control" => "no-cache",
+ "Set-Cookie" => ""
+ }, headers)
+
+ parts = []
+ body.each { |part| parts << part.to_s }
+ assert_equal ["0", "1", "2", "3", "4"], parts
+ end
+
+ test "content type" do
+ [204, 304].each do |c|
+ @response.status = c.to_s
+ @response.prepare!
+ status, headers, body = @response.to_a
+ assert !headers.has_key?("Content-Type"), "#{c} should not have Content-Type header"
+ end
+
+ [200, 302, 404, 500].each do |c|
+ @response.status = c.to_s
+ @response.prepare!
+ status, headers, body = @response.to_a
+ assert headers.has_key?("Content-Type"), "#{c} did not have Content-Type header"
+ end
+ end
+
+ test "does not include Status header" do
+ @response.status = "200 OK"
+ @response.prepare!
+ status, headers, body = @response.to_a
+ assert !headers.has_key?('Status')
+ end
+
+ test "response code" do
+ @response.status = "200 OK"
+ assert_equal 200, @response.response_code
+
+ @response.status = "200"
+ assert_equal 200, @response.response_code
+
+ @response.status = 200
+ assert_equal 200, @response.response_code
+ end
+
+ test "code" do
+ @response.status = "200 OK"
+ assert_equal "200", @response.code
+
+ @response.status = "200"
+ assert_equal "200", @response.code
+
+ @response.status = 200
+ assert_equal "200", @response.code
+ end
+
+ test "message" do
+ @response.status = "200 OK"
+ assert_equal "OK", @response.message
+
+ @response.status = "200"
+ assert_equal "OK", @response.message
+
+ @response.status = 200
+ assert_equal "OK", @response.message
+ end
+
+ test "cookies" do
+ @response.set_cookie("user_name", :value => "david", :path => "/")
+ @response.prepare!
+ status, headers, body = @response.to_a
+ assert_equal "user_name=david; path=/", headers["Set-Cookie"]
+ assert_equal({"user_name" => "david"}, @response.cookies)
+
+ @response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5))
+ @response.prepare!
+ status, headers, body = @response.to_a
+ assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", headers["Set-Cookie"]
+ assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies)
+ end
+end
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index b9bf8cf411..2db76818ac 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -5,13 +5,15 @@ class CookieStoreTest < ActionController::IntegrationTest
SessionKey = '_myapp_session'
SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
+ # Make sure Session middleware doesnt get included in the middleware stack
+ ActionController::Base.session_store = nil
+
DispatcherApp = ActionController::Dispatcher.new
CookieStoreApp = ActionDispatch::Session::CookieStore.new(DispatcherApp,
:key => SessionKey, :secret => SessionSecret)
Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1')
-
- SignedBar = "BAh7BjoIZm9vIghiYXI%3D--fef868465920f415f2c0652d6910d3af288a0367"
+ SignedBar = Verifier.generate(:foo => "bar", :session_id => ActiveSupport::SecureRandom.hex(16))
class TestController < ActionController::Base
def no_session_access
diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb
index 7561c93e4a..278f2c83ea 100644
--- a/actionpack/test/dispatch/session/mem_cache_store_test.rb
+++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb
@@ -61,6 +61,14 @@ class MemCacheStoreTest < ActionController::IntegrationTest
end
end
+ def test_getting_session_value_does_not_set_cookie
+ with_test_route_set do
+ get '/get_session_value'
+ assert_response :success
+ assert_equal "", headers["Set-Cookie"]
+ end
+ end
+
def test_setting_session_value_after_session_reset
with_test_route_set do
get '/set_session_value'
diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb
index de6539e1cc..0ff93f1c5d 100644
--- a/actionpack/test/dispatch/session/test_session_test.rb
+++ b/actionpack/test/dispatch/session/test_session_test.rb
@@ -2,37 +2,30 @@ require 'abstract_unit'
require 'stringio'
class ActionController::TestSessionTest < ActiveSupport::TestCase
-
def test_calling_delete_without_parameters_raises_deprecation_warning_and_calls_to_clear_test_session
assert_deprecated(/use clear instead/){ ActionController::TestSession.new.delete }
end
-
+
def test_calling_update_without_parameters_raises_deprecation_warning_and_calls_to_clear_test_session
assert_deprecated(/use replace instead/){ ActionController::TestSession.new.update }
end
-
+
def test_calling_close_raises_deprecation_warning
assert_deprecated(/sessions should no longer be closed/){ ActionController::TestSession.new.close }
end
-
- def test_defaults
- session = ActionController::TestSession.new
- assert_equal({}, session.data)
- assert_equal('', session.session_id)
- end
-
+
def test_ctor_allows_setting
session = ActionController::TestSession.new({:one => 'one', :two => 'two'})
assert_equal('one', session[:one])
assert_equal('two', session[:two])
end
-
+
def test_setting_session_item_sets_item
session = ActionController::TestSession.new
session[:key] = 'value'
assert_equal('value', session[:key])
end
-
+
def test_calling_delete_removes_item
session = ActionController::TestSession.new
session[:key] = 'value'
@@ -40,13 +33,13 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
session.delete(:key)
assert_nil(session[:key])
end
-
+
def test_calling_update_with_params_passes_to_attributes
session = ActionController::TestSession.new()
session.update('key' => 'value')
assert_equal('value', session[:key])
end
-
+
def test_clear_emptys_session
params = {:one => 'one', :two => 'two'}
session = ActionController::TestSession.new({:one => 'one', :two => 'two'})
@@ -54,5 +47,4 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
assert_nil(session[:one])
assert_nil(session[:two])
end
-
-end \ No newline at end of file
+end
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
new file mode 100644
index 0000000000..ce1973853e
--- /dev/null
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -0,0 +1,108 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ class ShowExceptions
+ private
+ def public_path
+ "#{FIXTURE_LOAD_PATH}/public"
+ end
+
+ # Silence logger
+ def logger
+ nil
+ end
+ end
+end
+
+class ShowExceptionsTest < ActionController::IntegrationTest
+ Boomer = lambda do |env|
+ req = ActionDispatch::Request.new(env)
+ case req.path
+ when "/not_found"
+ raise ActionController::UnknownAction
+ when "/method_not_allowed"
+ raise ActionController::MethodNotAllowed
+ when "/not_implemented"
+ raise ActionController::NotImplemented
+ when "/unprocessable_entity"
+ raise ActionController::InvalidAuthenticityToken
+ else
+ raise "puke!"
+ end
+ end
+
+ ProductionApp = ActionDispatch::ShowExceptions.new(Boomer, false)
+ DevelopmentApp = ActionDispatch::ShowExceptions.new(Boomer, true)
+
+ test "rescue in public from a remote ip" do
+ @integration_session = open_session(ProductionApp)
+ self.remote_addr = '208.77.188.166'
+
+ get "/"
+ assert_response 500
+ assert_equal "500 error fixture\n", body
+
+ get "/not_found"
+ assert_response 404
+ assert_equal "404 error fixture\n", body
+
+ get "/method_not_allowed"
+ assert_response 405
+ assert_equal "", body
+ end
+
+ test "rescue locally from a local request" do
+ @integration_session = open_session(ProductionApp)
+ self.remote_addr = '127.0.0.1'
+
+ get "/"
+ assert_response 500
+ assert_match /puke/, body
+
+ get "/not_found"
+ assert_response 404
+ assert_match /#{ActionController::UnknownAction.name}/, body
+
+ get "/method_not_allowed"
+ assert_response 405
+ assert_match /ActionController::MethodNotAllowed/, body
+ end
+
+ test "localize public rescue message" do
+ # Change locale
+ old_locale = I18n.locale
+ I18n.locale = :da
+
+ begin
+ @integration_session = open_session(ProductionApp)
+ self.remote_addr = '208.77.188.166'
+
+ get "/"
+ assert_response 500
+ assert_equal "500 localized error fixture\n", body
+
+ get "/not_found"
+ assert_response 404
+ assert_equal "404 error fixture\n", body
+ ensure
+ I18n.locale = old_locale
+ end
+ end
+
+ test "always rescue locally in development mode" do
+ @integration_session = open_session(DevelopmentApp)
+ self.remote_addr = '208.77.188.166'
+
+ get "/"
+ assert_response 500
+ assert_match /puke/, body
+
+ get "/not_found"
+ assert_response 404
+ assert_match /#{ActionController::UnknownAction.name}/, body
+
+ get "/method_not_allowed"
+ assert_response 405
+ assert_match /ActionController::MethodNotAllowed/, body
+ end
+end
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
new file mode 100644
index 0000000000..5da02b2ea6
--- /dev/null
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -0,0 +1,45 @@
+require 'abstract_unit'
+
+class TestRequestTest < ActiveSupport::TestCase
+ test "sane defaults" do
+ env = ActionDispatch::TestRequest.new.env
+
+ assert_equal "GET", env.delete("REQUEST_METHOD")
+ assert_equal "off", env.delete("HTTPS")
+ assert_equal "http", env.delete("rack.url_scheme")
+ assert_equal "example.org", env.delete("SERVER_NAME")
+ assert_equal "80", env.delete("SERVER_PORT")
+ assert_equal "/", env.delete("PATH_INFO")
+ assert_equal "", env.delete("SCRIPT_NAME")
+ assert_equal "", env.delete("QUERY_STRING")
+ assert_equal "0", env.delete("CONTENT_LENGTH")
+
+ assert_equal "test.host", env.delete("HTTP_HOST")
+ assert_equal "0.0.0.0", env.delete("REMOTE_ADDR")
+ assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT")
+
+ assert_equal [1, 0], env.delete("rack.version")
+ assert_equal "", env.delete("rack.input").string
+ assert_kind_of StringIO, env.delete("rack.errors")
+ assert_equal true, env.delete("rack.multithread")
+ assert_equal true, env.delete("rack.multiprocess")
+ assert_equal false, env.delete("rack.run_once")
+
+ assert env.empty?, env.inspect
+ end
+
+ test "cookie jar" do
+ req = ActionDispatch::TestRequest.new
+
+ assert_equal({}, req.cookies)
+ assert_equal nil, req.env["HTTP_COOKIE"]
+
+ req.cookies["user_name"] = "david"
+ assert_equal({"user_name" => "david"}, req.cookies)
+ assert_equal "user_name=david;", req.env["HTTP_COOKIE"]
+
+ req.cookies["login"] = "XJ-122"
+ assert_equal({"user_name" => "david", "login" => "XJ-122"}, req.cookies)
+ assert_equal %w(login=XJ-122 user_name=david), req.env["HTTP_COOKIE"].split(/; ?/).sort
+ end
+end
diff --git a/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab b/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab
index 018abfb0ac..fcee620d82 100644
--- a/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab
+++ b/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab
@@ -1 +1 @@
-Mab \ No newline at end of file
+layouts/third_party_template_library.mab \ No newline at end of file
diff --git a/actionpack/test/fixtures/layouts/standard.erb b/actionpack/test/fixtures/layouts/standard.html.erb
index 368764e6f4..368764e6f4 100644
--- a/actionpack/test/fixtures/layouts/standard.erb
+++ b/actionpack/test/fixtures/layouts/standard.html.erb
diff --git a/actionpack/test/fixtures/test/greeting.erb b/actionpack/test/fixtures/test/greeting.html.erb
index 62fb0293f0..62fb0293f0 100644
--- a/actionpack/test/fixtures/test/greeting.erb
+++ b/actionpack/test/fixtures/test/greeting.html.erb
diff --git a/actionpack/test/fixtures/test/render_file_with_locals_and_default.erb b/actionpack/test/fixtures/test/render_file_with_locals_and_default.erb
new file mode 100644
index 0000000000..9b4900acc5
--- /dev/null
+++ b/actionpack/test/fixtures/test/render_file_with_locals_and_default.erb
@@ -0,0 +1 @@
+<%= secret ||= 'one' %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb
index 0b4d19aa0e..58cd03b439 100644
--- a/actionpack/test/fixtures/test/utf8.html.erb
+++ b/actionpack/test/fixtures/test/utf8.html.erb
@@ -1,2 +1,5 @@
+<%# encoding: utf-8 -%>
Русский текст
-日本語のテキスト \ No newline at end of file
+<%= "日".encoding %>
+<%= @output_buffer.encoding %>
+<%= __ENCODING__ %>
diff --git a/actionpack/test/controller/html-scanner/cdata_node_test.rb b/actionpack/test/html-scanner/cdata_node_test.rb
index 1822cc565a..1822cc565a 100644
--- a/actionpack/test/controller/html-scanner/cdata_node_test.rb
+++ b/actionpack/test/html-scanner/cdata_node_test.rb
diff --git a/actionpack/test/controller/html-scanner/document_test.rb b/actionpack/test/html-scanner/document_test.rb
index c68f04fa75..c68f04fa75 100644
--- a/actionpack/test/controller/html-scanner/document_test.rb
+++ b/actionpack/test/html-scanner/document_test.rb
diff --git a/actionpack/test/controller/html-scanner/node_test.rb b/actionpack/test/html-scanner/node_test.rb
index b0df36877e..b0df36877e 100644
--- a/actionpack/test/controller/html-scanner/node_test.rb
+++ b/actionpack/test/html-scanner/node_test.rb
diff --git a/actionpack/test/controller/html-scanner/sanitizer_test.rb b/actionpack/test/html-scanner/sanitizer_test.rb
index e85a5c7abf..e85a5c7abf 100644
--- a/actionpack/test/controller/html-scanner/sanitizer_test.rb
+++ b/actionpack/test/html-scanner/sanitizer_test.rb
diff --git a/actionpack/test/controller/html-scanner/tag_node_test.rb b/actionpack/test/html-scanner/tag_node_test.rb
index d1d4667378..d1d4667378 100644
--- a/actionpack/test/controller/html-scanner/tag_node_test.rb
+++ b/actionpack/test/html-scanner/tag_node_test.rb
diff --git a/actionpack/test/controller/html-scanner/text_node_test.rb b/actionpack/test/html-scanner/text_node_test.rb
index 1ab3f4454e..1ab3f4454e 100644
--- a/actionpack/test/controller/html-scanner/text_node_test.rb
+++ b/actionpack/test/html-scanner/text_node_test.rb
diff --git a/actionpack/test/controller/html-scanner/tokenizer_test.rb b/actionpack/test/html-scanner/tokenizer_test.rb
index a001bcbbad..a001bcbbad 100644
--- a/actionpack/test/controller/html-scanner/tokenizer_test.rb
+++ b/actionpack/test/html-scanner/tokenizer_test.rb
diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/lib/active_record_unit.rb
index 9e0c66055d..1ba308e9d7 100644
--- a/actionpack/test/active_record_unit.rb
+++ b/actionpack/test/lib/active_record_unit.rb
@@ -16,7 +16,7 @@ if defined?(ActiveRecord) && defined?(Fixtures)
else
$stderr.print 'Attempting to load Active Record... '
begin
- PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib"
+ PATH_TO_AR = "#{File.dirname(__FILE__)}/../../../activerecord/lib"
raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
$LOAD_PATH.unshift PATH_TO_AR
require 'active_record'
@@ -72,13 +72,13 @@ class ActiveRecordTestConnector
# Load actionpack sqlite tables
def load_schema
- File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql|
+ File.read(File.dirname(__FILE__) + "/../fixtures/db_definitions/sqlite.sql").split(';').each do |sql|
ActiveRecord::Base.connection.execute(sql) unless sql.blank?
end
end
def require_fixture_models
- Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f}
+ Dir.glob(File.dirname(__FILE__) + "/../fixtures/*.rb").each {|f| require f}
end
end
end
diff --git a/actionpack/test/controller/fake_controllers.rb b/actionpack/test/lib/controller/fake_controllers.rb
index 75c114c103..75c114c103 100644
--- a/actionpack/test/controller/fake_controllers.rb
+++ b/actionpack/test/lib/controller/fake_controllers.rb
diff --git a/actionpack/test/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index 0b30c79b10..0b30c79b10 100644
--- a/actionpack/test/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb
index 26f6ec2d0c..59fb6819ed 100644
--- a/actionpack/test/lib/fixture_template.rb
+++ b/actionpack/test/lib/fixture_template.rb
@@ -1,35 +1,104 @@
module ActionView #:nodoc:
- class FixtureTemplate < Template
- class FixturePath < Template::Path
- def initialize(hash = {})
- @hash = {}
-
- hash.each do |k, v|
- @hash[k.sub(/\.\w+$/, '')] = FixtureTemplate.new(v, k.split("/").last, self)
+class Template
+ class FixturePath < Path
+ def initialize(hash = {}, options = {})
+ super(options)
+ @hash = hash
+ end
+
+ def find_templates(name, details, prefix, partial)
+ if regexp = details_to_regexp(name, details, prefix, partial)
+ cached(regexp) do
+ templates = []
+ @hash.select { |k,v| k =~ regexp }.each do |path, source|
+ templates << Template.new(source, path, *path_to_details(path))
+ end
+ templates
end
-
- super("fixtures://root")
- end
-
- def find_template(path)
- @hash[path]
end
end
- def initialize(body, *args)
- @body = body
- super(*args)
+ private
+
+ def formats_regexp
+ @formats_regexp ||= begin
+ formats = Mime::SET.map { |m| m.symbol }
+ '(?:' + formats.map { |l| "\\.#{Regexp.escape(l.to_s)}" }.join('|') + ')?'
+ end
end
- def source
- @body
+ def handler_regexp
+ e = TemplateHandlers.extensions.map{|h| "\\.#{Regexp.escape(h.to_s)}"}.join("|")
+ "(?:#{e})?"
end
- private
-
- def find_full_path(path, load_paths)
- return '/', path
+ def details_to_regexp(name, details, prefix, partial)
+ path = ""
+ path << "#{prefix}/" unless prefix.empty?
+ path << (partial ? "_#{name}" : name)
+
+ extensions = ""
+ [:locales, :formats].each do |k|
+ extensions << if exts = details[k]
+ '(?:' + exts.map {|e| "\\.#{Regexp.escape(e.to_s)}"}.join('|') + ')?'
+ else
+ k == :formats ? formats_regexp : ''
+ end
+ end
+
+ %r'^#{Regexp.escape(path)}#{extensions}#{handler_regexp}$'
+ 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[1] == '_'
+ details = (m[2]||"").split('.').reject { |e| e.empty? }
+ handler = Template.handler_class_for_extension(m[3])
+
+ format = Mime[details.last] && details.pop.to_sym
+ locale = details.last && details.pop.to_sym
+
+ return handler, :format => format, :locale => locale, :partial => partial
+ end
end
-
end
+
+
+ # class FixtureTemplate < Template
+ # class FixturePath < Template::Path
+ # def initialize(hash = {})
+ # @hash = {}
+ #
+ # hash.each do |k, v|
+ # @hash[k.sub(/\.\w+$/, '')] = FixtureTemplate.new(v, k.split("/").last, self)
+ # end
+ #
+ # super("fixtures://root")
+ # end
+ #
+ # def find_template(path)
+ # @hash[path]
+ # end
+ # end
+ #
+ # def initialize(body, *args)
+ # @body = body
+ # super(*args)
+ # end
+ #
+ # def source
+ # @body
+ # end
+ #
+ # private
+ #
+ # def find_full_path(path, load_paths)
+ # return '/', path
+ # end
+ #
+ # end
+end
end \ No newline at end of file
diff --git a/actionpack/test/testing_sandbox.rb b/actionpack/test/lib/testing_sandbox.rb
index c36585104f..c36585104f 100644
--- a/actionpack/test/testing_sandbox.rb
+++ b/actionpack/test/lib/testing_sandbox.rb
diff --git a/actionpack/test/new_base/abstract_unit.rb b/actionpack/test/new_base/abstract_unit.rb
new file mode 100644
index 0000000000..e6690d41d9
--- /dev/null
+++ b/actionpack/test/new_base/abstract_unit.rb
@@ -0,0 +1,166 @@
+$:.unshift(File.dirname(__FILE__) + '/../../lib')
+$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+
+$:.unshift(File.dirname(__FILE__) + '/../fixtures/helpers')
+$:.unshift(File.dirname(__FILE__) + '/../fixtures/alternate_helpers')
+
+ENV['new_base'] = "true"
+$stderr.puts "Running old tests on new_base"
+
+require 'test/unit'
+require 'active_support'
+
+# TODO : Revisit requiring all the core extensions here
+require 'active_support/core_ext'
+
+require 'active_support/test_case'
+require 'action_controller/abstract'
+require 'action_controller/new_base'
+require 'fixture_template'
+require 'action_controller/testing/process2'
+require 'action_view/test_case'
+require 'action_controller/testing/integration'
+require 'active_support/dependencies'
+
+$tags[:new_base] = true
+
+begin
+ require 'ruby-debug'
+ Debugger.settings[:autoeval] = true
+ Debugger.start
+rescue LoadError
+ # Debugging disabled. `gem install ruby-debug` to enable.
+end
+
+ActiveSupport::Dependencies.hook!
+
+# Show backtraces for deprecated behavior for quicker cleanup.
+ActiveSupport::Deprecation.debug = true
+
+# Register danish language for testing
+I18n.backend.store_translations 'da', {}
+I18n.backend.store_translations 'pt-BR', {}
+ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
+
+FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), '../fixtures')
+
+module ActionController
+ Base.session = {
+ :key => '_testing_session',
+ :secret => '8273f16463985e2b3747dc25e30f2528'
+}
+
+ class ActionControllerError < StandardError #:nodoc:
+ end
+
+ class SessionRestoreError < ActionControllerError #:nodoc:
+ end
+
+ class RenderError < ActionControllerError #:nodoc:
+ end
+
+ class RoutingError < ActionControllerError #:nodoc:
+ attr_reader :failures
+ def initialize(message, failures=[])
+ super(message)
+ @failures = failures
+ end
+ end
+
+ class MethodNotAllowed < ActionControllerError #:nodoc:
+ attr_reader :allowed_methods
+
+ def initialize(*allowed_methods)
+ super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
+ @allowed_methods = allowed_methods
+ end
+
+ def allowed_methods_header
+ allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
+ end
+
+ def handle_response!(response)
+ response.headers['Allow'] ||= allowed_methods_header
+ end
+ end
+
+ class NotImplemented < MethodNotAllowed #:nodoc:
+ end
+
+ class UnknownController < ActionControllerError #:nodoc:
+ end
+
+ class MissingFile < ActionControllerError #:nodoc:
+ end
+
+ class RenderError < ActionControllerError #:nodoc:
+ end
+
+ class SessionOverflowError < ActionControllerError #:nodoc:
+ DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
+ class UnknownHttpMethod < ActionControllerError #:nodoc:
+ end
+
+ class Base
+ include ActionController::Testing
+ end
+
+ Base.view_paths = FIXTURE_LOAD_PATH
+
+ class TestCase
+ include TestProcess
+ setup do
+ ActionController::Routing::Routes.draw do |map|
+ map.connect ':controller/:action/:id'
+ end
+ end
+
+ def assert_template(options = {}, message = nil)
+ validate_request!
+
+ hax = @controller._action_view.instance_variable_get(:@_rendered)
+
+ case options
+ when NilClass, String
+ rendered = (hax[:template] || []).map { |t| t.identifier }
+ msg = build_message(message,
+ "expecting <?> but rendering with <?>",
+ options, rendered.join(', '))
+ assert_block(msg) do
+ if options.nil?
+ hax[:template].blank?
+ else
+ rendered.any? { |t| t.match(options) }
+ end
+ end
+ when Hash
+ if expected_partial = options[:partial]
+ partials = hax[:partials]
+ if expected_count = options[:count]
+ found = partials.detect { |p, _| p.identifier.match(expected_partial) }
+ actual_count = found.nil? ? 0 : found.second
+ msg = build_message(message,
+ "expecting ? to be rendered ? time(s) but rendered ? time(s)",
+ expected_partial, expected_count, actual_count)
+ assert(actual_count == expected_count.to_i, msg)
+ else
+ msg = build_message(message,
+ "expecting partial <?> but action rendered <?>",
+ options[:partial], partials.keys)
+ assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg)
+ end
+ else
+ assert hax[:partials].empty?,
+ "Expected no partials to be rendered"
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/new_base/base_test.rb b/actionpack/test/new_base/base_test.rb
index 4f46cb6492..d9d552f9e5 100644
--- a/actionpack/test/new_base/base_test.rb
+++ b/actionpack/test/new_base/base_test.rb
@@ -1,8 +1,8 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
# Tests the controller dispatching happy path
-module HappyPath
- class SimpleDispatchController < ActionController::Base2
+module Dispatching
+ class SimpleController < ActionController::Base
def index
render :text => "success"
end
@@ -10,81 +10,62 @@ module HappyPath
def modify_response_body
self.response_body = "success"
end
-
+
def modify_response_body_twice
ret = (self.response_body = "success")
self.response_body = "#{ret}!"
end
-
+
def modify_response_headers
-
end
end
-
- class TestSimpleDispatch < SimpleRouteCase
-
- get "/happy_path/simple_dispatch/index"
-
- test "sets the body" do
+
+ class EmptyController < ActionController::Base ; end
+
+ module Submodule
+ class ContainedEmptyController < ActionController::Base ; end
+ end
+
+ class BaseTest < SimpleRouteCase
+ # :api: plugin
+ test "simple dispatching" do
+ get "/dispatching/simple/index"
+
assert_body "success"
- end
-
- test "sets the status code" do
assert_status 200
+ assert_content_type "text/html; charset=utf-8"
+ assert_header "Content-Length", "7"
end
-
- test "sets the content type" do
- assert_content_type Mime::HTML
- end
-
- test "sets the content length" do
- assert_header "Content-Length", 7
- end
-
- end
-
- # :api: plugin
- class TestDirectResponseMod < SimpleRouteCase
- get "/happy_path/simple_dispatch/modify_response_body"
-
- test "sets the body" do
+
+ # :api: plugin
+ test "directly modifying response body" do
+ get "/dispatching/simple/modify_response_body"
+
assert_body "success"
+ assert_header "Content-Length", "7" # setting the body manually sets the content length
end
-
- test "setting the body manually sets the content length" do
- assert_header "Content-Length", 7
- end
- end
-
- # :api: plugin
- class TestDirectResponseModTwice < SimpleRouteCase
- get "/happy_path/simple_dispatch/modify_response_body_twice"
-
- test "self.response_body= returns the body being set" do
+
+ # :api: plugin
+ test "directly modifying response body twice" do
+ get "/dispatching/simple/modify_response_body_twice"
+
assert_body "success!"
+ assert_header "Content-Length", "8"
end
-
- test "updating the response body updates the content length" do
- assert_header "Content-Length", 8
- end
- end
-end
+ test "controller path" do
+ assert_equal 'dispatching/empty', EmptyController.controller_path
+ assert_equal EmptyController.controller_path, EmptyController.new.controller_path
+ end
-class EmptyController < ActionController::Base2 ; end
-module Submodule
- class ContainedEmptyController < ActionController::Base2 ; end
-end
+ test "namespaced controller path" do
+ assert_equal 'dispatching/submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
+ assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
+ end
-class ControllerClassTests < Test::Unit::TestCase
- def test_controller_path
- assert_equal 'empty', EmptyController.controller_path
- assert_equal EmptyController.controller_path, EmptyController.new.controller_path
- assert_equal 'submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
- assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
+ test "controller name" do
+ assert_equal 'empty', EmptyController.controller_name
+ assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
+ end
end
- def test_controller_name
- assert_equal 'empty', EmptyController.controller_name
- assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
- end
end \ No newline at end of file
diff --git a/actionpack/test/new_base/content_type_test.rb b/actionpack/test/new_base/content_type_test.rb
new file mode 100644
index 0000000000..82b817a5a3
--- /dev/null
+++ b/actionpack/test/new_base/content_type_test.rb
@@ -0,0 +1,111 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+
+module ContentType
+ class BaseController < ActionController::Base
+ def index
+ render :text => "Hello world!"
+ end
+
+ def set_on_response_obj
+ response.content_type = Mime::RSS
+ render :text => "Hello world!"
+ end
+
+ def set_on_render
+ render :text => "Hello world!", :content_type => Mime::RSS
+ end
+ end
+
+ class ImpliedController < ActionController::Base
+ # Template's mime type is used if no content_type is specified
+
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "content_type/implied/i_am_html_erb.html.erb" => "Hello world!",
+ "content_type/implied/i_am_xml_erb.xml.erb" => "<xml>Hello world!</xml>",
+ "content_type/implied/i_am_html_builder.html.builder" => "xml.p 'Hello'",
+ "content_type/implied/i_am_xml_builder.xml.builder" => "xml.awesome 'Hello'"
+ )]
+
+ def i_am_html_erb() end
+ def i_am_xml_erb() end
+ def i_am_html_builder() end
+ def i_am_xml_builder() end
+ end
+
+ class CharsetController < ActionController::Base
+ def set_on_response_obj
+ response.charset = "utf-16"
+ render :text => "Hello world!"
+ end
+
+ def set_as_nil_on_response_obj
+ response.charset = nil
+ render :text => "Hello world!"
+ end
+ end
+
+ class ExplicitContentTypeTest < SimpleRouteCase
+ test "default response is HTML and UTF8" do
+ get "/content_type/base"
+
+ assert_body "Hello world!"
+ assert_header "Content-Type", "text/html; charset=utf-8"
+ end
+
+ test "setting the content type of the response directly on the response object" do
+ get "/content_type/base/set_on_response_obj"
+
+ assert_body "Hello world!"
+ assert_header "Content-Type", "application/rss+xml; charset=utf-8"
+ end
+
+ test "setting the content type of the response as an option to render" do
+ get "/content_type/base/set_on_render"
+
+ assert_body "Hello world!"
+ assert_header "Content-Type", "application/rss+xml; charset=utf-8"
+ end
+ end
+
+ class ImpliedContentTypeTest < SimpleRouteCase
+ test "sets Content-Type as text/html when rendering *.html.erb" do
+ get "/content_type/implied/i_am_html_erb"
+
+ assert_header "Content-Type", "text/html; charset=utf-8"
+ end
+
+ test "sets Content-Type as application/xml when rendering *.xml.erb" do
+ get "/content_type/implied/i_am_xml_erb"
+
+ assert_header "Content-Type", "application/xml; charset=utf-8"
+ end
+
+ test "sets Content-Type as text/html when rendering *.html.builder" do
+ get "/content_type/implied/i_am_html_builder"
+
+ assert_header "Content-Type", "text/html; charset=utf-8"
+ end
+
+ test "sets Content-Type as application/xml when rendering *.xml.builder" do
+ get "/content_type/implied/i_am_xml_builder"
+
+ assert_header "Content-Type", "application/xml; charset=utf-8"
+ end
+ end
+
+ class ExplicitCharsetTest < SimpleRouteCase
+ test "setting the charset of the response directly on the response object" do
+ get "/content_type/charset/set_on_response_obj"
+
+ assert_body "Hello world!"
+ assert_header "Content-Type", "text/html; charset=utf-16"
+ end
+
+ test "setting the charset of the response as nil directly on the response object" do
+ get "/content_type/charset/set_as_nil_on_response_obj"
+
+ assert_body "Hello world!"
+ assert_header "Content-Type", "text/html; charset=utf-8"
+ end
+ end
+end
diff --git a/actionpack/test/new_base/etag_test.rb b/actionpack/test/new_base/etag_test.rb
new file mode 100644
index 0000000000..a40d3c936a
--- /dev/null
+++ b/actionpack/test/new_base/etag_test.rb
@@ -0,0 +1,46 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+
+module Etags
+ class BasicController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "etags/basic/base.html.erb" => "Hello from without_layout.html.erb",
+ "layouts/etags.html.erb" => "teh <%= yield %> tagz"
+ )]
+
+ def without_layout
+ render :action => "base"
+ end
+
+ def with_layout
+ render :action => "base", :layout => "etags"
+ end
+ end
+
+ class EtagTest < SimpleRouteCase
+ describe "Rendering without any special etag options returns an etag that is an MD5 hash of its text"
+
+ test "an action without a layout" do
+ get "/etags/basic/without_layout"
+
+ body = "Hello from without_layout.html.erb"
+ assert_body body
+ assert_header "Etag", etag_for(body)
+ assert_status 200
+ end
+
+ test "an action with a layout" do
+ get "/etags/basic/with_layout"
+
+ body = "teh Hello from without_layout.html.erb tagz"
+ assert_body body
+ assert_header "Etag", etag_for(body)
+ assert_status 200
+ end
+
+ private
+
+ def etag_for(text)
+ %("#{Digest::MD5.hexdigest(text)}")
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/new_base/redirect_test.rb b/actionpack/test/new_base/redirect_test.rb
new file mode 100644
index 0000000000..e591ebd05f
--- /dev/null
+++ b/actionpack/test/new_base/redirect_test.rb
@@ -0,0 +1 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") \ No newline at end of file
diff --git a/actionpack/test/new_base/render_action_test.rb b/actionpack/test/new_base/render_action_test.rb
index 2bfb374a31..4402eadf42 100644
--- a/actionpack/test/new_base/render_action_test.rb
+++ b/actionpack/test/new_base/render_action_test.rb
@@ -1,26 +1,24 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
module RenderAction
-
# This has no layout and it works
- class BasicController < ActionController::Base2
-
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ class BasicController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new(
"render_action/basic/hello_world.html.erb" => "Hello world!"
)]
-
+
def hello_world
render :action => "hello_world"
end
-
+
def hello_world_as_string
render "hello_world"
end
-
+
def hello_world_as_string_with_options
render "hello_world", :status => 404
end
-
+
def hello_world_as_symbol
render :hello_world
end
@@ -28,298 +26,295 @@ module RenderAction
def hello_world_with_symbol
render :action => :hello_world
end
-
+
def hello_world_with_layout
render :action => "hello_world", :layout => true
end
-
+
def hello_world_with_layout_false
render :action => "hello_world", :layout => false
end
-
+
def hello_world_with_layout_nil
render :action => "hello_world", :layout => nil
end
-
+
def hello_world_with_custom_layout
render :action => "hello_world", :layout => "greetings"
end
-
- end
-
- class TestBasic < SimpleRouteCase
- describe "Rendering an action using :action => <String>"
-
- get "/render_action/basic/hello_world"
- assert_body "Hello world!"
- assert_status 200
- end
-
- class TestWithString < SimpleRouteCase
- describe "Render an action using 'hello_world'"
-
- get "/render_action/basic/hello_world_as_string"
- assert_body "Hello world!"
- assert_status 200
- end
-
- class TestWithStringAndOptions < SimpleRouteCase
- describe "Render an action using 'hello_world'"
-
- get "/render_action/basic/hello_world_as_string_with_options"
- assert_body "Hello world!"
- assert_status 404
- end
-
- class TestAsSymbol < SimpleRouteCase
- describe "Render an action using :hello_world"
-
- get "/render_action/basic/hello_world_as_symbol"
- assert_body "Hello world!"
- assert_status 200
+
end
-
- class TestWithSymbol < SimpleRouteCase
- describe "Render an action using :action => :hello_world"
-
- get "/render_action/basic/hello_world_with_symbol"
- assert_body "Hello world!"
- assert_status 200
+
+ class RenderActionTest < SimpleRouteCase
+ test "rendering an action using :action => <String>" do
+ get "/render_action/basic/hello_world"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "rendering an action using '<action>'" do
+ get "/render_action/basic/hello_world_as_string"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "rendering an action using '<action>' and options" do
+ get "/render_action/basic/hello_world_as_string_with_options"
+
+ assert_body "Hello world!"
+ assert_status 404
+ end
+
+ test "rendering an action using :action" do
+ get "/render_action/basic/hello_world_as_symbol"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "rendering an action using :action => :hello_world" do
+ get "/render_action/basic/hello_world_with_symbol"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
end
-
- class TestLayoutTrue < SimpleRouteCase
- describe "rendering a normal template with full path with layout => true"
-
- test "raises an exception when requesting a layout and none exist" do
- assert_raise(ArgumentError, /no default layout for RenderAction::BasicController in/) do
- get "/render_action/basic/hello_world_with_layout"
+
+ class RenderLayoutTest < SimpleRouteCase
+ describe "Both <controller_path>.html.erb and application.html.erb are missing"
+
+ test "rendering with layout => true" do
+ assert_raise(ArgumentError, /no default layout for RenderAction::BasicController in/) do
+ get "/render_action/basic/hello_world_with_layout", {}, "action_dispatch.show_exceptions" => false
end
end
- end
-
- class TestLayoutFalse < SimpleRouteCase
- describe "rendering a normal template with full path with layout => false"
-
- get "/render_action/basic/hello_world_with_layout_false"
- assert_body "Hello world!"
- assert_status 200
- end
-
- class TestLayoutNil < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :nil"
-
- get "/render_action/basic/hello_world_with_layout_nil"
- assert_body "Hello world!"
- assert_status 200
- end
-
- class TestCustomLayout < SimpleRouteCase
- describe "rendering a normal template with full path with layout => 'greetings'"
-
- test "raises an exception when requesting a layout that does not exist" do
- assert_raise(ActionView::MissingTemplate) { get "/render_action/basic/hello_world_with_custom_layout" }
+
+ test "rendering with layout => false" do
+ get "/render_action/basic/hello_world_with_layout_false"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "rendering with layout => :nil" do
+ get "/render_action/basic/hello_world_with_layout_nil"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "rendering with layout => 'greetings'" do
+ assert_raise(ActionView::MissingTemplate) do
+ get "/render_action/basic/hello_world_with_custom_layout", {}, "action_dispatch.show_exceptions" => false
+ end
end
end
-
end
module RenderActionWithApplicationLayout
-
# # ==== Render actions with layouts ====
-
class BasicController < ::ApplicationController
# Set the view path to an application view structure with layouts
- self.view_paths = self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ self.view_paths = self.view_paths = [ActionView::Template::FixturePath.new(
"render_action_with_application_layout/basic/hello_world.html.erb" => "Hello World!",
+ "render_action_with_application_layout/basic/hello.html.builder" => "xml.p 'Omg'",
"layouts/application.html.erb" => "OHAI <%= yield %> KTHXBAI",
- "layouts/greetings.html.erb" => "Greetings <%= yield %> Bai"
+ "layouts/greetings.html.erb" => "Greetings <%= yield %> Bai",
+ "layouts/builder.html.builder" => "xml.html do\n xml << yield\nend"
)]
-
+
def hello_world
render :action => "hello_world"
end
-
+
def hello_world_with_layout
render :action => "hello_world", :layout => true
end
-
+
def hello_world_with_layout_false
render :action => "hello_world", :layout => false
end
-
+
def hello_world_with_layout_nil
render :action => "hello_world", :layout => nil
end
-
+
def hello_world_with_custom_layout
render :action => "hello_world", :layout => "greetings"
end
+
+ def with_builder_and_layout
+ render :action => "hello", :layout => "builder"
+ end
end
-
- class TestDefaultLayout < SimpleRouteCase
- describe %(
- Render hello_world and implicitly use application.html.erb as a layout if
- no layout is specified and no controller layout is present
- )
-
- get "/render_action_with_application_layout/basic/hello_world"
- assert_body "OHAI Hello World! KTHXBAI"
- assert_status 200
- end
-
- class TestLayoutTrue < SimpleRouteCase
- describe "rendering a normal template with full path with layout => true"
-
- get "/render_action_with_application_layout/basic/hello_world_with_layout"
- assert_body "OHAI Hello World! KTHXBAI"
- assert_status 200
- end
-
- class TestLayoutFalse < SimpleRouteCase
- describe "rendering a normal template with full path with layout => false"
-
- get "/render_action_with_application_layout/basic/hello_world_with_layout_false"
- assert_body "Hello World!"
- assert_status 200
- end
-
- class TestLayoutNil < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :nil"
-
- get "/render_action_with_application_layout/basic/hello_world_with_layout_nil"
- assert_body "Hello World!"
- assert_status 200
+
+ class LayoutTest < SimpleRouteCase
+ describe "Only application.html.erb is present and <controller_path>.html.erb is missing"
+
+ test "rendering implicit application.html.erb as layout" do
+ get "/render_action_with_application_layout/basic/hello_world"
+
+ assert_body "OHAI Hello World! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering with layout => true" do
+ get "/render_action_with_application_layout/basic/hello_world_with_layout"
+
+ assert_body "OHAI Hello World! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering with layout => false" do
+ get "/render_action_with_application_layout/basic/hello_world_with_layout_false"
+
+ assert_body "Hello World!"
+ assert_status 200
+ end
+
+ test "rendering with layout => :nil" do
+ get "/render_action_with_application_layout/basic/hello_world_with_layout_nil"
+
+ assert_body "Hello World!"
+ assert_status 200
+ end
+
+ test "rendering with layout => 'greetings'" do
+ get "/render_action_with_application_layout/basic/hello_world_with_custom_layout"
+
+ assert_body "Greetings Hello World! Bai"
+ assert_status 200
+ end
end
-
- class TestCustomLayout < SimpleRouteCase
- describe "rendering a normal template with full path with layout => 'greetings'"
-
- get "/render_action_with_application_layout/basic/hello_world_with_custom_layout"
- assert_body "Greetings Hello World! Bai"
- assert_status 200
+
+ class TestLayout < SimpleRouteCase
+ testing BasicController
+
+ test "builder works with layouts" do
+ get :with_builder_and_layout
+ assert_response "<html>\n<p>Omg</p>\n</html>\n"
+ end
end
-
+
end
module RenderActionWithControllerLayout
-
- class BasicController < ActionController::Base2
- self.view_paths = self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ class BasicController < ActionController::Base
+ self.view_paths = self.view_paths = [ActionView::Template::FixturePath.new(
"render_action_with_controller_layout/basic/hello_world.html.erb" => "Hello World!",
"layouts/render_action_with_controller_layout/basic.html.erb" => "With Controller Layout! <%= yield %> KTHXBAI"
)]
-
+
def hello_world
render :action => "hello_world"
end
-
+
def hello_world_with_layout
render :action => "hello_world", :layout => true
end
-
+
def hello_world_with_layout_false
render :action => "hello_world", :layout => false
end
-
+
def hello_world_with_layout_nil
render :action => "hello_world", :layout => nil
end
-
+
def hello_world_with_custom_layout
render :action => "hello_world", :layout => "greetings"
end
end
-
- class TestControllerLayout < SimpleRouteCase
- describe "Render hello_world and implicitly use <controller_path>.html.erb as a layout."
- get "/render_action_with_controller_layout/basic/hello_world"
- assert_body "With Controller Layout! Hello World! KTHXBAI"
- assert_status 200
- end
-
- class TestLayoutTrue < SimpleRouteCase
- describe "rendering a normal template with full path with layout => true"
-
- get "/render_action_with_controller_layout/basic/hello_world_with_layout"
- assert_body "With Controller Layout! Hello World! KTHXBAI"
- assert_status 200
- end
-
- class TestLayoutFalse < SimpleRouteCase
- describe "rendering a normal template with full path with layout => false"
-
- get "/render_action_with_controller_layout/basic/hello_world_with_layout_false"
- assert_body "Hello World!"
- assert_status 200
- end
-
- class TestLayoutNil < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :nil"
-
- get "/render_action_with_controller_layout/basic/hello_world_with_layout_nil"
- assert_body "Hello World!"
- assert_status 200
+ class ControllerLayoutTest < SimpleRouteCase
+ describe "Only <controller_path>.html.erb is present and application.html.erb is missing"
+
+ test "render hello_world and implicitly use <controller_path>.html.erb as a layout." do
+ get "/render_action_with_controller_layout/basic/hello_world"
+
+ assert_body "With Controller Layout! Hello World! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering with layout => true" do
+ get "/render_action_with_controller_layout/basic/hello_world_with_layout"
+
+ assert_body "With Controller Layout! Hello World! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering with layout => false" do
+ get "/render_action_with_controller_layout/basic/hello_world_with_layout_false"
+
+ assert_body "Hello World!"
+ assert_status 200
+ end
+
+ test "rendering with layout => :nil" do
+ get "/render_action_with_controller_layout/basic/hello_world_with_layout_nil"
+
+ assert_body "Hello World!"
+ assert_status 200
+ end
end
-
end
module RenderActionWithBothLayouts
-
- class BasicController < ActionController::Base2
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new({
+ class BasicController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new({
"render_action_with_both_layouts/basic/hello_world.html.erb" => "Hello World!",
"layouts/application.html.erb" => "OHAI <%= yield %> KTHXBAI",
"layouts/render_action_with_both_layouts/basic.html.erb" => "With Controller Layout! <%= yield %> KTHXBAI"
})]
-
+
def hello_world
render :action => "hello_world"
end
-
+
def hello_world_with_layout
render :action => "hello_world", :layout => true
end
-
+
def hello_world_with_layout_false
render :action => "hello_world", :layout => false
end
-
+
def hello_world_with_layout_nil
render :action => "hello_world", :layout => nil
end
end
-
- class TestControllerLayoutFirst < SimpleRouteCase
- describe "Render hello_world and implicitly use <controller_path>.html.erb over application.html.erb as a layout"
- get "/render_action_with_both_layouts/basic/hello_world"
- assert_body "With Controller Layout! Hello World! KTHXBAI"
- assert_status 200
- end
-
- class TestLayoutTrue < SimpleRouteCase
- describe "rendering a normal template with full path with layout => true"
-
- get "/render_action_with_both_layouts/basic/hello_world_with_layout"
- assert_body "With Controller Layout! Hello World! KTHXBAI"
- assert_status 200
- end
-
- class TestLayoutFalse < SimpleRouteCase
- describe "rendering a normal template with full path with layout => false"
-
- get "/render_action_with_both_layouts/basic/hello_world_with_layout_false"
- assert_body "Hello World!"
- assert_status 200
- end
-
- class TestLayoutNil < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :nil"
-
- get "/render_action_with_both_layouts/basic/hello_world_with_layout_nil"
- assert_body "Hello World!"
- assert_status 200
+ class ControllerLayoutTest < SimpleRouteCase
+ describe "Both <controller_path>.html.erb and application.html.erb are present"
+
+ test "rendering implicitly use <controller_path>.html.erb over application.html.erb as a layout" do
+ get "/render_action_with_both_layouts/basic/hello_world"
+
+ assert_body "With Controller Layout! Hello World! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering with layout => true" do
+ get "/render_action_with_both_layouts/basic/hello_world_with_layout"
+
+ assert_body "With Controller Layout! Hello World! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering with layout => false" do
+ get "/render_action_with_both_layouts/basic/hello_world_with_layout_false"
+
+ assert_body "Hello World!"
+ assert_status 200
+ end
+
+ test "rendering with layout => :nil" do
+ get "/render_action_with_both_layouts/basic/hello_world_with_layout_nil"
+
+ assert_body "Hello World!"
+ assert_status 200
+ end
end
-
end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_file_test.rb b/actionpack/test/new_base/render_file_test.rb
new file mode 100644
index 0000000000..769949be0c
--- /dev/null
+++ b/actionpack/test/new_base/render_file_test.rb
@@ -0,0 +1,110 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+
+module RenderFile
+
+ class BasicController < ActionController::Base
+ self.view_paths = "."
+
+ def index
+ render :file => File.join(File.dirname(__FILE__), *%w[.. fixtures test hello_world])
+ end
+
+ def with_instance_variables
+ @secret = 'in the sauce'
+ render :file => File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb')
+ end
+
+ def without_file_key
+ render File.join(File.dirname(__FILE__), *%w[.. fixtures test hello_world])
+ end
+
+ def without_file_key_with_instance_variable
+ @secret = 'in the sauce'
+ render File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb')
+ end
+
+ def relative_path
+ @secret = 'in the sauce'
+ render :file => '../fixtures/test/render_file_with_ivar'
+ end
+
+ def relative_path_with_dot
+ @secret = 'in the sauce'
+ render :file => '../fixtures/test/dot.directory/render_file_with_ivar'
+ end
+
+ def pathname
+ @secret = 'in the sauce'
+ render :file => Pathname.new(File.dirname(__FILE__)).join(*%w[.. fixtures test dot.directory render_file_with_ivar.erb])
+ end
+
+ def with_locals
+ path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb')
+ render :file => path, :locals => {:secret => 'in the sauce'}
+ end
+
+ def without_file_key_with_locals
+ path = File.expand_path('../fixtures/test/render_file_with_locals.erb')
+ render path, :locals => {:secret => 'in the sauce'}
+ end
+ end
+
+ class TestBasic < SimpleRouteCase
+ testing RenderFile::BasicController
+
+ def setup
+ @old_pwd = Dir.pwd
+ Dir.chdir(File.dirname(__FILE__))
+ end
+
+ def teardown
+ Dir.chdir(@old_pwd)
+ end
+
+ test "rendering simple template" do
+ get :index
+ assert_response "Hello world!"
+ end
+
+ test "rendering template with ivar" do
+ get :with_instance_variables
+ assert_response "The secret is in the sauce\n"
+ end
+
+ test "rendering path without specifying the :file key" do
+ get :without_file_key
+ assert_response "Hello world!"
+ end
+
+ test "rendering path without specifying the :file key with ivar" do
+ get :without_file_key_with_instance_variable
+ assert_response "The secret is in the sauce\n"
+ end
+
+ test "rendering a relative path" do
+ get :relative_path
+ assert_response "The secret is in the sauce\n"
+ end
+
+ test "rendering a relative path with dot" do
+ get :relative_path_with_dot
+ assert_response "The secret is in the sauce\n"
+ end
+
+ test "rendering a Pathname" do
+ get :pathname
+ assert_response "The secret is in the sauce\n"
+ end
+
+ test "rendering file with locals" do
+ get :with_locals
+ assert_response "The secret is in the sauce\n"
+ end
+
+ test "rendering path without specifying the :file key with locals" do
+ get :without_file_key_with_locals
+ assert_response "The secret is in the sauce\n"
+ end
+ end
+
+end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_implicit_action_test.rb b/actionpack/test/new_base/render_implicit_action_test.rb
index 798505b539..2846df48da 100644
--- a/actionpack/test/new_base/render_implicit_action_test.rb
+++ b/actionpack/test/new_base/render_implicit_action_test.rb
@@ -1,16 +1,28 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
-module HappyPath
-
- class RenderImplicitActionController < ActionController::Base2
- # No actions yet, they are implicit
+module RenderImplicitAction
+ class SimpleController < ::ApplicationController
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "render_implicit_action/simple/hello_world.html.erb" => "Hello world!",
+ "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!"
+ )]
+
+ def hello_world() end
end
- class TestRendersActionImplicitly < SimpleRouteCase
-
- test "renders action implicitly" do
- assert true
+ class RenderImplicitActionTest < SimpleRouteCase
+ test "render a simple action with new explicit call to render" do
+ get "/render_implicit_action/simple/hello_world"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "render an action with a missing method and has special characters" do
+ get "/render_implicit_action/simple/hyphen-ated"
+
+ assert_body "Hello hyphen-ated!"
+ assert_status 200
end
-
end
end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_layout_test.rb b/actionpack/test/new_base/render_layout_test.rb
index facf67ea85..f32c60d683 100644
--- a/actionpack/test/new_base/render_layout_test.rb
+++ b/actionpack/test/new_base/render_layout_test.rb
@@ -2,44 +2,100 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
module ControllerLayouts
class ImplicitController < ::ApplicationController
-
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ self.view_paths = [ActionView::Template::FixturePath.new(
"layouts/application.html.erb" => "OMG <%= yield %> KTHXBAI",
- "basic.html.erb" => "Hello world!"
+ "layouts/override.html.erb" => "Override! <%= yield %>",
+ "basic.html.erb" => "Hello world!",
+ "controller_layouts/implicit/layout_false.html.erb" => "hai(layout_false.html.erb)"
)]
-
+
def index
render :template => "basic"
end
+
+ def override
+ render :template => "basic", :layout => "override"
+ end
+
+ def layout_false
+ render :layout => false
+ end
+
+ def builder_override
+ end
end
-
- class TestImplicitLayout < SimpleRouteCase
- describe "rendering a normal template, but using the implicit layout"
-
- get "/controller_layouts/implicit/index"
- assert_body "OMG Hello world! KTHXBAI"
- assert_status 200
- end
-
+
class ImplicitNameController < ::ApplicationController
-
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ self.view_paths = [ActionView::Template::FixturePath.new(
"layouts/controller_layouts/implicit_name.html.erb" => "OMGIMPLICIT <%= yield %> KTHXBAI",
"basic.html.erb" => "Hello world!"
)]
-
+
def index
render :template => "basic"
end
end
-
- class TestImplicitNamedLayout < SimpleRouteCase
- describe "rendering a normal template, but using an implicit NAMED layout"
-
- get "/controller_layouts/implicit_name/index"
- assert_body "OMGIMPLICIT Hello world! KTHXBAI"
- assert_status 200
+
+ class RenderLayoutTest < SimpleRouteCase
+ test "rendering a normal template, but using the implicit layout" do
+ get "/controller_layouts/implicit/index"
+
+ assert_body "OMG Hello world! KTHXBAI"
+ assert_status 200
+ end
+
+ test "rendering a normal template, but using an implicit NAMED layout" do
+ get "/controller_layouts/implicit_name/index"
+
+ assert_body "OMGIMPLICIT Hello world! KTHXBAI"
+ assert_status 200
+ end
+
+ test "overriding an implicit layout with render :layout option" do
+ get "/controller_layouts/implicit/override"
+ assert_body "Override! Hello world!"
+ end
+
+ end
+
+ class LayoutOptionsTest < SimpleRouteCase
+ testing ControllerLayouts::ImplicitController
+
+ test "rendering with :layout => false leaves out the implicit layout" do
+ get :layout_false
+ assert_response "hai(layout_false.html.erb)"
+ end
+ end
+
+ class MismatchFormatController < ::ApplicationController
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "layouts/application.html.erb" => "<html><%= yield %></html>",
+ "controller_layouts/mismatch_format/index.js.rjs" => "page[:test].omg",
+ "controller_layouts/mismatch_format/implicit.rjs" => "page[:test].omg"
+ )]
+
+ def explicit
+ render :layout => "application"
+ end
+ end
+
+ class MismatchFormatTest < SimpleRouteCase
+ testing ControllerLayouts::MismatchFormatController
+
+ test "if JS is selected, an HTML template is not also selected" do
+ get :index
+ assert_response "$(\"test\").omg();"
+ end
+
+ test "if JS is implicitly selected, an HTML template is not also selected" do
+ get :implicit
+ assert_response "$(\"test\").omg();"
+ end
+
+ test "if an HTML template is explicitly provides for a JS template, an error is raised" do
+ assert_raises ActionView::MissingTemplate do
+ get :explicit, {}, "action_dispatch.show_exceptions" => false
+ end
+ end
end
-
-
end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_partial_test.rb b/actionpack/test/new_base/render_partial_test.rb
new file mode 100644
index 0000000000..3a300afe5c
--- /dev/null
+++ b/actionpack/test/new_base/render_partial_test.rb
@@ -0,0 +1,27 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+
+module RenderPartial
+
+ class BasicController < ActionController::Base
+
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "render_partial/basic/_basic.html.erb" => "OMG!",
+ "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>"
+ )]
+
+ def changing
+ @test_unchanged = 'hello'
+ render :action => "basic"
+ end
+ end
+
+ class TestPartial < SimpleRouteCase
+ testing BasicController
+
+ test "rendering a partial in ActionView doesn't pull the ivars again from the controller" do
+ get :changing
+ assert_response("goodbyeOMG!goodbye")
+ end
+ end
+
+end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_template_test.rb b/actionpack/test/new_base/render_template_test.rb
index c6c0269b40..face5b7571 100644
--- a/actionpack/test/new_base/render_template_test.rb
+++ b/actionpack/test/new_base/render_template_test.rb
@@ -1,133 +1,170 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
-module HappyPath
-
- class RenderTemplateWithoutLayoutController < ActionController::Base2
-
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
- "test/basic.html.erb" => "Hello from basic.html.erb",
- "shared.html.erb" => "Elastica"
+module RenderTemplate
+ class WithoutLayoutController < ActionController::Base
+
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "test/basic.html.erb" => "Hello from basic.html.erb",
+ "shared.html.erb" => "Elastica",
+ "locals.html.erb" => "The secret is <%= secret %>",
+ "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend"
)]
- def render_hello_world
+ def index
render :template => "test/basic"
end
-
- def render_hello_world_with_forward_slash
- render :template => "/test/basic"
+
+ def index_without_key
+ render "test/basic"
end
- def render_template_in_top_directory
+ def in_top_directory
render :template => 'shared'
end
- def render_template_in_top_directory_with_slash
+ def in_top_directory_with_slash
render :template => '/shared'
end
- end
-
- class TestTemplateRenderWithoutLayout < SimpleRouteCase
- describe "rendering a normal template with full path without layout"
- get "/happy_path/render_template_without_layout/render_hello_world"
- assert_body "Hello from basic.html.erb"
- assert_status 200
- end
-
- class TestTemplateRenderWithForwardSlash < SimpleRouteCase
- describe "rendering a normal template with full path starting with a leading slash"
+ def in_top_directory_with_slash_without_key
+ render '/shared'
+ end
+
+ def with_locals
+ render :template => "locals", :locals => { :secret => 'area51' }
+ end
- get "/happy_path/render_template_without_layout/render_hello_world_with_forward_slash"
- assert_body "Hello from basic.html.erb"
- assert_status 200
+ def builder_template
+ render :template => "xml_template"
+ end
end
- class TestTemplateRenderInTopDirectory < SimpleRouteCase
- describe "rendering a template not in a subdirectory"
+ class TestWithoutLayout < SimpleRouteCase
+ testing RenderTemplate::WithoutLayoutController
- get "/happy_path/render_template_without_layout/render_template_in_top_directory"
- assert_body "Elastica"
- assert_status 200
- end
-
- class TestTemplateRenderInTopDirectoryWithSlash < SimpleRouteCase
- describe "rendering a template not in a subdirectory with a leading slash"
+ test "rendering a normal template with full path without layout" do
+ get :index
+ assert_response "Hello from basic.html.erb"
+ end
- get "/happy_path/render_template_without_layout/render_template_in_top_directory_with_slash"
- assert_body "Elastica"
- assert_status 200
- end
+ test "rendering a normal template with full path without layout without key" do
+ get :index_without_key
+ assert_response "Hello from basic.html.erb"
+ end
+
+ test "rendering a template not in a subdirectory" do
+ get :in_top_directory
+ assert_response "Elastica"
+ end
+
+ test "rendering a template not in a subdirectory with a leading slash" do
+ get :in_top_directory_with_slash
+ assert_response "Elastica"
+ end
+
+ test "rendering a template not in a subdirectory with a leading slash without key" do
+ get :in_top_directory_with_slash_without_key
+ assert_response "Elastica"
+ end
+
+ test "rendering a template with local variables" do
+ get :with_locals
+ assert_response "The secret is area51"
+ end
- class RenderTemplateWithLayoutController < ::ApplicationController
+ test "rendering a builder template" do
+ get :builder_template
+ assert_response "<html>\n <p>Hello</p>\n</html>\n"
+ end
+ end
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+ class WithLayoutController < ::ApplicationController
+ self.view_paths = [ActionView::Template::FixturePath.new(
"test/basic.html.erb" => "Hello from basic.html.erb",
"shared.html.erb" => "Elastica",
"layouts/application.html.erb" => "<%= yield %>, I'm here!",
"layouts/greetings.html.erb" => "<%= yield %>, I wish thee well."
)]
- def render_hello_world
+ def index
render :template => "test/basic"
end
- def render_hello_world_with_layout
+ def with_layout
render :template => "test/basic", :layout => true
end
- def render_hello_world_with_layout_false
+ def with_layout_false
render :template => "test/basic", :layout => false
end
- def render_hello_world_with_layout_nil
+ def with_layout_nil
render :template => "test/basic", :layout => nil
end
- def render_hello_world_with_custom_layout
+ def with_custom_layout
render :template => "test/basic", :layout => "greetings"
end
end
- class TestTemplateRenderWithLayout < SimpleRouteCase
- describe "rendering a normal template with full path with layout"
-
- get "/happy_path/render_template_with_layout/render_hello_world"
- assert_body "Hello from basic.html.erb, I'm here!"
- assert_status 200
- end
-
- class TestTemplateRenderWithLayoutTrue < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :true"
-
- get "/happy_path/render_template_with_layout/render_hello_world_with_layout"
- assert_body "Hello from basic.html.erb, I'm here!"
- assert_status 200
- end
-
- class TestTemplateRenderWithLayoutFalse < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :false"
-
- get "/happy_path/render_template_with_layout/render_hello_world_with_layout_false"
- assert_body "Hello from basic.html.erb"
- assert_status 200
- end
-
- class TestTemplateRenderWithLayoutNil < SimpleRouteCase
- describe "rendering a normal template with full path with layout => :nil"
-
- get "/happy_path/render_template_with_layout/render_hello_world_with_layout_nil"
- assert_body "Hello from basic.html.erb"
- assert_status 200
- end
-
- class TestTemplateRenderWithCustomLayout < SimpleRouteCase
- describe "rendering a normal template with full path with layout => 'greetings'"
-
- get "/happy_path/render_template_with_layout/render_hello_world_with_custom_layout"
- assert_body "Hello from basic.html.erb, I wish thee well."
- assert_status 200
+ class TestWithLayout < SimpleRouteCase
+ describe "Rendering with :template using implicit or explicit layout"
+
+ test "rendering with implicit layout" do
+ get "/render_template/with_layout"
+
+ assert_body "Hello from basic.html.erb, I'm here!"
+ assert_status 200
+ end
+
+ test "rendering with layout => :true" do
+ get "/render_template/with_layout/with_layout"
+
+ assert_body "Hello from basic.html.erb, I'm here!"
+ assert_status 200
+ end
+
+ test "rendering with layout => :false" do
+ get "/render_template/with_layout/with_layout_false"
+
+ assert_body "Hello from basic.html.erb"
+ assert_status 200
+ end
+
+ test "rendering with layout => :nil" do
+ get "/render_template/with_layout/with_layout_nil"
+
+ assert_body "Hello from basic.html.erb"
+ assert_status 200
+ end
+
+ test "rendering layout => 'greetings'" do
+ get "/render_template/with_layout/with_custom_layout"
+
+ assert_body "Hello from basic.html.erb, I wish thee well."
+ assert_status 200
+ end
end
-
-
+ module Compatibility
+ class WithoutLayoutController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "test/basic.html.erb" => "Hello from basic.html.erb",
+ "shared.html.erb" => "Elastica"
+ )]
+
+ def with_forward_slash
+ render :template => "/test/basic"
+ end
+ end
+
+ class TestTemplateRenderWithForwardSlash < SimpleRouteCase
+ test "rendering a normal template with full path starting with a leading slash" do
+ get "/render_template/compatibility/without_layout/with_forward_slash"
+
+ assert_body "Hello from basic.html.erb"
+ assert_status 200
+ end
+ end
+ end
end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_test.rb b/actionpack/test/new_base/render_test.rb
new file mode 100644
index 0000000000..ed3d50fa0b
--- /dev/null
+++ b/actionpack/test/new_base/render_test.rb
@@ -0,0 +1,85 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+
+module Render
+ class BlankRenderController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "render/blank_render/index.html.erb" => "Hello world!",
+ "render/blank_render/access_request.html.erb" => "The request: <%= request.method.to_s.upcase %>",
+ "render/blank_render/access_action_name.html.erb" => "Action Name: <%= action_name %>",
+ "render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>"
+ )]
+
+ def index
+ render
+ end
+
+ def access_request
+ render :action => "access_request"
+ end
+
+ def render_action_name
+ render :action => "access_action_name"
+ end
+
+ private
+
+ def secretz
+ render :text => "FAIL WHALE!"
+ end
+ end
+
+ class DoubleRenderController < ActionController::Base
+ def index
+ render :text => "hello"
+ render :text => "world"
+ end
+ end
+
+ class RenderTest < SimpleRouteCase
+ test "render with blank" do
+ get "/render/blank_render"
+
+ assert_body "Hello world!"
+ assert_status 200
+ end
+
+ test "rendering more than once raises an exception" do
+ assert_raises(AbstractController::DoubleRenderError) do
+ get "/render/double_render", {}, "action_dispatch.show_exceptions" => false
+ end
+ end
+ end
+
+ class TestOnlyRenderPublicActions < SimpleRouteCase
+ describe "Only public methods on actual controllers are callable actions"
+
+ test "raises an exception when a method of Object is called" do
+ assert_raises(AbstractController::ActionNotFound) do
+ get "/render/blank_render/clone", {}, "action_dispatch.show_exceptions" => false
+ end
+ end
+
+ test "raises an exception when a private method is called" do
+ assert_raises(AbstractController::ActionNotFound) do
+ get "/render/blank_render/secretz", {}, "action_dispatch.show_exceptions" => false
+ end
+ end
+ end
+
+ class TestVariousObjectsAvailableInView < SimpleRouteCase
+ test "The request object is accessible in the view" do
+ get "/render/blank_render/access_request"
+ assert_body "The request: GET"
+ end
+
+ test "The action_name is accessible in the view" do
+ get "/render/blank_render/render_action_name"
+ assert_body "Action Name: render_action_name"
+ end
+
+ test "The controller_name is accessible in the view" do
+ get "/render/blank_render/access_controller_name"
+ assert_body "Controller Name: blank_render"
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/new_base/render_text_test.rb b/actionpack/test/new_base/render_text_test.rb
index a20ca5fb8c..ffc149283b 100644
--- a/actionpack/test/new_base/render_text_test.rb
+++ b/actionpack/test/new_base/render_text_test.rb
@@ -1,144 +1,139 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
-class ApplicationController < ActionController::Base2
-end
-
-module HappyPath
-
- class RenderTextWithoutLayoutsController < ActionController::Base2
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new]
+module RenderText
+ class SimpleController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new]
- def render_hello_world
+ def index
render :text => "hello david"
end
end
-
- class RenderTextWithLayoutsController < ::ApplicationController
- self.view_paths = [ActionView::FixtureTemplate::FixturePath.new(
+
+ class WithLayoutController < ::ApplicationController
+ self.view_paths = [ActionView::Template::FixturePath.new(
"layouts/application.html.erb" => "<%= yield %>, I'm here!",
- "layouts/greetings.html.erb" => "<%= yield %>, I wish thee well."
+ "layouts/greetings.html.erb" => "<%= yield %>, I wish thee well.",
+ "layouts/ivar.html.erb" => "<%= yield %>, <%= @ivar %>"
)]
- def render_hello_world
+ def index
render :text => "hello david"
end
- def render_custom_code
+ def custom_code
render :text => "hello world", :status => 404
end
- def render_with_custom_code_as_string
+ def with_custom_code_as_string
render :text => "hello world", :status => "404 Not Found"
end
- def render_text_with_nil
+ def with_nil
render :text => nil
end
- def render_text_with_nil_and_status
+ def with_nil_and_status
render :text => nil, :status => 403
end
- def render_text_with_false
+ def with_false
render :text => false
end
- def render_text_with_layout
+ def with_layout_true
render :text => "hello world", :layout => true
end
- def render_text_with_layout_false
+ def with_layout_false
render :text => "hello world", :layout => false
end
- def render_text_with_layout_nil
+ def with_layout_nil
render :text => "hello world", :layout => nil
end
- def render_text_with_custom_layout
+ def with_custom_layout
render :text => "hello world", :layout => "greetings"
end
- end
-
- class TestSimpleTextRenderWithNoLayout < SimpleRouteCase
- describe "Rendering text from a action with default options renders the text with the layout"
-
- get "/happy_path/render_text_without_layouts/render_hello_world"
- assert_body "hello david"
- assert_status 200
- end
-
- class TestSimpleTextRenderWithLayout < SimpleRouteCase
- describe "Rendering text from a action with default options renders the text without the layout"
-
- get "/happy_path/render_text_with_layouts/render_hello_world"
- assert_body "hello david"
- assert_status 200
- end
-
- class TestTextRenderWithStatus < SimpleRouteCase
- describe "Rendering text, while also providing a custom status code"
-
- get "/happy_path/render_text_with_layouts/render_custom_code"
- assert_body "hello world"
- assert_status 404
- end
-
- class TestTextRenderWithNil < SimpleRouteCase
- describe "Rendering text with nil returns a single space character"
-
- get "/happy_path/render_text_with_layouts/render_text_with_nil"
- assert_body " "
- assert_status 200
- end
-
- class TestTextRenderWithNilAndStatus < SimpleRouteCase
- describe "Rendering text with nil and custom status code returns a single space character with the status"
-
- get "/happy_path/render_text_with_layouts/render_text_with_nil_and_status"
- assert_body " "
- assert_status 403
- end
-
- class TestTextRenderWithFalse < SimpleRouteCase
- describe "Rendering text with false returns the string 'false'"
-
- get "/happy_path/render_text_with_layouts/render_text_with_false"
- assert_body "false"
- assert_status 200
- end
-
- class TestTextRenderWithLayoutTrue < SimpleRouteCase
- describe "Rendering text with :layout => true"
-
- get "/happy_path/render_text_with_layouts/render_text_with_layout"
- assert_body "hello world, I'm here!"
- assert_status 200
- end
-
- class TestTextRenderWithCustomLayout < SimpleRouteCase
- describe "Rendering text with :layout => 'greetings'"
-
- get "/happy_path/render_text_with_layouts/render_text_with_custom_layout"
- assert_body "hello world, I wish thee well."
- assert_status 200
- end
-
- class TestTextRenderWithLayoutFalse < SimpleRouteCase
- describe "Rendering text with :layout => false"
- get "/happy_path/render_text_with_layouts/render_text_with_layout_false"
- assert_body "hello world"
- assert_status 200
+ def with_ivar_in_layout
+ @ivar = "hello world"
+ render :text => "hello world", :layout => "ivar"
+ end
end
-
- class TestTextRenderWithLayoutNil < SimpleRouteCase
- describe "Rendering text with :layout => nil"
-
- get "/happy_path/render_text_with_layouts/render_text_with_layout_nil"
- assert_body "hello world"
- assert_status 200
+
+ class RenderTextTest < SimpleRouteCase
+ describe "Rendering text using render :text"
+
+ test "rendering text from a action with default options renders the text with the layout" do
+ get "/render_text/simple"
+ assert_body "hello david"
+ assert_status 200
+ end
+
+ test "rendering text from a action with default options renders the text without the layout" do
+ get "/render_text/with_layout"
+
+ assert_body "hello david"
+ assert_status 200
+ end
+
+ test "rendering text, while also providing a custom status code" do
+ get "/render_text/with_layout/custom_code"
+
+ assert_body "hello world"
+ assert_status 404
+ end
+
+ test "rendering text with nil returns an empty body padded for Safari" do
+ get "/render_text/with_layout/with_nil"
+
+ assert_body " "
+ assert_status 200
+ end
+
+ test "Rendering text with nil and custom status code returns an empty body padded for Safari and the status" do
+ get "/render_text/with_layout/with_nil_and_status"
+
+ assert_body " "
+ assert_status 403
+ end
+
+ test "rendering text with false returns the string 'false'" do
+ get "/render_text/with_layout/with_false"
+
+ assert_body "false"
+ assert_status 200
+ end
+
+ test "rendering text with :layout => true" do
+ get "/render_text/with_layout/with_layout_true"
+
+ assert_body "hello world, I'm here!"
+ assert_status 200
+ end
+
+ test "rendering text with :layout => 'greetings'" do
+ get "/render_text/with_layout/with_custom_layout"
+
+ assert_body "hello world, I wish thee well."
+ assert_status 200
+ end
+
+ test "rendering text with :layout => false" do
+ get "/render_text/with_layout/with_layout_false"
+
+ assert_body "hello world"
+ assert_status 200
+ end
+
+ test "rendering text with :layout => nil" do
+ get "/render_text/with_layout/with_layout_nil"
+
+ assert_body "hello world"
+ assert_status 200
+ end
end
end
-ActionController::Base2.app_loaded! \ No newline at end of file
+ActionController::Base.app_loaded!
diff --git a/actionpack/test/new_base/render_xml_test.rb b/actionpack/test/new_base/render_xml_test.rb
new file mode 100644
index 0000000000..e6c40b1533
--- /dev/null
+++ b/actionpack/test/new_base/render_xml_test.rb
@@ -0,0 +1,11 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
+
+module RenderXml
+
+ # This has no layout and it works
+ class BasicController < ActionController::Base
+ self.view_paths = [ActionView::Template::FixturePath.new(
+ "render_xml/basic/with_render_erb" => "Hello world!"
+ )]
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/new_base/test_helper.rb b/actionpack/test/new_base/test_helper.rb
index d29449ddc1..d92029df7f 100644
--- a/actionpack/test/new_base/test_helper.rb
+++ b/actionpack/test/new_base/test_helper.rb
@@ -5,8 +5,7 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
require 'test/unit'
require 'active_support'
require 'active_support/test_case'
-require 'action_controller'
-require 'action_view/base'
+require 'action_view'
require 'fixture_template'
begin
@@ -21,118 +20,85 @@ require 'action_controller/abstract'
require 'action_controller/new_base'
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
-require 'rubygems'
-require 'rack/test'
-
-module ActionController
- class Base2 < AbstractBase
- use AbstractController::Callbacks
- use AbstractController::Helpers
- use AbstractController::Logger
-
- use ActionController::HideActions
- use ActionController::UrlFor
- use ActionController::Renderer
- use ActionController::Layouts
-
- def self.inherited(klass)
- ::ActionController::Base2.subclasses << klass.to_s
- super
- end
-
- def self.subclasses
- @subclasses ||= []
- end
-
- def self.app_loaded!
- @subclasses.each do |subclass|
- subclass.constantize._write_layout_method
- end
- end
-
- # append_view_path File.join(File.dirname(__FILE__), '..', 'fixtures')
-
- CORE_METHODS = self.public_instance_methods
+require 'action_controller/testing/process'
+require 'action_controller/testing/integration'
+
+module Rails
+ def self.env
+ x = Object.new
+ def x.test?() true end
+ x
end
end
# Temporary base class
-class Rack::TestCase < ActiveSupport::TestCase
- include Rack::Test::Methods
-
+class Rack::TestCase < ActionController::IntegrationTest
setup do
ActionController::Base.session_options[:key] = "abc"
ActionController::Base.session_options[:secret] = ("*" * 30)
-
- controllers = ActionController::Base2.subclasses.map do |k|
+
+ controllers = ActionController::Base.subclasses.map do |k|
k.underscore.sub(/_controller$/, '')
end
-
+
ActionController::Routing.use_controllers!(controllers)
-
+
# Move into a bootloader
- AbstractController::Base.subclasses.each do |klass|
+ ActionController::Base.subclasses.each do |klass|
klass = klass.constantize
next unless klass < AbstractController::Layouts
klass.class_eval do
_write_layout_method
end
- end
+ end
end
-
+
def app
@app ||= ActionController::Dispatcher.new
end
-
- def self.get(url)
- setup do |test|
- test.get url
+
+ def self.testing(klass = nil)
+ if klass
+ @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '')
+ else
+ @testing
end
end
-
- def assert_body(body)
- assert_equal [body], last_response.body
- end
-
- def self.assert_body(body)
- test "body is set to '#{body}'" do
- assert_body body
+
+ def get(thing, *args)
+ if thing.is_a?(Symbol)
+ super("#{self.class.testing}/#{thing}", *args)
+ else
+ super
end
end
-
+
+ def assert_body(body)
+ assert_equal body, Array.wrap(response.body).join
+ end
+
def assert_status(code)
- assert_equal code, last_response.status
+ assert_equal code, response.status
end
-
- def self.assert_status(code)
- test "status code is set to #{code}" do
- assert_status code
+
+ def assert_response(body, status = 200, headers = {})
+ assert_body body
+ assert_status status
+ headers.each do |header, value|
+ assert_header header, value
end
end
-
+
def assert_content_type(type)
- assert_equal type, last_response.headers["Content-Type"]
- end
-
- def self.assert_content_type(type)
- test "content type is set to #{type}" do
- assert_content_type(type)
- end
+ assert_equal type, response.headers["Content-Type"]
end
-
+
def assert_header(name, value)
- assert_equal value, last_response.headers[name]
- end
-
- def self.assert_header(name, value)
- test "'#{name}' header is set to #{value.inspect}" do
- assert_header(name, value)
- end
+ assert_equal value, response.headers[name]
end
-
end
-class ::ApplicationController < ActionController::Base2
+class ::ApplicationController < ActionController::Base
end
class SimpleRouteCase < Rack::TestCase
diff --git a/actionpack/test/template/body_parts_test.rb b/actionpack/test/template/body_parts_test.rb
index 4c82b75cdc..4e7aa63f96 100644
--- a/actionpack/test/template/body_parts_test.rb
+++ b/actionpack/test/template/body_parts_test.rb
@@ -4,9 +4,12 @@ class BodyPartsTest < ActionController::TestCase
RENDERINGS = [Object.new, Object.new, Object.new]
class TestController < ActionController::Base
+ def performed?
+ defined?(ActionController::Http) ? true : super
+ end
def index
RENDERINGS.each do |rendering|
- response.template.punctuate_body! rendering
+ @template.punctuate_body! rendering
end
@performed_render = true
end
@@ -16,7 +19,11 @@ class BodyPartsTest < ActionController::TestCase
def test_body_parts
get :index
- assert_equal RENDERINGS, @response.body_parts
+ pending(:old_base) do
+ # TestProcess buffers body_parts into body
+ # TODO: Rewrite test w/o going through process
+ assert_equal RENDERINGS, @response.body_parts
+ end
assert_equal RENDERINGS.join, @response.body
end
end
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
index c75e29ed9a..b29b03f99d 100644
--- a/actionpack/test/template/compiled_templates_test.rb
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -5,37 +5,13 @@ class CompiledTemplatesTest < Test::Unit::TestCase
def setup
@compiled_templates = ActionView::Base::CompiledTemplates
@compiled_templates.instance_methods.each do |m|
- @compiled_templates.send(:remove_method, m) if m =~ /^_run_/
+ @compiled_templates.send(:remove_method, m) if m =~ /^_render_template_/
end
end
-
- def test_template_gets_compiled
- assert_equal 0, @compiled_templates.instance_methods.size
- assert_equal "Hello world!", render(:file => "test/hello_world.erb")
- assert_equal 1, @compiled_templates.instance_methods.size
- end
-
+
def test_template_gets_recompiled_when_using_different_keys_in_local_assigns
- assert_equal 0, @compiled_templates.instance_methods.size
- assert_equal "Hello world!", render(:file => "test/hello_world.erb")
- assert_equal "Hello world!", render(:file => "test/hello_world.erb", :locals => {:foo => "bar"})
- assert_equal 2, @compiled_templates.instance_methods.size
- end
-
- def test_compiled_template_will_not_be_recompiled_when_rendered_with_identical_local_assigns
- assert_equal 0, @compiled_templates.instance_methods.size
- assert_equal "Hello world!", render(:file => "test/hello_world.erb")
- ActionView::Template.any_instance.expects(:compile!).never
- assert_equal "Hello world!", render(:file => "test/hello_world.erb")
- end
-
- def test_compiled_template_will_always_be_recompiled_when_template_is_not_cached
- ActionView::Template.any_instance.expects(:recompile?).times(3).returns(true)
- assert_equal 0, @compiled_templates.instance_methods.size
- assert_equal "Hello world!", render(:file => "#{FIXTURE_LOAD_PATH}/test/hello_world.erb")
- ActionView::Template.any_instance.expects(:compile!).times(3)
- 3.times { assert_equal "Hello world!", render(:file => "#{FIXTURE_LOAD_PATH}/test/hello_world.erb") }
- assert_equal 1, @compiled_templates.instance_methods.size
+ assert_equal "one", render(:file => "test/render_file_with_locals_and_default.erb")
+ assert_equal "two", render(:file => "test/render_file_with_locals_and_default.erb", :locals => { :secret => "two" })
end
def test_template_changes_are_not_reflected_with_cached_templates
@@ -61,14 +37,12 @@ class CompiledTemplatesTest < Test::Unit::TestCase
def render_with_cache(*args)
view_paths = ActionController::Base.view_paths
- assert_equal ActionView::Template::FileSystemPath, view_paths.first.class
ActionView::Base.new(view_paths, {}).render(*args)
end
def render_without_cache(*args)
- path = ActionView::Template::FileSystemPath.new(FIXTURE_LOAD_PATH)
+ path = ActionView::Template::FileSystemPathWithFallback.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::Base.process_view_paths(path)
- assert_equal ActionView::Template::FileSystemPath, view_paths.first.class
ActionView::Base.new(view_paths, {}).render(*args)
end
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 78db87971b..73624406be 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -80,6 +80,14 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_string_options_for_select
+ options = "<option value=\"Denmark\">Denmark</option><option value=\"USA\">USA</option><option value=\"Sweden\">Sweden</option>"
+ assert_dom_equal(
+ options,
+ options_for_select(options)
+ )
+ end
+
def test_array_options_for_select
assert_dom_equal(
"<option value=\"&lt;Denmark&gt;\">&lt;Denmark&gt;</option>\n<option value=\"USA\">USA</option>\n<option value=\"Sweden\">Sweden</option>",
@@ -324,6 +332,20 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_under_fields_for_with_string_and_given_prompt
+ @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|
+ concat f.select(:category, options, :prompt => 'The prompt')
+ end
+
+ assert_dom_equal(
+ "<select id=\"post_category\" name=\"post[category]\"><option value=\"\">The prompt</option>\n#{options}</select>",
+ output_buffer
+ )
+ end
+
def test_select_with_blank
@post = Post.new
@post.category = "<mus>"
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index f9bc92c7c9..8caabfc3e1 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -3,6 +3,8 @@ require 'abstract_unit'
class JavaScriptHelperTest < ActionView::TestCase
tests ActionView::Helpers::JavaScriptHelper
+ def _evaluate_assigns_and_ivars() end
+
attr_accessor :formats, :output_buffer
def setup
@@ -10,6 +12,8 @@ class JavaScriptHelperTest < ActionView::TestCase
@template = self
end
+ def _evaluate_assigns_and_ivars() end
+
def test_escape_javascript
assert_equal '', escape_javascript(nil)
assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos'))
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 29cb60fd73..b6542ef29d 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -118,6 +118,10 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2)
assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4)
assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4)
+ assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 0)
+ assert_equal '500 MB', number_to_human_size(524288000, :precision=>0)
+ assert_equal '40 KB', number_to_human_size(41010, :precision => 0)
+ assert_equal '40 KB', number_to_human_size(41100, :precision => 0)
end
def test_number_to_human_size_with_custom_delimiter_and_separator
diff --git a/actionpack/test/template/output_buffer_test.rb b/actionpack/test/template/output_buffer_test.rb
index 6d8eab63dc..36bbaf9099 100644
--- a/actionpack/test/template/output_buffer_test.rb
+++ b/actionpack/test/template/output_buffer_test.rb
@@ -9,27 +9,52 @@ class OutputBufferTest < ActionController::TestCase
tests TestController
- def test_flush_output_buffer
- # Start with the default body parts
+ def setup
get :index
- assert_equal ['foo'], @response.body_parts
- assert_nil @response.template.output_buffer
-
- # Nil output buffer is skipped
- @response.template.flush_output_buffer
- assert_nil @response.template.output_buffer
- assert_equal ['foo'], @response.body_parts
-
- # Empty output buffer is skipped
- @response.template.output_buffer = ''
- @response.template.flush_output_buffer
- assert_equal '', @response.template.output_buffer
- assert_equal ['foo'], @response.body_parts
-
- # Flushing appends the output buffer to the body parts
- @response.template.output_buffer = 'bar'
- @response.template.flush_output_buffer
- assert_equal '', @response.template.output_buffer
- assert_equal ['foo', 'bar'], @response.body_parts
+ assert_equal ['foo'], body_parts
end
+
+ test 'output buffer is nil after rendering' do
+ assert_nil output_buffer
+ end
+
+ test 'flushing ignores nil output buffer' do
+ @controller.template.flush_output_buffer
+ assert_nil output_buffer
+ assert_equal ['foo'], body_parts
+ end
+
+ test 'flushing ignores empty output buffer' do
+ @controller.template.output_buffer = ''
+ @controller.template.flush_output_buffer
+ assert_equal '', output_buffer
+ assert_equal ['foo'], body_parts
+ end
+
+ test 'flushing appends the output buffer to the body parts' do
+ @controller.template.output_buffer = 'bar'
+ @controller.template.flush_output_buffer
+ assert_equal '', output_buffer
+ assert_equal ['foo', 'bar'], body_parts
+ end
+
+ if '1.9'.respond_to?(:force_encoding)
+ test 'flushing preserves output buffer encoding' do
+ original_buffer = ' '.force_encoding(Encoding::EUC_JP)
+ @controller.template.output_buffer = original_buffer
+ @controller.template.flush_output_buffer
+ assert_equal ['foo', original_buffer], body_parts
+ assert_not_equal original_buffer, output_buffer
+ assert_equal Encoding::EUC_JP, output_buffer.encoding
+ end
+ end
+
+ protected
+ def output_buffer
+ @controller.template.output_buffer
+ end
+
+ def body_parts
+ @controller.template.response.body_parts
+ end
end
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index cd6ee6ea11..f9f418aec9 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -61,6 +61,8 @@ class PrototypeHelperBaseTest < ActionView::TestCase
end
class PrototypeHelperTest < PrototypeHelperBaseTest
+ def _evaluate_assigns_and_ivars() end
+
def setup
@record = @author = Author.new
@article = Article.new
@@ -304,6 +306,8 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
@generator = create_generator
end
+ def _evaluate_assigns_and_ivars() end
+
def test_insert_html_with_string
assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });',
@generator.insert_html(:top, 'element', '<p>This is a test</p>')
@@ -328,28 +332,28 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
def test_remove
assert_equal 'Element.remove("foo");',
@generator.remove('foo')
- assert_equal '["foo", "bar", "baz"].each(Element.remove);',
+ assert_equal '["foo","bar","baz"].each(Element.remove);',
@generator.remove('foo', 'bar', 'baz')
end
def test_show
assert_equal 'Element.show("foo");',
@generator.show('foo')
- assert_equal '["foo", "bar", "baz"].each(Element.show);',
+ assert_equal '["foo","bar","baz"].each(Element.show);',
@generator.show('foo', 'bar', 'baz')
end
def test_hide
assert_equal 'Element.hide("foo");',
@generator.hide('foo')
- assert_equal '["foo", "bar", "baz"].each(Element.hide);',
+ assert_equal '["foo","bar","baz"].each(Element.hide);',
@generator.hide('foo', 'bar', 'baz')
end
def test_toggle
assert_equal 'Element.toggle("foo");',
@generator.toggle('foo')
- assert_equal '["foo", "bar", "baz"].each(Element.toggle);',
+ assert_equal '["foo","bar","baz"].each(Element.toggle);',
@generator.toggle('foo', 'bar', 'baz')
end
@@ -386,7 +390,7 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
assert_equal <<-EOS.chomp, @generator.to_s
Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
-["foo", "bar"].each(Element.remove);
+["foo","bar"].each(Element.remove);
Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
EOS
end
@@ -555,8 +559,8 @@ return (value.className == "welcome");
end
assert_equal <<-EOS.strip, @generator.to_s
-var a = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
-var b = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) {
+var a = [1, 2, 3].zip([4,5,6], [7,8,9]);
+var b = [1, 2, 3].zip([4,5,6], [7,8,9], function(array) {
return array.reverse();
});
EOS
@@ -607,7 +611,7 @@ return value.reverse();
def test_literal
literal = @generator.literal("function() {}")
- assert_equal "function() {}", literal.to_json
+ assert_equal "function() {}", ActiveSupport::JSON.encode(literal)
assert_equal "", @generator.to_s
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 8843f6fdd7..a56d7aee75 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -29,23 +29,19 @@ module RenderTestCases
end
def test_render_file_with_localization
- pending do
- begin
- old_locale = I18n.locale
- I18n.locale = :da
- assert_equal "Hey verden", @view.render(:file => "test/hello_world")
- ensure
- I18n.locale = old_locale
- end
+ begin
+ old_locale = I18n.locale
+ I18n.locale = :da
+ assert_equal "Hey verden", @view.render(:file => "test/hello_world")
+ ensure
+ I18n.locale = old_locale
end
end
def test_render_file_with_dashed_locale
old_locale = I18n.locale
- pending do
- I18n.locale = :"pt-BR"
- assert_equal "Ola mundo", @view.render(:file => "test/hello_world")
- end
+ I18n.locale = :"pt-BR"
+ assert_equal "Ola mundo", @view.render(:file => "test/hello_world")
ensure
I18n.locale = old_locale
end
@@ -95,10 +91,6 @@ module RenderTestCases
assert_equal "The secret is in the sauce\n", @view.render(:file => "test/dot.directory/render_file_with_ivar")
end
- def test_render_has_access_current_template
- assert_equal "test/template.erb", @view.render(:file => "test/template.erb")
- end
-
def test_render_update
# TODO: You should not have to stub out template because template is self!
@view.instance_variable_set(:@template, @view)
@@ -244,10 +236,6 @@ module RenderTestCases
end
end
- def test_template_with_malformed_template_handler_is_reachable_through_its_exact_filename
- assert_equal "Don't render me!", @view.render(:file => 'test/malformed/malformed.html.erb~')
- end
-
def test_render_with_layout
assert_equal %(<title></title>\nHello world!\n),
@view.render(:file => "test/hello_world.erb", :layout => "layouts/yield")
@@ -261,7 +249,7 @@ module RenderTestCases
if '1.9'.respond_to?(:force_encoding)
def test_render_utf8_template
result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
- assert_equal "Русский текст\n日本語のテキスト", result
+ assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result
assert_equal Encoding::UTF_8, result.encoding
end
end
@@ -273,7 +261,7 @@ class CachedViewRenderTest < ActiveSupport::TestCase
# Ensure view path cache is primed
def setup
view_paths = ActionController::Base.view_paths
- assert_equal ActionView::Template::FileSystemPath, view_paths.first.class
+ assert_equal ActionView::Template::FileSystemPathWithFallback, view_paths.first.class
setup_view(view_paths)
end
end
@@ -284,9 +272,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::Template::FileSystemPath.new(FIXTURE_LOAD_PATH)
+ path = ActionView::Template::FileSystemPathWithFallback.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::Base.process_view_paths(path)
- assert_equal ActionView::Template::FileSystemPath, view_paths.first.class
+ assert_equal ActionView::Template::FileSystemPathWithFallback, view_paths.first.class
setup_view(view_paths)
end
end
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index be7163888e..706b5085f4 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -49,6 +49,9 @@ class TextHelperTest < ActionView::TestCase
assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]")
assert_equal "Hello W...", truncate("Hello World!", :length => 10)
assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10)
+ assert_equal "Hello[...]", truncate("Hello Big World!", :omission => "[...]", :length => 13, :separator => ' ')
+ assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 14, :separator => ' ')
+ assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 15, :separator => ' ')
end
if RUBY_VERSION < '1.9.0'
@@ -401,6 +404,13 @@ class TextHelperTest < ActionView::TestCase
auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.",
:link => :all, :html => { :class => "menu", :target => "_blank" })
end
+
+ def test_auto_link_with_multiple_trailing_punctuations
+ url = "http://youtube.com"
+ url_result = generate_result(url)
+ assert_equal url_result, auto_link(url)
+ assert_equal "(link: #{url_result}).", auto_link("(link: #{url}).")
+ end
def test_cycle_class
value = Cycle.new("one", 2, "3")
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index 283380db96..ae2fbdb002 100755
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -10,6 +10,12 @@ Rake::TestTask.new do |t|
t.test_files = Dir.glob("test/cases/**/*_test.rb").sort
t.verbose = true
end
+task :isolated_test do
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ Dir.glob("test/**/*_test.rb").all? do |file|
+ system(ruby, '-Ilib:test', file)
+ end or raise "Failures"
+end
# Generate the RDoc documentation
Rake::RDocTask.new do |rdoc|
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 7f943b60ce..803f5b0157 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -21,15 +21,11 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+require 'active_support'
+
+require 'active_support/inflector'
module ActiveModel
autoload :Base, 'active_model/base'
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index c2bde29895..4be91d0505 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -41,9 +41,10 @@ module ActiveModel
end
def to_xml(options={})
+ require 'builder' unless defined? ::Builder
options[:root] ||= "errors"
options[:indent] ||= 2
- options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
options[:builder].instruct! unless options.delete(:skip_instruct)
options[:builder].errors do |e|
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index a1bb93b731..cc68d847a2 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -29,29 +29,17 @@ class I18nValidationTest < ActiveModel::TestCase
end
end
- def test_percent_s_interpolation_syntax_in_error_messages_still_works
- ActiveSupport::Deprecation.silence do
- result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this'
- assert_equal result, "this interpolation syntax is deprecated"
+ def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated
+ assert_not_deprecated do
+ default = "%s interpolation syntax was deprecated"
+ assert_equal default, I18n.t(:does_not_exist, :default => default, :value => 'this')
end
end
- def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated
- assert_deprecated('using %s in messages') do
- I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this'
- end
- end
-
- def test_percent_d_interpolation_syntax_in_error_messages_still_works
- ActiveSupport::Deprecation.silence do
- result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2
- assert_equal result, "2 interpolation syntaxes are deprecated"
- end
- end
-
- def test_percent_d_interpolation_syntax_in_error_messages_is_deprecated
- assert_deprecated('using %d in messages') do
- I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2
+ def test_percent_d_interpolation_syntax_in_error_messages_was_deprecated
+ assert_not_deprecated do
+ default = "%d interpolation syntaxes are deprecated"
+ assert_equal default, I18n.t(:does_not_exist, :default => default, :count => 2)
end
end
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index d58b44144b..411b640c9e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Implement #many? for NamedScope and AssociationCollection using #size. #1500 [Chris Kampmeier]
+
* Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH]
* Added ActiveRecord::Base#touch to update the updated_at/on attributes (or another specified timestamp) with the current time [DHH]
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index b50008c971..0b3f50d17e 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require 'rake/contrib/sshpublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
@@ -33,21 +32,32 @@ desc 'Run mysql, sqlite, and postgresql tests'
task :test => defined?(JRUBY_VERSION) ?
%w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) :
%w(test_mysql test_sqlite3 test_postgresql)
+task :isolated_test => defined?(JRUBY_VERSION) ?
+ %w(isolated_test_jdbcmysql isolated_test_jdbcsqlite3 isolated_test_jdbcpostgresql) :
+ %w(isolated_test_mysql isolated_test_sqlite3 isolated_test_postgresql)
-for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb )
+%w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter|
Rake::TestTask.new("test_#{adapter}") { |t|
- if adapter =~ /jdbc/
- t.libs << "test" << "test/connections/jdbc_#{adapter}"
- else
- t.libs << "test" << "test/connections/native_#{adapter}"
- end
+ connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}"
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
+ t.libs << "test" << connection_path
t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
t.verbose = true
}
+ task "isolated_test_#{adapter}" do
+ connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}"
+ adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
+ puts [adapter, adapter_short, connection_path].inspect
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ Dir["test/cases/**/*_test{,_#{adapter_short}}.rb"].all? do |file|
+ system(ruby, "-Ilib:test:#{connection_path}", file)
+ end or raise "Failures"
+ end
+
namespace adapter do
task :test => "test_#{adapter}"
+ task :isolated_test => "isolated_test_#{adapter}"
end
end
@@ -231,12 +241,14 @@ end
desc "Publish the beta gem"
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/ar", "doc").upload
end
diff --git a/activerecord/examples/simple.rb b/activerecord/examples/simple.rb
new file mode 100644
index 0000000000..c12f746992
--- /dev/null
+++ b/activerecord/examples/simple.rb
@@ -0,0 +1,14 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'active_record'
+
+class Person < ActiveRecord::Base
+ establish_connection :adapter => 'sqlite3', :database => 'foobar.db'
+ connection.create_table table_name, :force => true do |t|
+ t.string :name
+ end
+end
+
+bob = Person.create!(:name => 'bob')
+puts Person.all.inspect
+bob.destroy
+puts Person.all.inspect
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 07a10fb023..0ad7b02785 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -21,15 +21,9 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+require 'active_support'
begin
require 'active_model'
@@ -87,5 +81,4 @@ module ActiveRecord
end
end
-require 'active_record/i18n_interpolation_deprecation'
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 1eefebb3b3..9ecf231a66 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -1,8 +1,6 @@
module ActiveRecord
module Aggregations # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
def clear_aggregation_cache #:nodoc:
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index e4ab69aa1b..af80a579d6 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -1,9 +1,7 @@
module ActiveRecord
# See ActiveRecord::AssociationPreload::ClassMethods for documentation.
module AssociationPreload #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
# Implements the details of eager loading of ActiveRecord associations.
# Application developers should not use this module directly.
@@ -126,6 +124,7 @@ module ActiveRecord
association_proxy = parent_record.send(reflection_name)
association_proxy.loaded
association_proxy.target.push(*[associated_record].flatten)
+ association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
end
end
@@ -152,7 +151,8 @@ module ActiveRecord
seen_keys[associated_record[key].to_s] = true
mapped_records = id_to_record_map[associated_record[key].to_s]
mapped_records.each do |mapped_record|
- mapped_record.send("set_#{reflection_name}_target", associated_record)
+ association_proxy = mapped_record.send("set_#{reflection_name}_target", associated_record)
+ association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record)
end
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 2115878e32..157716a477 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,4 +1,12 @@
+require 'active_support/core_ext/module/delegation'
+
module ActiveRecord
+ class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
+ def initialize(reflection)
+ super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name})")
+ end
+ end
+
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection)
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
@@ -71,6 +79,8 @@ module ActiveRecord
# See ActiveRecord::Associations::ClassMethods for documentation.
module Associations # :nodoc:
+ extend ActiveSupport::Concern
+
# These classes will be loaded when associations are created.
# So there is no need to eager load them.
autoload :AssociationCollection, 'active_record/associations/association_collection'
@@ -83,10 +93,6 @@ module ActiveRecord
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
- def self.included(base)
- base.extend(ClassMethods)
- end
-
# Clears out the association cache
def clear_association_cache #:nodoc:
self.class.reflect_on_all_associations.to_a.each do |assoc|
@@ -1488,7 +1494,7 @@ module ActiveRecord
:finder_sql, :counter_sql,
:before_add, :after_add, :before_remove, :after_remove,
:extend, :readonly,
- :validate
+ :validate, :inverse_of
]
def create_has_many_reflection(association_id, options, &extension)
@@ -1502,7 +1508,7 @@ module ActiveRecord
@@valid_keys_for_has_one_association = [
:class_name, :foreign_key, :remote, :select, :conditions, :order,
:include, :dependent, :counter_cache, :extend, :as, :readonly,
- :validate, :primary_key
+ :validate, :primary_key, :inverse_of
]
def create_has_one_reflection(association_id, options)
@@ -1521,7 +1527,7 @@ module ActiveRecord
@@valid_keys_for_belongs_to_association = [
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
:include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
- :validate, :touch
+ :validate, :touch, :inverse_of
]
def create_belongs_to_reflection(association_id, options)
@@ -1662,7 +1668,19 @@ module ActiveRecord
def tables_in_string(string)
return [] if string.blank?
- string.scan(/([\.a-zA-Z_]+).?\./).flatten
+ string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten
+ end
+
+ def tables_in_hash(hash)
+ return [] if hash.blank?
+ tables = hash.map do |key, value|
+ if value.is_a?(Hash)
+ key.to_s
+ else
+ tables_in_string(key) if key.is_a?(String)
+ end
+ end
+ tables.flatten.compact
end
def conditions_tables(options)
@@ -1670,12 +1688,12 @@ module ActiveRecord
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
case cond
when nil then all
- when Array then all << cond.first
- when Hash then all << cond.keys
- else all << cond
+ when Array then all << tables_in_string(cond.first)
+ when Hash then all << tables_in_hash(cond)
+ else all << tables_in_string(cond)
end
end
- tables_in_string(conditions.join(' '))
+ conditions.flatten
end
def order_tables(options)
@@ -1910,21 +1928,27 @@ module ActiveRecord
return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
association = join.instantiate(row)
collection.target.push(association)
+ collection.__send__(:set_inverse_instance, association, record)
when :has_one
return if record.id.to_s != join.parent.record_id(row).to_s
return if record.instance_variable_defined?("@#{join.reflection.name}")
association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
- record.send("set_#{join.reflection.name}_target", association)
+ set_target_and_inverse(join, association, record)
when :belongs_to
return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
association = join.instantiate(row)
- record.send("set_#{join.reflection.name}_target", association)
+ set_target_and_inverse(join, association, record)
else
raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
end
return association
end
+ def set_target_and_inverse(join, association, record)
+ association_proxy = record.send("set_#{join.reflection.name}_target", association)
+ association_proxy.__send__(:set_inverse_instance, association, record)
+ end
+
class JoinBase # :nodoc:
attr_reader :active_record, :table_joins
delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 3aef1b21e9..e12f6be35d 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -302,6 +302,15 @@ module ActiveRecord
end
end
+ # Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
+ def many?
+ if block_given?
+ method_missing(:many?) { |*block_args| yield(*block_args) }
+ else
+ size > 1
+ end
+ end
+
def uniq(collection = self)
seen = Set.new
collection.inject([]) do |kept, record|
@@ -399,11 +408,14 @@ module ActiveRecord
find(:all)
end
- @reflection.options[:uniq] ? uniq(records) : records
+ records = @reflection.options[:uniq] ? uniq(records) : records
+ records.each do |record|
+ set_inverse_instance(record, @owner)
+ end
+ records
end
private
-
def create_record(attrs)
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
ensure_owner_is_not_new
@@ -433,6 +445,7 @@ module ActiveRecord
@target ||= [] unless loaded?
@target << record unless @reflection.options[:uniq] && @target.include?(record)
callback(:after_add, record)
+ set_inverse_instance(record, @owner)
record
end
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 241b9bfee0..e36b04ea95 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -53,6 +53,7 @@ module ActiveRecord
def initialize(owner, reflection)
@owner, @reflection = owner, reflection
+ reflection.check_validity!
Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
reset
end
@@ -274,6 +275,19 @@ module ActiveRecord
def owner_quoted_id
@owner.quoted_id
end
+
+ def set_inverse_instance(record, instance)
+ return if record.nil? || !we_can_set_the_inverse_on_this?(record)
+ inverse_relationship = @reflection.inverse_of
+ unless inverse_relationship.nil?
+ record.send(:"set_#{inverse_relationship.name}_target", instance)
+ end
+ end
+
+ # Override in subclasses
+ def we_can_set_the_inverse_on_this?(record)
+ false
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index f05c6be075..c88575048a 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -31,6 +31,8 @@ module ActiveRecord
@updated = true
end
+ set_inverse_instance(record, @owner)
+
loaded
record
end
@@ -41,18 +43,26 @@ module ActiveRecord
private
def find_target
- @reflection.klass.find(
+ the_target = @reflection.klass.find(
@owner[@reflection.primary_key_name],
:select => @reflection.options[:select],
:conditions => conditions,
:include => @reflection.options[:include],
:readonly => @reflection.options[:readonly]
)
+ set_inverse_instance(the_target, @owner)
+ the_target
end
def foreign_key_present
!@owner[@reflection.primary_key_name].nil?
end
+
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
+ # has_one associations.
+ def we_can_set_the_inverse_on_this?(record)
+ @reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index a2cbabfe0c..73dd50dd07 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -116,6 +116,11 @@ module ActiveRecord
:create => create_scoping
}
end
+
+ def we_can_set_the_inverse_on_this?(record)
+ inverse = @reflection.inverse_of
+ return !inverse.nil?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 1c091e7d5a..e8dbae9011 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -1,11 +1,6 @@
module ActiveRecord
module Associations
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
- def initialize(owner, reflection)
- reflection.check_validity!
- super
- end
-
alias_method :new, :build
def create!(attrs = nil)
@@ -22,6 +17,13 @@ module ActiveRecord
end
end
+ def destroy(*records)
+ transaction do
+ delete_records(flatten_deeper(records))
+ super
+ end
+ end
+
# Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
# calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero,
# and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length.
@@ -251,6 +253,11 @@ module ActiveRecord
def cached_counter_attribute_name
"#{@reflection.name}_count"
end
+
+ # NOTE - not sure that we can actually cope with inverses here
+ def we_can_set_the_inverse_on_this?(record)
+ false
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index b92cbbdeab..b72b84343b 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -74,13 +74,15 @@ module ActiveRecord
private
def find_target
- @reflection.klass.find(:first,
+ the_target = @reflection.klass.find(:first,
:conditions => @finder_sql,
:select => @reflection.options[:select],
:order => @reflection.options[:order],
:include => @reflection.options[:include],
:readonly => @reflection.options[:readonly]
)
+ set_inverse_instance(the_target, @owner)
+ the_target
end
def construct_sql
@@ -117,8 +119,15 @@ module ActiveRecord
self.target = record
end
+ set_inverse_instance(record, @owner)
+
record
end
+
+ def we_can_set_the_inverse_on_this?(record)
+ inverse = @reflection.inverse_of
+ return !inverse.nil?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index 8073ebaf9f..d93c8e7852 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -1,31 +1,31 @@
module ActiveRecord
module Associations
class HasOneThroughAssociation < HasManyThroughAssociation
-
+
def create_through_record(new_value) #nodoc:
klass = @reflection.through_reflection.klass
current_object = @owner.send(@reflection.through_reflection.name)
-
+
if current_object
- current_object.update_attributes(construct_join_attributes(new_value))
+ new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
else
- @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value)))
+ @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value
end
end
-
+
private
def find(*args)
super(args.merge(:limit => 1))
end
-
+
def find_target
super.first
end
def reset_target!
@target = nil
- end
- end
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 3ffc48941c..15358979c2 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -1,17 +1,23 @@
+require 'active_support/core_ext/enumerable'
+
module ActiveRecord
module AttributeMethods #:nodoc:
+ extend ActiveSupport::Concern
+
DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
- def self.included(base)
- base.extend ClassMethods
- base.attribute_method_suffix(*DEFAULT_SUFFIXES)
- base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
- base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
- base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
- base.time_zone_aware_attributes = false
- base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
- base.skip_time_zone_conversion_for_attributes = []
+ included do
+ attribute_method_suffix(*DEFAULT_SUFFIXES)
+
+ cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+ self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+
+ cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+ self.time_zone_aware_attributes = false
+
+ class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ self.skip_time_zone_conversion_for_attributes = []
end
# Declare and check for suffixed attribute methods.
@@ -99,8 +105,8 @@ module ActiveRecord
def instance_method_already_implemented?(method_name)
method_name = method_name.to_s
return true if method_name =~ /^id(=$|\?$|$)/
- @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map(&:to_s).to_set
- @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map(&:to_s).to_set
+ @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
+ @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
@_defined_class_methods.include?(method_name)
end
@@ -118,7 +124,7 @@ module ActiveRecord
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
def cached_attributes
@cached_attributes ||=
- columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
+ columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map{|col| col.name}.to_set
end
# Returns +true+ if the provided attribute is being cached.
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 4a03585b18..a540570f42 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -125,16 +125,15 @@ module ActiveRecord
# post.author.name = ''
# post.save(false) # => true
module AutosaveAssociation
+ extend ActiveSupport::Concern
+
ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
- def self.included(base)
- base.class_eval do
- base.extend(ClassMethods)
- alias_method_chain :reload, :autosave_associations
+ included do
+ alias_method_chain :reload, :autosave_associations
- ASSOCIATION_TYPES.each do |type|
- base.send("valid_keys_for_#{type}_association") << :autosave
- end
+ ASSOCIATION_TYPES.each do |type|
+ send("valid_keys_for_#{type}_association") << :autosave
end
end
@@ -311,11 +310,13 @@ module ActiveRecord
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_has_one_association(reflection)
if (association = association_instance_get(reflection.name)) && !association.target.nil?
- if reflection.options[:autosave] && association.marked_for_destruction?
+ autosave = reflection.options[:autosave]
+
+ if autosave && association.marked_for_destruction?
association.destroy
- elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
+ elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave
association[reflection.primary_key_name] = id
- association.save(false)
+ association.save(!autosave)
end
end
end
@@ -330,10 +331,12 @@ module ActiveRecord
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_belongs_to_association(reflection)
if association = association_instance_get(reflection.name)
- if reflection.options[:autosave] && association.marked_for_destruction?
+ autosave = reflection.options[:autosave]
+
+ if autosave && association.marked_for_destruction?
association.destroy
else
- association.save(false) if association.new_record? || reflection.options[:autosave]
+ association.save(!autosave) if association.new_record? || autosave
if association.updated?
self[reflection.primary_key_name] = association.id
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 251d5000f5..ec49d40a12 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1,5 +1,16 @@
require 'yaml'
require 'set'
+require 'active_support/dependencies'
+require 'active_support/time'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/delegating_attributes'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/array/extract_options'
+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/symbol'
module ActiveRecord #:nodoc:
# Generic Active Record exception class.
@@ -687,14 +698,9 @@ module ActiveRecord #:nodoc:
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
def exists?(id_or_conditions = {})
- connection.select_all(
- construct_finder_sql(
- :select => "#{quoted_table_name}.#{primary_key}",
- :conditions => expand_id_conditions(id_or_conditions),
- :limit => 1
- ),
- "#{name} Exists"
- ).size > 0
+ find_initial(
+ :select => "#{quoted_table_name}.#{primary_key}",
+ :conditions => expand_id_conditions(id_or_conditions)) ? true : false
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -1030,7 +1036,7 @@ module ActiveRecord #:nodoc:
# To start from an all-closed default and enable attributes as needed,
# have a look at +attr_accessible+.
def attr_protected(*attributes)
- write_inheritable_attribute(:attr_protected, Set.new(attributes.map(&:to_s)) + (protected_attributes || []))
+ write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || []))
end
# Returns an array of all the attributes that have been protected from mass-assignment.
@@ -1536,12 +1542,12 @@ module ActiveRecord #:nodoc:
end
def reverse_sql_order(order_query)
- reversed_query = order_query.to_s.split(/,/).each { |s|
+ order_query.to_s.split(/,/).each { |s|
if s.match(/\s(asc|ASC)$/)
s.gsub!(/\s(asc|ASC)$/, ' DESC')
elsif s.match(/\s(desc|DESC)$/)
s.gsub!(/\s(desc|DESC)$/, ' ASC')
- elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
+ else
s.concat(' DESC')
end
}.join(',')
@@ -1887,7 +1893,7 @@ module ActiveRecord #:nodoc:
else
find(:#{finder}, options.merge(finder_options))
end
- #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
+ #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect { |pair| pair.join(\' = \') }.join(\', \')}")' if bang}
end
}, __FILE__, __LINE__
send(method_id, *arguments)
@@ -2171,7 +2177,7 @@ module ActiveRecord #:nodoc:
# default_scope :order => 'last_name, first_name'
# end
def default_scope(options = {})
- self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
+ self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} }
end
# Test whether the given method and optional key are scoped.
@@ -2609,11 +2615,11 @@ module ActiveRecord #:nodoc:
# Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
# instance will affect the other.
def becomes(klass)
- returning klass.new do |became|
- became.instance_variable_set("@attributes", @attributes)
- became.instance_variable_set("@attributes_cache", @attributes_cache)
- became.instance_variable_set("@new_record", new_record?)
- end
+ became = klass.new
+ became.instance_variable_set("@attributes", @attributes)
+ became.instance_variable_set("@attributes_cache", @attributes_cache)
+ became.instance_variable_set("@new_record", new_record?)
+ became
end
# Updates a single attribute and saves the record without going through the normal validation procedure.
diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb
index 5a6cecd4ad..e41d38fb8f 100644
--- a/activerecord/lib/active_record/batches.rb
+++ b/activerecord/lib/active_record/batches.rb
@@ -1,8 +1,6 @@
module ActiveRecord
module Batches # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
# When processing large numbers of records, it's often a good idea to do
# so in batches to prevent memory ballooning.
diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb
index f077818d3b..727f4c1dc6 100644
--- a/activerecord/lib/active_record/calculations.rb
+++ b/activerecord/lib/active_record/calculations.rb
@@ -1,9 +1,8 @@
module ActiveRecord
module Calculations #:nodoc:
+ extend ActiveSupport::Concern
+
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
- def self.included(base)
- base.extend(ClassMethods)
- end
module ClassMethods
# Count operates using three different approaches.
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index e375037b5b..36f5f2ce47 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -211,21 +211,23 @@ module ActiveRecord
# needs to be aware of it because an ordinary +save+ will raise such exception
# instead of quietly returning +false+.
module Callbacks
+ extend ActiveSupport::Concern
+
CALLBACKS = %w(
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
after_validation before_validation_on_create after_validation_on_create before_validation_on_update
after_validation_on_update before_destroy after_destroy
)
- def self.included(base) #:nodoc:
- base.extend Observable
+ included do
+ extend Observable
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
- base.send :alias_method_chain, method, :callbacks
+ alias_method_chain method, :callbacks
end
- base.send :include, ActiveSupport::Callbacks
- base.define_callbacks *CALLBACKS
+ include ActiveSupport::Callbacks
+ define_callbacks *CALLBACKS
end
# Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index aac84cc5f4..500dafdc2e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -1,5 +1,6 @@
require 'monitor'
require 'set'
+require 'active_support/core_ext/module/synchronization'
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -107,13 +108,14 @@ module ActiveRecord
checkin conn if conn
end
- # Reserve a connection, and yield it to a block. Ensure the connection is
- # checked back in when finished.
+ # If a connection already exists yield it to the block. If no connection
+ # exists checkout a connection, yield it to the block, and checkin the
+ # connection when finished.
def with_connection
- conn = checkout
- yield conn
+ fresh_connection = true unless @reserved_connections[current_connection_id]
+ yield connection
ensure
- checkin conn
+ release_connection if fresh_connection
end
# Returns true if a connection has already been opened.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 3a7bf35248..720fba29e9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/big_decimal/conversions'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
module Quoting
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index c29c1562b4..ff63ea3a2e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -316,7 +316,7 @@ module ActiveRecord
def initialize_schema_migrations_table
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- unless tables.detect { |t| t == sm_table }
+ unless table_exists?(sm_table)
create_table(sm_table, :id => false) do |schema_migrations_table|
schema_migrations_table.column :version, :string, :null => false
end
@@ -327,7 +327,7 @@ module ActiveRecord
# migrated up to that point:
si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
- if tables.detect { |t| t == si_table }
+ if table_exists?(si_table)
old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
assume_migrated_upto_version(old_version)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index a8cd9f033b..91b111ab55 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -12,6 +12,8 @@ require 'active_record/connection_adapters/abstract/connection_pool'
require 'active_record/connection_adapters/abstract/connection_specification'
require 'active_record/connection_adapters/abstract/query_cache'
+require 'active_support/core_ext/benchmark'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
# ActiveRecord supports multiple database systems. AbstractAdapter and
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 9300df28ee..d5536e4d67 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_support/core_ext/kernel/requires'
require 'set'
module MysqlCompat #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 4961793866..002696d2c4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_support/core_ext/kernel/requires'
begin
require_library_or_gem 'pg'
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 75420f69aa..5eef692d05 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -25,9 +25,9 @@ module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLite3Adapter < SQLiteAdapter # :nodoc:
def table_structure(table_name)
- returning structure = @connection.table_info(quote_table_name(table_name)) do
- raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
- end
+ structure = @connection.table_info(quote_table_name(table_name))
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
+ structure
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 05334a830a..c9d0c9574f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_support/core_ext/kernel/requires'
module ActiveRecord
class Base
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
index 4a2510aa63..178767e0c3 100644
--- a/activerecord/lib/active_record/dirty.rb
+++ b/activerecord/lib/active_record/dirty.rb
@@ -34,20 +34,21 @@ module ActiveRecord
# person.name << 'by'
# person.name_change # => ['uncle bob', 'uncle bobby']
module Dirty
+ extend ActiveSupport::Concern
+
DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']
- def self.included(base)
- base.attribute_method_suffix *DIRTY_SUFFIXES
- base.alias_method_chain :write_attribute, :dirty
- base.alias_method_chain :save, :dirty
- base.alias_method_chain :save!, :dirty
- base.alias_method_chain :update, :dirty
- base.alias_method_chain :reload, :dirty
+ included do
+ attribute_method_suffix *DIRTY_SUFFIXES
- base.superclass_delegating_accessor :partial_updates
- base.partial_updates = true
+ alias_method_chain :write_attribute, :dirty
+ alias_method_chain :save, :dirty
+ alias_method_chain :save!, :dirty
+ alias_method_chain :update, :dirty
+ alias_method_chain :reload, :dirty
- base.send(:extend, ClassMethods)
+ superclass_delegating_accessor :partial_updates
+ self.partial_updates = true
end
# Do any attributes have unsaved changes?
@@ -167,7 +168,9 @@ module ActiveRecord
module ClassMethods
def self.extended(base)
- base.metaclass.alias_method_chain(:alias_attribute, :dirty)
+ class << base
+ alias_method_chain :alias_attribute, :dirty
+ end
end
def alias_attribute_with_dirty(new_name, old_name)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index c6501113bf..2b0cfc2c3b 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -1,8 +1,10 @@
require 'erb'
require 'yaml'
require 'csv'
+require 'zlib'
require 'active_support/dependencies'
require 'active_support/test_case'
+require 'active_support/core_ext/logger'
if RUBY_VERSION < '1.9'
module YAML #:nodoc:
@@ -433,6 +435,7 @@ end
# Any fixture labeled "DEFAULTS" is safely ignored.
class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
+ MAX_ID = 2 ** 31 - 1
DEFAULT_FILTER_RE = /\.ya?ml$/
@@all_cached_fixtures = {}
@@ -524,11 +527,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
cached_fixtures(connection, table_names)
end
- # Returns a consistent identifier for +label+. This will always
- # be a positive integer, and will always be the same for a given
- # label, assuming the same OS, platform, and version of Ruby.
+ # Returns a consistent, platform-independent identifier for +label+.
+ # Identifiers are positive integers less than 2^32.
def self.identify(label)
- label.to_s.hash.abs
+ Zlib.crc32(label.to_s) % MAX_ID
end
attr_reader :table_name, :name
@@ -805,27 +807,25 @@ end
module ActiveRecord
module TestFixtures
- def self.included(base)
- base.class_eval do
- setup :setup_fixtures
- teardown :teardown_fixtures
-
- superclass_delegating_accessor :fixture_path
- superclass_delegating_accessor :fixture_table_names
- superclass_delegating_accessor :fixture_class_names
- superclass_delegating_accessor :use_transactional_fixtures
- superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
- superclass_delegating_accessor :pre_loaded_fixtures
-
- self.fixture_table_names = []
- self.use_transactional_fixtures = false
- self.use_instantiated_fixtures = true
- self.pre_loaded_fixtures = false
-
- self.fixture_class_names = {}
- end
+ extend ActiveSupport::Concern
+
+ included do
+ setup :setup_fixtures
+ teardown :teardown_fixtures
+
+ superclass_delegating_accessor :fixture_path
+ superclass_delegating_accessor :fixture_table_names
+ superclass_delegating_accessor :fixture_class_names
+ superclass_delegating_accessor :use_transactional_fixtures
+ superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
+ superclass_delegating_accessor :pre_loaded_fixtures
+
+ self.fixture_table_names = []
+ self.use_transactional_fixtures = false
+ self.use_instantiated_fixtures = true
+ self.pre_loaded_fixtures = false
- base.extend ClassMethods
+ self.fixture_class_names = {}
end
module ClassMethods
diff --git a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb
deleted file mode 100644
index cd634e1b8d..0000000000
--- a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# Deprecates the use of the former message interpolation syntax in activerecord
-# as in "must have %d characters". The new syntax uses explicit variable names
-# as in "{{value}} must have {{count}} characters".
-
-require 'i18n/backend/simple'
-module I18n
- module Backend
- class Simple
- DEPRECATED_INTERPOLATORS = { '%d' => '{{count}}', '%s' => '{{value}}' }
-
- protected
- def interpolate_with_deprecated_syntax(locale, string, values = {})
- return string unless string.is_a?(String)
-
- string = string.gsub(/%d|%s/) do |s|
- instead = DEPRECATED_INTERPOLATORS[s]
- ActiveSupport::Deprecation.warn "using #{s} in messages is deprecated; use #{instead} instead."
- instead
- end
-
- interpolate_without_deprecated_syntax(locale, string, values)
- end
- alias_method_chain :interpolate, :deprecated_syntax
- end
- end
-end \ No newline at end of file
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 7fa7e267d8..cec5ca3324 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -42,17 +42,17 @@ module ActiveRecord
# To override the name of the lock_version column, invoke the <tt>set_locking_column</tt> method.
# This method uses the same syntax as <tt>set_table_name</tt>
module Optimistic
- def self.included(base) #:nodoc:
- base.extend ClassMethods
+ extend ActiveSupport::Concern
- base.cattr_accessor :lock_optimistically, :instance_writer => false
- base.lock_optimistically = true
+ included do
+ cattr_accessor :lock_optimistically, :instance_writer => false
+ self.lock_optimistically = true
- base.alias_method_chain :update, :lock
- base.alias_method_chain :destroy, :lock
- base.alias_method_chain :attributes_from_column_definition, :lock
+ alias_method_chain :update, :lock
+ alias_method_chain :destroy, :lock
+ alias_method_chain :attributes_from_column_definition, :lock
- class << base
+ class << self
alias_method :locking_column=, :set_locking_column
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 657acd6dc0..a7be3539d5 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -511,11 +511,11 @@ module ActiveRecord
raise DuplicateMigrationNameError.new(name.camelize)
end
- klasses << returning(MigrationProxy.new) do |migration|
- migration.name = name.camelize
- migration.version = version
- migration.filename = file
- end
+ migration = MigrationProxy.new
+ migration.name = name.camelize
+ migration.version = version
+ migration.filename = file
+ klasses << migration
end
migrations = migrations.sort_by(&:version)
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 1f3ef300f2..1b22fa5e24 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -1,5 +1,10 @@
+require 'active_support/core_ext/array'
+require 'active_support/core_ext/hash/except'
+
module ActiveRecord
module NamedScope
+ extend ActiveSupport::Concern
+
# All subclasses of ActiveRecord::Base have one named scope:
# * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
#
@@ -7,11 +12,8 @@ module ActiveRecord
# intermediate values (scopes) around as first-class objects is convenient.
#
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
- def self.included(base)
- base.class_eval do
- extend ClassMethods
- named_scope :scoped, lambda { |scope| scope }
- end
+ included do
+ named_scope :scoped, lambda { |scope| scope }
end
module ClassMethods
@@ -107,14 +109,14 @@ module ActiveRecord
class Scope
attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined
- NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
+ NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? many? respond_to?).to_set
[].methods.each do |m|
unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
delegate m, :to => :proxy_found
end
end
- delegate :scopes, :with_scope, :to => :proxy_scope
+ delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
def initialize(proxy_scope, options, &block)
options ||= {}
@@ -166,6 +168,15 @@ module ActiveRecord
end
end
+ # Returns true if the named scope has more than 1 matching record.
+ def many?
+ if block_given?
+ proxy_found.many? { |*block_args| yield(*block_args) }
+ else
+ size > 1
+ end
+ end
+
protected
def proxy_found
@found || load_found
@@ -178,7 +189,7 @@ module ActiveRecord
else
with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
method = :new if method == :build
- if current_scoped_methods_when_defined
+ if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
with_scope current_scoped_methods_when_defined do
proxy_scope.send(method, *args, &block)
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index e3122d195a..0beb4321a2 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -1,9 +1,13 @@
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/object/try'
+
module ActiveRecord
module NestedAttributes #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- base.class_inheritable_accessor :reject_new_nested_attributes_procs, :instance_writer => false
- base.reject_new_nested_attributes_procs = {}
+ extend ActiveSupport::Concern
+
+ included do
+ class_inheritable_accessor :reject_new_nested_attributes_procs, :instance_writer => false
+ self.reject_new_nested_attributes_procs = {}
end
# == Nested Attributes
@@ -180,10 +184,14 @@ module ActiveRecord
# and the Proc should return either +true+ or +false+. When no Proc
# is specified a record will be built for all attribute hashes that
# do not have a <tt>_delete</tt> that evaluates to true.
+ # Passing <tt>:all_blank</tt> instead of a Proc will create a proc
+ # that will reject a record where all the attributes are blank.
#
# Examples:
# # creates avatar_attributes=
# accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? }
+ # # creates avatar_attributes=
+ # accepts_nested_attributes_for :avatar, :reject_if => :all_blank
# # creates avatar_attributes= and posts_attributes=
# accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
def accepts_nested_attributes_for(*attr_names)
@@ -201,7 +209,12 @@ module ActiveRecord
end
reflection.options[:autosave] = true
- self.reject_new_nested_attributes_procs[association_name.to_sym] = options[:reject_if]
+
+ self.reject_new_nested_attributes_procs[association_name.to_sym] = if options[:reject_if] == :all_blank
+ proc { |attributes| attributes.all? {|k,v| v.blank?} }
+ else
+ options[:reject_if]
+ end
# def pirate_attributes=(attributes)
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, false)
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index b35e407cc1..89ec0962bf 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -3,9 +3,7 @@ require 'set'
module ActiveRecord
module Observing # :nodoc:
- def self.included(base)
- base.extend ClassMethods
- end
+ extend ActiveSupport::Concern
module ClassMethods
# Activates the observers assigned. Examples:
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 2d4c1d5507..0baa9654b7 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,8 +1,6 @@
module ActiveRecord
module Reflection # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
# Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
# This information can, for example, be used in a form builder that took an Active Record object and created input
@@ -212,6 +210,13 @@ module ActiveRecord
end
def check_validity!
+ check_validity_of_inverse!
+ end
+
+ def check_validity_of_inverse!
+ if has_inverse? && inverse_of.nil?
+ raise InverseOfAssociationNotFoundError.new(self)
+ end
end
def through_reflection
@@ -225,6 +230,18 @@ module ActiveRecord
nil
end
+ def has_inverse?
+ !@options[:inverse_of].nil?
+ end
+
+ def inverse_of
+ if has_inverse?
+ @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
+ else
+ nil
+ end
+ end
+
private
def derive_class_name
class_name = name.to_s.camelize
@@ -300,6 +317,8 @@ module ActiveRecord
unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
raise HasManyThroughSourceAssociationMacroError.new(self)
end
+
+ check_validity_of_inverse!
end
def through_reflection_primary_key
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 557a554966..de530a3456 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -1,5 +1,5 @@
require 'stringio'
-require 'bigdecimal'
+require 'active_support/core_ext/big_decimal'
module ActiveRecord
# This class is used to dump the database schema for some connection to some
@@ -176,4 +176,4 @@ HEADER
end
end
end
-end \ No newline at end of file
+end
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 870b4b2dd4..7959f2b510 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -1,5 +1,3 @@
-require 'active_support/json'
-
module ActiveRecord #:nodoc:
module Serialization
class Serializer #:nodoc:
@@ -20,9 +18,9 @@ module ActiveRecord #:nodoc:
if options[:only]
options.delete(:except)
- attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
+ attribute_names = attribute_names & Array.wrap(options[:only]).collect { |n| n.to_s }
else
- options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
+ options[:except] = Array.wrap(options[:except]) | Array.wrap(@record.class.inheritance_column)
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
end
@@ -30,7 +28,7 @@ module ActiveRecord #:nodoc:
end
def serializable_method_names
- Array(options[:methods]).inject([]) do |method_attributes, name|
+ Array.wrap(options[:methods]).inject([]) do |method_attributes, name|
method_attributes << name if @record.respond_to?(name.to_s)
method_attributes
end
@@ -51,7 +49,7 @@ module ActiveRecord #:nodoc:
:only => options[:only] }
include_has_options = include_associations.is_a?(Hash)
- associations = include_has_options ? include_associations.keys : Array(include_associations)
+ associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
for association in associations
records = case @record.class.reflect_on_association(association).macro
@@ -73,16 +71,19 @@ module ActiveRecord #:nodoc:
end
def serializable_record
- returning(serializable_record = {}) do
- serializable_names.each { |name| serializable_record[name] = @record.send(name) }
- add_includes do |association, records, opts|
+ record = {}
+ serializable_names.each { |name| record[name] = @record.send(name) }
+
+ add_includes do |association, records, opts|
+ record[association] =
if records.is_a?(Enumerable)
- serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record }
+ records.collect { |r| self.class.new(r, opts).serializable_record }
else
- serializable_record[association] = self.class.new(records, opts).serializable_record
+ self.class.new(records, opts).serializable_record
end
- end
end
+
+ record
end
def serialize
diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb
index 1fd65ed006..67e2b2abb3 100644
--- a/activerecord/lib/active_record/serializers/json_serializer.rb
+++ b/activerecord/lib/active_record/serializers/json_serializer.rb
@@ -1,8 +1,11 @@
+require 'active_support/json'
+
module ActiveRecord #:nodoc:
module Serialization
- def self.included(base)
- base.cattr_accessor :include_root_in_json, :instance_writer => false
- base.extend ClassMethods
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :include_root_in_json, :instance_writer => false
end
# Returns a JSON string representing the model. Some configuration is
@@ -72,10 +75,11 @@ module ActiveRecord #:nodoc:
# {"comments": [{"body": "Don't think too hard"}],
# "title": "So I was thinking"}]}
def to_json(options = {})
+ json = JsonSerializer.new(self, options).to_s
if include_root_in_json
- "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
+ "{#{self.class.json_class_name}:#{json}}"
else
- JsonSerializer.new(self, options).to_s
+ json
end
end
@@ -84,9 +88,15 @@ module ActiveRecord #:nodoc:
self
end
+ private
+ # For compatibility with ActiveSupport::JSON.encode
+ def rails_to_json(options, *args)
+ to_json(options)
+ end
+
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
def serialize
- serializable_record.to_json
+ ActiveSupport::JSON.encode(serializable_record)
end
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index fa75874603..4eaf9531e2 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/conversions'
+
module ActiveRecord #:nodoc:
module Serialization
# Builds an XML document to represent the model. Some configuration is
@@ -165,8 +167,9 @@ module ActiveRecord #:nodoc:
class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
def builder
@builder ||= begin
+ require 'builder' unless defined? ::Builder
options[:indent] ||= 2
- builder = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
unless options[:skip_instruct]
builder.instruct!
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index 21471da419..9dda3361d8 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -295,7 +295,7 @@ module ActiveRecord
def set_session(env, sid, session_data)
Base.silence do
- record = env[SESSION_RECORD_KEY] ||= find_session(sid)
+ record = get_session_model(env, sid)
record.data = session_data
return false unless record.save
@@ -309,6 +309,14 @@ module ActiveRecord
return true
end
+
+ def get_session_model(env, sid)
+ if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
+ env[SESSION_RECORD_KEY] = find_session(sid)
+ else
+ env[SESSION_RECORD_KEY] ||= find_session(sid)
+ end
+ end
def find_session(id)
@@session_class.find_by_session_id(id) ||
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index 8c6abaaccb..2dfe2c09ea 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -20,7 +20,7 @@ module ActiveRecord
patterns_to_match.each do |pattern|
failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
end
- assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found."
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
end
def assert_queries(num = 1)
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index d9e1ef351f..da075dabd3 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -8,12 +8,14 @@ module ActiveRecord
# Timestamps are in the local timezone by default but you can use UTC by setting
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
module Timestamp
- def self.included(base) #:nodoc:
- base.alias_method_chain :create, :timestamps
- base.alias_method_chain :update, :timestamps
+ extend ActiveSupport::Concern
- base.class_inheritable_accessor :record_timestamps, :instance_writer => false
- base.record_timestamps = true
+ included do
+ alias_method_chain :create, :timestamps
+ alias_method_chain :update, :timestamps
+
+ class_inheritable_accessor :record_timestamps, :instance_writer => false
+ self.record_timestamps = true
end
# Saves the record with the updated_at/on attributes set to the current time.
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index b059eb7f6f..4f8ccdd40e 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -3,16 +3,14 @@ require 'thread'
module ActiveRecord
# See ActiveRecord::Transactions::ClassMethods for documentation.
module Transactions
+ extend ActiveSupport::Concern
+
class TransactionError < ActiveRecordError # :nodoc:
end
- def self.included(base)
- base.extend(ClassMethods)
-
- base.class_eval do
- [:destroy, :save, :save!].each do |method|
- alias_method_chain method, :transactions
- end
+ included do
+ [:destroy, :save, :save!].each do |method|
+ alias_method_chain method, :transactions
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 744205df1b..04f4ed47ac 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/integer/even_odd'
+
module ActiveRecord
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
# +record+ method to retrieve the record which did not validate.
@@ -98,17 +100,18 @@ module ActiveRecord
end
module Validations
- def self.included(base) # :nodoc:
- base.send :include, ActiveModel::Validations
- base.extend ClassMethods
- base.send :include, InstanceMethods
+ extend ActiveSupport::Concern
- base.define_callbacks :validate_on_create, :validate_on_update
+ depends_on ActiveSupport::Callbacks
+ depends_on ActiveModel::Validations
- base.class_eval do
- alias_method_chain :save, :validation
- alias_method_chain :save!, :validation
- end
+ included do
+ include Validations::InstanceMethods
+
+ alias_method_chain :save, :validation
+ alias_method_chain :save!, :validation
+
+ define_callbacks :validate_on_create, :validate_on_update
end
module ClassMethods
@@ -197,7 +200,6 @@ module ActiveRecord
respond_to?(attribute.to_sym) ? send(attribute.to_sym) : self[attribute.to_sym]
end
end
-
end
end
diff --git a/activerecord/test/cases/aaa_create_tables_test.rb b/activerecord/test/cases/aaa_create_tables_test.rb
deleted file mode 100644
index 3911afac49..0000000000
--- a/activerecord/test/cases/aaa_create_tables_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# The filename begins with "aaa" to ensure this is the first test.
-require "cases/helper"
-
-class AAACreateTablesTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false
-
- def test_load_schema
- eval(File.read(SCHEMA_ROOT + "/schema.rb"))
- if File.exists?(adapter_specific_schema_file)
- eval(File.read(adapter_specific_schema_file))
- end
- assert true
- end
-
- def test_drop_and_create_courses_table
- eval(File.read(SCHEMA_ROOT + "/schema2.rb"))
- assert true
- end
-
- private
- def adapter_specific_schema_file
- SCHEMA_ROOT + '/' + ActiveRecord::Base.connection.adapter_name.downcase + '_specific_schema.rb'
- end
-end
diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb
index 4e0e1c7f15..8b6ec04018 100644
--- a/activerecord/test/cases/aggregations_test.rb
+++ b/activerecord/test/cases/aggregations_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'models/customer'
+require 'active_support/core_ext/exception'
class AggregationsTest < ActiveRecord::TestCase
fixtures :customers
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index 1b2e0fc11e..f313a75233 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -1,18 +1,18 @@
require 'cases/helper'
-require 'models/author'
require 'models/post'
+require 'models/author'
require 'models/comment'
require 'models/category'
require 'models/categorization'
+require 'active_support/core_ext/array/random_access'
module Remembered
- def self.included(base)
- base.extend ClassMethods
- base.class_eval do
- after_create :remember
- protected
- def remember; self.class.remembered << self; end
- end
+ extend ActiveSupport::Concern
+
+ included do
+ after_create :remember
+ protected
+ def remember; self.class.remembered << self; end
end
module ClassMethods
@@ -66,13 +66,13 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
def setup
generate_test_object_graphs
end
-
+
def teardown
- [Circle, Square, Triangle, PaintColor, PaintTexture,
+ [Circle, Square, Triangle, PaintColor, PaintTexture,
ShapeExpression, NonPolyOne, NonPolyTwo].each do |c|
c.delete_all
end
-
+
end
@@ -127,4 +127,4 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
Author.all :include => includes, :conditions => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name'
end
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 40723814c5..4cf49be668 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -223,6 +223,18 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ def test_eager_association_loading_with_belongs_to_and_conditions_hash
+ comments = []
+ assert_nothing_raised do
+ comments = Comment.find(:all, :include => :post, :conditions => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id')
+ end
+ assert_equal 3, comments.length
+ assert_equal [5,6,7], comments.collect { |c| c.id }
+ assert_no_queries do
+ comments.first.post
+ end
+ end
+
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
assert_nothing_raised do
@@ -577,6 +589,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
end
+ def test_limited_eager_with_numeric_in_association
+ assert_equal people(:david, :susan), Person.find(:all, :include => [:readers, :primary_contact, :number1_fan], :conditions => "number1_fans_people.first_name like 'M%'", :order => 'people.id', :limit => 2, :offset => 0)
+ end
+
def test_preload_with_interpolation
assert_equal [comments(:greetings)], Post.find(posts(:welcome).id, :include => :comments_with_interpolated_conditions).comments_with_interpolated_conditions
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 5e8b2cadfc..8dc95806b9 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -24,6 +24,7 @@ require 'models/club'
require 'models/member'
require 'models/membership'
require 'models/sponsor'
+require 'active_support/core_ext/string/conversions'
class ProjectWithAfterCreateHook < ActiveRecord::Base
set_table_name 'projects'
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 5df74fcdcd..d99424f9cd 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1023,6 +1023,45 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert firm.clients.loaded?
end
+ def test_calling_many_should_count_instead_of_loading_association
+ firm = companies(:first_firm)
+ assert_queries(1) do
+ firm.clients.many? # use count query
+ end
+ assert !firm.clients.loaded?
+ end
+
+ def test_calling_many_on_loaded_association_should_not_use_query
+ firm = companies(:first_firm)
+ firm.clients.collect # force load
+ assert_no_queries { assert firm.clients.many? }
+ end
+
+ def test_calling_many_should_defer_to_collection_if_using_a_block
+ firm = companies(:first_firm)
+ assert_queries(1) do
+ firm.clients.expects(:size).never
+ firm.clients.many? { true }
+ end
+ assert firm.clients.loaded?
+ end
+
+ def test_calling_many_should_return_false_if_none_or_one
+ firm = companies(:another_firm)
+ assert !firm.clients_like_ms.many?
+ assert_equal 0, firm.clients_like_ms.size
+
+ firm = companies(:first_firm)
+ assert !firm.limited_clients.many?
+ assert_equal 1, firm.limited_clients.size
+ end
+
+ def test_calling_many_should_return_true_if_more_than_one
+ firm = companies(:first_firm)
+ assert firm.clients.many?
+ assert_equal 2, firm.clients.size
+ end
+
def test_joins_with_namespaced_model_should_use_correct_type
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = true
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 97efca7891..4254badef2 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -13,7 +13,7 @@ require 'models/pet'
require 'models/toy'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys
+ fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references
def test_associate_existing
assert_queries(2) { posts(:thinking);people(:david) }
@@ -23,49 +23,49 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
posts(:thinking).people << people(:david)
end
-
+
assert_queries(1) do
assert posts(:thinking).people.include?(people(:david))
end
-
+
assert posts(:thinking).reload.people(true).include?(people(:david))
end
def test_associating_new
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
-
+
assert_queries(0) do
new_person = Person.new :first_name => 'bob'
end
-
+
# Associating new records always saves them
# Thus, 1 query for the new person record, 1 query for the new join table record
assert_queries(2) do
posts(:thinking).people << new_person
end
-
+
assert_queries(1) do
assert posts(:thinking).people.include?(new_person)
end
-
+
assert posts(:thinking).reload.people(true).include?(new_person)
end
def test_associate_new_by_building
assert_queries(1) { posts(:thinking) }
-
+
assert_queries(0) do
posts(:thinking).people.build(:first_name=>"Bob")
posts(:thinking).people.new(:first_name=>"Ted")
end
-
+
# Should only need to load the association once
assert_queries(1) do
assert posts(:thinking).people.collect(&:first_name).include?("Bob")
assert posts(:thinking).people.collect(&:first_name).include?("Ted")
end
-
+
# 2 queries for each new record (1 to save the record itself, 1 for the join model)
# * 2 new records = 4
# + 1 query to save the actual post = 5
@@ -73,27 +73,27 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts(:thinking).body += '-changed'
posts(:thinking).save
end
-
+
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob")
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted")
end
def test_delete_association
assert_queries(2){posts(:welcome);people(:michael); }
-
+
assert_queries(1) do
posts(:welcome).people.delete(people(:michael))
end
-
+
assert_queries(1) do
assert posts(:welcome).people.empty?
end
-
+
assert posts(:welcome).reload.people(true).empty?
end
def test_destroy_association
- assert_difference "Person.count", -1 do
+ assert_difference ["Person.count", "Reader.count"], -1 do
posts(:welcome).people.destroy(people(:michael))
end
@@ -102,7 +102,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_destroy_all
- assert_difference "Person.count", -1 do
+ assert_difference ["Person.count", "Reader.count"], -1 do
posts(:welcome).people.destroy_all
end
@@ -110,38 +110,44 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:welcome).people(true).empty?
end
+ def test_should_raise_exception_for_destroying_mismatching_records
+ assert_no_difference ["Person.count", "Reader.count"] do
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) }
+ end
+ end
+
def test_replace_association
assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
-
+
# 1 query to delete the existing reader (michael)
# 1 query to associate the new reader (david)
assert_queries(2) do
posts(:welcome).people = [people(:david)]
end
-
+
assert_queries(0){
assert posts(:welcome).people.include?(people(:david))
assert !posts(:welcome).people.include?(people(:michael))
}
-
+
assert posts(:welcome).reload.people(true).include?(people(:david))
assert !posts(:welcome).reload.people(true).include?(people(:michael))
end
def test_associate_with_create
assert_queries(1) { posts(:thinking) }
-
+
# 1 query for the new record, 1 for the join table record
# No need to update the actual collection yet!
assert_queries(2) do
posts(:thinking).people.create(:first_name=>"Jeb")
end
-
+
# *Now* we actually need the collection so it's loaded
assert_queries(1) do
assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
end
-
+
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb")
end
@@ -159,15 +165,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_clear_associations
assert_queries(2) { posts(:welcome);posts(:welcome).people(true) }
-
+
assert_queries(1) do
posts(:welcome).people.clear
end
-
+
assert_queries(0) do
assert posts(:welcome).people.empty?
end
-
+
assert posts(:welcome).reload.people(true).empty?
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 12c598751b..ab6e6d20fc 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -43,7 +43,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
@member.reload
end
end
-
+
+ def test_set_record_to_nil_should_delete_association
+ @member.club = nil
+ @member.reload
+ assert_equal nil, @member.current_membership
+ assert_nil @member.club
+ end
+
def test_has_one_through_polymorphic
assert_equal clubs(:moustache_club), @member.sponsor_club
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
new file mode 100644
index 0000000000..47f83db112
--- /dev/null
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -0,0 +1,313 @@
+require "cases/helper"
+require 'models/man'
+require 'models/face'
+require 'models/interest'
+require 'models/zine'
+require 'models/club'
+require 'models/sponsor'
+
+class InverseAssociationTests < ActiveRecord::TestCase
+ def test_should_allow_for_inverse_of_options_in_associations
+ assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_many') do
+ Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car)
+ end
+
+ assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_one') do
+ Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car)
+ end
+
+ assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on belongs_to') do
+ Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver)
+ end
+ end
+
+ def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse
+ has_one_with_inverse_ref = Man.reflect_on_association(:face)
+ assert has_one_with_inverse_ref.respond_to?(:has_inverse?)
+ assert has_one_with_inverse_ref.has_inverse?
+
+ has_many_with_inverse_ref = Man.reflect_on_association(:interests)
+ assert has_many_with_inverse_ref.respond_to?(:has_inverse?)
+ assert has_many_with_inverse_ref.has_inverse?
+
+ belongs_to_with_inverse_ref = Face.reflect_on_association(:man)
+ assert belongs_to_with_inverse_ref.respond_to?(:has_inverse?)
+ assert belongs_to_with_inverse_ref.has_inverse?
+
+ has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
+ assert has_one_without_inverse_ref.respond_to?(:has_inverse?)
+ assert !has_one_without_inverse_ref.has_inverse?
+
+ has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
+ assert has_many_without_inverse_ref.respond_to?(:has_inverse?)
+ assert !has_many_without_inverse_ref.has_inverse?
+
+ belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
+ assert belongs_to_without_inverse_ref.respond_to?(:has_inverse?)
+ assert !belongs_to_without_inverse_ref.has_inverse?
+ end
+
+ def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of
+ has_one_ref = Man.reflect_on_association(:face)
+ assert has_one_ref.respond_to?(:inverse_of)
+
+ has_many_ref = Man.reflect_on_association(:interests)
+ assert has_many_ref.respond_to?(:inverse_of)
+
+ belongs_to_ref = Face.reflect_on_association(:man)
+ assert belongs_to_ref.respond_to?(:inverse_of)
+ end
+
+ def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
+ has_one_ref = Man.reflect_on_association(:face)
+ assert_equal Face.reflect_on_association(:man), has_one_ref.inverse_of
+
+ has_many_ref = Man.reflect_on_association(:interests)
+ assert_equal Interest.reflect_on_association(:man), has_many_ref.inverse_of
+
+ belongs_to_ref = Face.reflect_on_association(:man)
+ assert_equal Man.reflect_on_association(:face), belongs_to_ref.inverse_of
+ end
+
+ def test_associations_with_no_inverse_of_should_return_nil
+ has_one_ref = Club.reflect_on_association(:sponsor)
+ assert_nil has_one_ref.inverse_of
+
+ has_many_ref = Club.reflect_on_association(:memberships)
+ assert_nil has_many_ref.inverse_of
+
+ belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
+ assert_nil belongs_to_ref.inverse_of
+ end
+end
+
+class InverseHasOneTests < ActiveRecord::TestCase
+ fixtures :men, :faces
+
+ def test_parent_instance_should_be_shared_with_child_on_find
+ m = Man.find(:first)
+ f = m.face
+ assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
+ f.man.name = 'Mungo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+
+ def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
+ m = Man.find(:first, :include => :face)
+ f = m.face
+ assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
+ f.man.name = 'Mungo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
+
+ m = Man.find(:first, :include => :face, :order => 'faces.id')
+ f = m.face
+ assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
+ f.man.name = 'Mungo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+ def test_parent_instance_should_be_shared_with_newly_built_child
+ m = Man.find(:first)
+ f = m.build_face(:description => 'haunted')
+ assert_not_nil f.man
+ assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
+ f.man.name = 'Mungo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
+ end
+
+ def test_parent_instance_should_be_shared_with_newly_created_child
+ m = Man.find(:first)
+ f = m.create_face(:description => 'haunted')
+ assert_not_nil f.man
+ assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
+ f.man.name = 'Mungo'
+ assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
+ end
+
+ def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
+ assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).dirty_face }
+ end
+end
+
+class InverseHasManyTests < ActiveRecord::TestCase
+ fixtures :men, :interests
+
+ def test_parent_instance_should_be_shared_with_every_child_on_find
+ m = Man.find(:first)
+ is = m.interests
+ is.each do |i|
+ assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
+ i.man.name = 'Mungo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
+ end
+ end
+
+ def test_parent_instance_should_be_shared_with_eager_loaded_children
+ m = Man.find(:first, :include => :interests)
+ is = m.interests
+ is.each do |i|
+ assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
+ i.man.name = 'Mungo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+ m = Man.find(:first, :include => :interests, :order => 'interests.id')
+ is = m.interests
+ is.each do |i|
+ assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
+ i.man.name = 'Mungo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+ end
+
+ def test_parent_instance_should_be_shared_with_newly_built_child
+ m = Man.find(:first)
+ i = m.interests.build(:topic => 'Industrial Revolution Re-enactment')
+ assert_not_nil i.man
+ assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
+ i.man.name = 'Mungo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
+ end
+
+ def test_parent_instance_should_be_shared_with_newly_created_child
+ m = Man.find(:first)
+ i = m.interests.create(:topic => 'Industrial Revolution Re-enactment')
+ assert_not_nil i.man
+ assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
+ i.man.name = 'Mungo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
+ end
+
+ def test_parent_instance_should_be_shared_with_poked_in_child
+ m = Man.find(:first)
+ i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
+ m.interests << i
+ assert_not_nil i.man
+ assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
+ m.name = 'Bongo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
+ i.man.name = 'Mungo'
+ assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
+ end
+
+ def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
+ assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).secret_interests }
+ end
+end
+
+class InverseBelongsToTests < ActiveRecord::TestCase
+ fixtures :men, :faces, :interests
+
+ def test_child_instance_should_be_shared_with_parent_on_find
+ f = Face.find(:first)
+ m = f.man
+ assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
+ f.description = 'gormless'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
+ m.face.description = 'pleasing'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
+ end
+
+ def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
+ f = Face.find(:first, :include => :man)
+ m = f.man
+ assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
+ f.description = 'gormless'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
+ m.face.description = 'pleasing'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
+
+
+ f = Face.find(:first, :include => :man, :order => 'men.id')
+ m = f.man
+ assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
+ f.description = 'gormless'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
+ m.face.description = 'pleasing'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
+ end
+
+ def test_child_instance_should_be_shared_with_newly_built_parent
+ f = Face.find(:first)
+ m = f.build_man(:name => 'Charles')
+ assert_not_nil m.face
+ assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
+ f.description = 'gormless'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
+ m.face.description = 'pleasing'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to just-built-parent-owned instance"
+ end
+
+ def test_child_instance_should_be_shared_with_newly_created_parent
+ f = Face.find(:first)
+ m = f.create_man(:name => 'Charles')
+ assert_not_nil m.face
+ assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
+ f.description = 'gormless'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
+ m.face.description = 'pleasing'
+ assert_equal f.description, m.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance"
+ end
+
+ def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
+ i = Interest.find(:first)
+ m = i.man
+ assert_not_nil m.interests
+ iz = m.interests.detect {|iz| iz.id == i.id}
+ assert_not_nil iz
+ assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
+ i.topic = 'Eating cheese with a spoon'
+ assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
+ iz.topic = 'Cow tipping'
+ assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
+ end
+
+ def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
+ assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_man }
+ end
+end
+
+# NOTE - these tests might not be meaningful, ripped as they were from the parental_control plugin
+# which would guess the inverse rather than look for an explicit configuration option.
+class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
+ fixtures :men, :interests, :zines
+
+ def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models
+ assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
+ i = Interest.find(:first)
+ z = i.zine
+ m = i.man
+ end
+ end
+
+ def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
+ assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
+ i = Interest.find(:first)
+ i.build_zine(:title => 'Get Some in Winter! 2008')
+ i.build_man(:name => 'Gordon')
+ i.save!
+ end
+ end
+end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 2712b2a05c..ddca5e962d 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -12,6 +12,7 @@ require 'models/reader'
require 'models/ship'
require 'models/ship_part'
require 'models/treasure'
+require 'models/company'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_should_be_a_valid_option_for_has_one
@@ -38,6 +39,19 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
end
class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
+ fixtures :companies, :accounts
+
+ def test_should_save_parent_but_not_invalid_child
+ firm = Firm.new(:name => 'GlobalMegaCorp')
+ assert firm.valid?
+
+ firm.build_account_using_primary_key
+ assert !firm.build_account_using_primary_key.valid?
+
+ assert firm.save
+ assert firm.account_using_primary_key.new_record?
+ end
+
def test_save_fails_for_invalid_has_one
firm = Firm.find(:first)
assert firm.valid?
@@ -126,6 +140,19 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
end
class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
+ fixtures :companies
+
+ def test_should_save_parent_but_not_invalid_child
+ client = Client.new(:name => 'Joe (the Plumber)')
+ assert client.valid?
+
+ client.build_firm
+ assert !client.firm.valid?
+
+ assert client.save
+ assert client.firm.new_record?
+ end
+
def test_save_fails_for_invalid_belongs_to
assert log = AuditLog.create(:developer_id => 0, :message => "")
@@ -898,4 +925,4 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
end
include AutosaveAssociationOnACollectionAssociationTests
-end \ No newline at end of file
+end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index d97cd17d75..59aa6953e3 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'models/post'
require 'models/author'
require 'models/topic'
require 'models/reply'
@@ -12,12 +13,12 @@ require 'models/auto_id'
require 'models/column_name'
require 'models/subscriber'
require 'models/keyboard'
-require 'models/post'
require 'models/comment'
require 'models/minimalistic'
require 'models/warehouse_thing'
require 'models/parrot'
require 'rexml/document'
+require 'active_support/core_ext/exception'
class Category < ActiveRecord::Base; end
class Categorization < ActiveRecord::Base; end
@@ -1115,7 +1116,7 @@ class BasicsTest < ActiveRecord::TestCase
Time.zone = nil
Topic.skip_time_zone_conversion_for_attributes = []
end
-
+
def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.default_timezone = :utc
@@ -1439,7 +1440,7 @@ class BasicsTest < ActiveRecord::TestCase
topic = Topic.create("content" => myobj).reload
assert_equal(myobj, topic.content)
end
-
+
def test_serialized_string_attribute
myobj = "Yes"
topic = Topic.create("content" => myobj).reload
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 56dcdea110..fd455e0864 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -233,7 +233,7 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 8, c['Jadedpixel']
end
- def test_should_group_by_summed_field_with_conditions_and_having
+ def test_should_group_by_summed_field_through_association_and_having
c = companies(:rails_core).companies.sum(:id, :group => :name,
:having => 'sum(id) > 7')
assert_nil c['Leetsoft']
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index d8778957c0..037b67ec84 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'models/post'
require 'models/author'
require 'models/categorization'
require 'models/comment'
@@ -6,11 +7,10 @@ require 'models/company'
require 'models/topic'
require 'models/reply'
require 'models/entrant'
+require 'models/project'
require 'models/developer'
-require 'models/post'
require 'models/customer'
require 'models/job'
-require 'models/categorization'
class DynamicFinderMatchTest < ActiveRecord::TestCase
def test_find_no_match
@@ -64,7 +64,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
end
class FinderTest < ActiveRecord::TestCase
- fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers
+ fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations
def test_find
assert_equal(topics(:first).title, Topic.find(1).title)
@@ -94,16 +94,16 @@ class FinderTest < ActiveRecord::TestCase
assert_raise(NoMethodError) { Topic.exists?([1,2]) }
end
-
+
def test_exists_returns_true_with_one_record_and_no_args
assert Topic.exists?
end
-
+
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
end
-
+
def test_exists_with_aggregate_having_three_mappings
existing_address = customers(:david).address
assert Customer.exists?(:address => existing_address)
@@ -119,6 +119,12 @@ class FinderTest < ActiveRecord::TestCase
Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
end
+ def test_exists_with_scoped_include
+ Developer.with_scope(:find => { :include => :projects, :order => "projects.name" }) do
+ assert Developer.exists?
+ end
+ end
+
def test_find_by_array_of_one_id
assert_kind_of(Array, Topic.find([ 1 ]))
assert_equal(1, Topic.find([ 1 ]).length)
@@ -485,8 +491,9 @@ class FinderTest < ActiveRecord::TestCase
assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
end
- def test_bind_string
- assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '')
+ def test_bind_empty_string
+ quoted_empty = ActiveRecord::Base.connection.quote('')
+ assert_equal quoted_empty, bind('?', '')
end
def test_bind_chars
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 252bf4ff61..b07d4f3521 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -518,6 +518,11 @@ class FoxyFixturesTest < ActiveRecord::TestCase
assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo))
end
+ def test_identifies_consistently
+ assert_equal 1281023246, Fixtures.identify(:ruby)
+ assert_equal 2140105598, Fixtures.identify(:sapphire_2)
+ end
+
TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
def test_populates_timestamp_columns
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index a4f05f0c72..c30186286d 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -5,6 +5,7 @@ require 'config'
require 'rubygems'
require 'test/unit'
+require 'stringio'
gem 'mocha', '>= 0.9.5'
require 'mocha'
@@ -15,6 +16,11 @@ require 'connection'
require 'cases/repair_helper'
+begin
+ require 'ruby-debug'
+rescue LoadError
+end
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
@@ -67,3 +73,20 @@ class ActiveSupport::TestCase
Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
end
end
+
+# silence verbose schema loading
+original_stdout = $stdout
+$stdout = StringIO.new
+
+begin
+ adapter_name = ActiveRecord::Base.connection.adapter_name.downcase
+ adapter_specific_schema_file = SCHEMA_ROOT + "/#{adapter_name}_specific_schema.rb"
+
+ load SCHEMA_ROOT + "/schema.rb"
+
+ if File.exists?(adapter_specific_schema_file)
+ load adapter_specific_schema_file
+ end
+ensure
+ $stdout = original_stdout
+end
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index 975acde096..0ffbc9bf3b 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -26,19 +26,19 @@ class JsonSerializationTest < ActiveRecord::TestCase
NamespacedContact.include_root_in_json = true
@contact = NamespacedContact.new :name => 'whatever'
json = @contact.to_json
- assert_match %r{^\{"namespaced_contact": \{}, json
+ assert_match %r{^\{"namespaced_contact":\{}, json
end
def test_should_include_root_in_json
Contact.include_root_in_json = true
json = @contact.to_json
- assert_match %r{^\{"contact": \{}, json
- assert_match %r{"name": "Konata Izumi"}, json
- assert_match %r{"age": 16}, json
- assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
- assert_match %r{"awesome": true}, json
- assert_match %r{"preferences": \{"shows": "anime"\}}, json
+ assert_match %r{^\{"contact":\{}, json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_match %r{"awesome":true}, json
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
ensure
Contact.include_root_in_json = false
end
@@ -46,31 +46,31 @@ class JsonSerializationTest < ActiveRecord::TestCase
def test_should_encode_all_encodable_attributes
json = @contact.to_json
- assert_match %r{"name": "Konata Izumi"}, json
- assert_match %r{"age": 16}, json
- assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
- assert_match %r{"awesome": true}, json
- assert_match %r{"preferences": \{"shows": "anime"\}}, json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_match %r{"awesome":true}, json
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
end
def test_should_allow_attribute_filtering_with_only
json = @contact.to_json(:only => [:name, :age])
- assert_match %r{"name": "Konata Izumi"}, json
- assert_match %r{"age": 16}, json
- assert_no_match %r{"awesome": true}, json
- assert !json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
- assert_no_match %r{"preferences": \{"shows": "anime"\}}, json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert_no_match %r{"awesome":true}, json
+ assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_no_match %r{"preferences":\{"shows":"anime"\}}, json
end
def test_should_allow_attribute_filtering_with_except
json = @contact.to_json(:except => [:name, :age])
- assert_no_match %r{"name": "Konata Izumi"}, json
- assert_no_match %r{"age": 16}, json
- assert_match %r{"awesome": true}, json
- assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
- assert_match %r{"preferences": \{"shows": "anime"\}}, json
+ assert_no_match %r{"name":"Konata Izumi"}, json
+ assert_no_match %r{"age":16}, json
+ assert_match %r{"awesome":true}, json
+ assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
end
def test_methods_are_called_on_object
@@ -79,12 +79,12 @@ class JsonSerializationTest < ActiveRecord::TestCase
def @contact.favorite_quote; "Constraints are liberating"; end
# Single method.
- assert_match %r{"label": "Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
+ assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
# Both methods.
methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
- assert_match %r{"label": "Has cheezburger"}, methods_json
- assert_match %r{"favorite_quote": "Constraints are liberating"}, methods_json
+ assert_match %r{"label":"Has cheezburger"}, methods_json
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
end
end
@@ -99,42 +99,42 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
def test_includes_uses_association_name
json = @david.to_json(:include => :posts)
- assert_match %r{"posts": \[}, json
+ assert_match %r{"posts":\[}, json
- assert_match %r{"id": 1}, json
- assert_match %r{"name": "David"}, json
+ assert_match %r{"id":1}, json
+ assert_match %r{"name":"David"}, json
- assert_match %r{"author_id": 1}, json
- assert_match %r{"title": "Welcome to the weblog"}, json
- assert_match %r{"body": "Such a lovely day"}, json
+ assert_match %r{"author_id":1}, json
+ assert_match %r{"title":"Welcome to the weblog"}, json
+ assert_match %r{"body":"Such a lovely day"}, json
- assert_match %r{"title": "So I was thinking"}, json
- assert_match %r{"body": "Like I hopefully always am"}, json
+ assert_match %r{"title":"So I was thinking"}, json
+ assert_match %r{"body":"Like I hopefully always am"}, json
end
def test_includes_uses_association_name_and_applies_attribute_filters
json = @david.to_json(:include => { :posts => { :only => :title } })
- assert_match %r{"name": "David"}, json
- assert_match %r{"posts": \[}, json
+ assert_match %r{"name":"David"}, json
+ assert_match %r{"posts":\[}, json
- assert_match %r{"title": "Welcome to the weblog"}, json
- assert_no_match %r{"body": "Such a lovely day"}, json
+ assert_match %r{"title":"Welcome to the weblog"}, json
+ assert_no_match %r{"body":"Such a lovely day"}, json
- assert_match %r{"title": "So I was thinking"}, json
- assert_no_match %r{"body": "Like I hopefully always am"}, json
+ assert_match %r{"title":"So I was thinking"}, json
+ assert_no_match %r{"body":"Like I hopefully always am"}, json
end
def test_includes_fetches_second_level_associations
json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } })
- assert_match %r{"name": "David"}, json
- assert_match %r{"posts": \[}, json
+ assert_match %r{"name":"David"}, json
+ assert_match %r{"posts":\[}, json
- assert_match %r{"comments": \[}, json
- assert_match %r{\{"body": "Thank you again for the welcome"\}}, json
- assert_match %r{\{"body": "Don't think too hard"\}}, json
- assert_no_match %r{"post_id": }, json
+ assert_match %r{"comments":\[}, json
+ assert_match %r{\{"body":"Thank you again for the welcome"\}}, json
+ assert_match %r{\{"body":"Don't think too hard"\}}, json
+ assert_no_match %r{"post_id":}, json
end
def test_includes_fetches_nth_level_associations
@@ -151,11 +151,11 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
}
})
- assert_match %r{"name": "David"}, json
- assert_match %r{"posts": \[}, json
+ assert_match %r{"name":"David"}, json
+ assert_match %r{"posts":\[}, json
- assert_match %r{"taggings": \[}, json
- assert_match %r{"tag": \{"name": "General"\}}, json
+ assert_match %r{"taggings":\[}, json
+ assert_match %r{"tag":\{"name":"General"\}}, json
end
def test_should_not_call_methods_on_associations_that_dont_respond
@@ -163,20 +163,20 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
json = @david.to_json(:include => :posts, :methods => :favorite_quote)
assert !@david.posts.first.respond_to?(:favorite_quote)
- assert_match %r{"favorite_quote": "Constraints are liberating"}, json
- assert_equal %r{"favorite_quote": }.match(json).size, 1
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, json
+ assert_equal %r{"favorite_quote":}.match(json).size, 1
end
def test_should_allow_only_option_for_list_of_authors
authors = [@david, @mary]
- assert_equal %([{"name": "David"}, {"name": "Mary"}]), authors.to_json(:only => :name)
+ assert_equal %([{"name":"David"},{"name":"Mary"}]), authors.to_json(:only => :name)
end
def test_should_allow_except_option_for_list_of_authors
authors = [@david, @mary]
- assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id])
+ assert_equal %([{"id":1},{"id":2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id])
end
def test_should_allow_includes_for_list_of_authors
@@ -188,8 +188,8 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
}
)
- ['"name": "David"', '"posts": [', '{"id": 1}', '{"id": 2}', '{"id": 4}',
- '{"id": 5}', '{"id": 6}', '"name": "Mary"', '"posts": [{"id": 7}]'].each do |fragment|
+ ['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}',
+ '{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[{"id":7}]'].each do |fragment|
assert json.include?(fragment), json
end
end
@@ -200,6 +200,6 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
2 => @mary
}
- assert_equal %({"1": {"name": "David"}}), authors_hash.to_json(:only => [1, :name])
+ assert_equal %({"1":{"name":"David"}}), authors_hash.to_json(:only => [1, :name])
end
end
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 3c34cdeade..d8246f49b8 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -1,9 +1,9 @@
require "cases/helper"
+require 'models/post'
require 'models/author'
require 'models/developer'
require 'models/project'
require 'models/comment'
-require 'models/post'
require 'models/category'
class MethodScopingTest < ActiveRecord::TestCase
@@ -591,6 +591,16 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
+ def test_default_scope_with_conditions_string
+ assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
+ assert_equal nil, DeveloperCalledDavid.create!.name
+ end
+
+ def test_default_scope_with_conditions_hash
+ assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
+ assert_equal 'Jamis', DeveloperCalledJamis.create!.name
+ end
+
def test_default_scoping_with_threads
scope = [{ :create => {}, :find => { :order => 'salary DESC' } }]
@@ -628,9 +638,9 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
- def test_named_scope
- expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary }
- received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.salary }
+ def test_named_scope_overwrites_default
+ expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name }
+ received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
assert_equal expected, received
end
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index ae6a54a5bd..92fe48cb5a 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'active_support/core_ext/array/random_access'
require 'models/post'
require 'models/topic'
require 'models/comment'
@@ -234,6 +235,40 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_no_queries { assert topics.any? }
end
+ def test_many_should_not_load_results
+ topics = Topic.base
+ assert_queries(2) do
+ topics.many? # use count query
+ topics.collect # force load
+ topics.many? # use loaded (no query)
+ end
+ end
+
+ def test_many_should_call_proxy_found_if_using_a_block
+ topics = Topic.base
+ assert_queries(1) do
+ topics.expects(:size).never
+ topics.many? { true }
+ end
+ end
+
+ def test_many_should_not_fire_query_if_named_scope_loaded
+ topics = Topic.base
+ topics.collect # force load
+ assert_no_queries { assert topics.many? }
+ end
+
+ def test_many_should_return_false_if_none_or_one
+ topics = Topic.base.scoped(:conditions => {:id => 0})
+ assert !topics.many?
+ topics = Topic.base.scoped(:conditions => {:id => 1})
+ assert !topics.many?
+ end
+
+ def test_many_should_return_true_if_more_than_one
+ assert Topic.base.many?
+ end
+
def test_should_build_with_proxy_options
topic = Topic.approved.build({})
assert topic.approved
@@ -265,7 +300,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_rand_should_select_a_random_object_from_proxy
- assert Topic.approved.rand.is_a?(Topic)
+ assert_kind_of Topic, Topic.approved.rand
end
def test_should_use_where_in_query_for_named_scope
@@ -338,7 +373,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
end
-class DynamicScopeMatchTest < ActiveRecord::TestCase
+class DynamicScopeMatchTest < ActiveRecord::TestCase
def test_scoped_by_no_match
assert_nil ActiveRecord::DynamicScopeMatch.match("not_scoped_at_all")
end
@@ -352,6 +387,8 @@ class DynamicScopeMatchTest < ActiveRecord::TestCase
end
class DynamicScopeTest < ActiveRecord::TestCase
+ fixtures :posts
+
def test_dynamic_scope
assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1)
assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index cd6277c24b..f31275163d 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -4,6 +4,7 @@ require "models/ship"
require "models/bird"
require "models/parrot"
require "models/treasure"
+require 'active_support/hash_with_indifferent_access'
module AssertRaiseWithMessage
def assert_raise_with_message(expected_exception, expected_message)
@@ -31,11 +32,27 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
end
def test_should_add_a_proc_to_reject_new_nested_attributes_procs
- [:parrots, :birds].each do |name|
+ [:parrots, :birds, :birds_with_reject_all_blank].each do |name|
assert_instance_of Proc, Pirate.reject_new_nested_attributes_procs[name]
end
end
+ def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
+ pirate.save!
+
+ assert pirate.birds_with_reject_all_blank.empty?
+ end
+
+ def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}]
+ pirate.save!
+
+ assert_equal 1, pirate.birds_with_reject_all_blank.count
+ end
+
def test_should_raise_an_ArgumentError_for_non_existing_associations
assert_raise_with_message ArgumentError, "No association found for name `honesty'. Has it been defined yet?" do
Pirate.accepts_nested_attributes_for :honesty
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index 2649a9358a..bb9013c2a1 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -1,4 +1,6 @@
require "cases/helper"
+require "models/project"
+require "timeout"
class PooledConnectionsTest < ActiveRecord::TestCase
def setup
@@ -89,6 +91,33 @@ class PooledConnectionsTest < ActiveRecord::TestCase
ensure
ActiveRecord::Base.connection_handler = old_handler
end
+
+ def test_with_connection_nesting_safety
+ ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1, :wait_timeout => 0.1}))
+
+ before_count = Project.count
+
+ add_record('one')
+
+ ActiveRecord::Base.connection.transaction do
+ add_record('two')
+ # Have another thread try to screw up the transaction
+ Thread.new do
+ ActiveRecord::Base.connection.rollback_db_transaction
+ ActiveRecord::Base.connection_pool.release_connection
+ end.join rescue nil
+ add_record('three')
+ end
+
+ after_count = Project.count
+ assert_equal 3, after_count - before_count
+ end
+
+ private
+
+ def add_record(name)
+ ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name }
+ end
end unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name
class AllowConcurrencyDeprecatedTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/repair_helper.rb b/activerecord/test/cases/repair_helper.rb
index 0155668811..80d04010d6 100644
--- a/activerecord/test/cases/repair_helper.rb
+++ b/activerecord/test/cases/repair_helper.rb
@@ -1,11 +1,7 @@
module ActiveRecord
module Testing
module RepairHelper
- def self.included(base)
- base.class_eval do
- extend ClassMethods
- end
- end
+ extend ActiveSupport::Concern
module Toolbox
def self.record_validations(*model_classes)
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 4b34a65bb7..0303147e50 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -38,12 +38,21 @@ class I18nValidationTest < ActiveRecord::TestCase
end
end
- def test_default_error_messages_is_deprecated
- assert_deprecated('Errors.default_error_messages') do
- ActiveRecord::Errors.default_error_messages
+ def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated
+ assert_not_deprecated do
+ default = "%s interpolation syntax was deprecated"
+ assert_equal default, I18n.t(:does_not_exist, :default => default, :value => 'this')
end
end
+ def test_percent_d_interpolation_syntax_in_error_messages_was_deprecated
+ assert_not_deprecated do
+ default = "%d interpolation syntaxes are deprecated"
+ assert_equal default, I18n.t(:does_not_exist, :default => default, :count => 2)
+ end
+ end
+
+ # ActiveRecord::Errors
def test_errors_generate_message_translates_custom_model_attribute_key
I18n.expects(:translate).with(
:topic,
diff --git a/activerecord/test/fixtures/faces.yml b/activerecord/test/fixtures/faces.yml
new file mode 100644
index 0000000000..1dd2907cf7
--- /dev/null
+++ b/activerecord/test/fixtures/faces.yml
@@ -0,0 +1,7 @@
+trusting:
+ description: trusting
+ man: gordon
+
+weather_beaten:
+ description: weather beaten
+ man: steve
diff --git a/activerecord/test/fixtures/interests.yml b/activerecord/test/fixtures/interests.yml
new file mode 100644
index 0000000000..ec71890ab6
--- /dev/null
+++ b/activerecord/test/fixtures/interests.yml
@@ -0,0 +1,29 @@
+trainspotting:
+ topic: Trainspotting
+ zine: staying_in
+ man: gordon
+
+birdwatching:
+ topic: Birdwatching
+ zine: staying_in
+ man: gordon
+
+stamp_collecting:
+ topic: Stamp Collecting
+ zine: staying_in
+ man: gordon
+
+hunting:
+ topic: Hunting
+ zine: going_out
+ man: steve
+
+woodsmanship:
+ topic: Woodsmanship
+ zine: going_out
+ man: steve
+
+survial:
+ topic: Survival
+ zine: going_out
+ man: steve
diff --git a/activerecord/test/fixtures/men.yml b/activerecord/test/fixtures/men.yml
new file mode 100644
index 0000000000..c67429f925
--- /dev/null
+++ b/activerecord/test/fixtures/men.yml
@@ -0,0 +1,5 @@
+gordon:
+ name: Gordon
+
+steve:
+ name: Steve
diff --git a/activerecord/test/fixtures/people.yml b/activerecord/test/fixtures/people.yml
index 3babb1fe59..123673a2af 100644
--- a/activerecord/test/fixtures/people.yml
+++ b/activerecord/test/fixtures/people.yml
@@ -2,14 +2,17 @@ michael:
id: 1
first_name: Michael
primary_contact_id: 2
+ number1_fan_id: 3
gender: M
david:
id: 2
first_name: David
primary_contact_id: 3
+ number1_fan_id: 1
gender: M
susan:
id: 3
first_name: Susan
primary_contact_id: 2
- gender: F \ No newline at end of file
+ number1_fan_id: 1
+ gender: F
diff --git a/activerecord/test/fixtures/zines.yml b/activerecord/test/fixtures/zines.yml
new file mode 100644
index 0000000000..07dce4db7e
--- /dev/null
+++ b/activerecord/test/fixtures/zines.yml
@@ -0,0 +1,5 @@
+staying_in:
+ title: Staying in '08
+
+going_out:
+ title: Outdoor Pursuits 2k+8
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 1122c7256f..8b84c2fb5e 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/misc'
+
module MyApplication
module Business
class Company < ActiveRecord::Base
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 92039a4f54..058970336b 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -89,3 +89,13 @@ class DeveloperOrderedBySalary < ActiveRecord::Base
end
end
end
+
+class DeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope :conditions => "name = 'David'"
+end
+
+class DeveloperCalledJamis < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope :conditions => { :name => 'Jamis' }
+end
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
new file mode 100644
index 0000000000..1540dbf741
--- /dev/null
+++ b/activerecord/test/models/face.rb
@@ -0,0 +1,5 @@
+class Face < ActiveRecord::Base
+ belongs_to :man, :inverse_of => :face
+ # This is a "broken" inverse_of for the purposes of testing
+ belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face
+end
diff --git a/activerecord/test/models/interest.rb b/activerecord/test/models/interest.rb
new file mode 100644
index 0000000000..d8291d00cc
--- /dev/null
+++ b/activerecord/test/models/interest.rb
@@ -0,0 +1,4 @@
+class Interest < ActiveRecord::Base
+ belongs_to :man, :inverse_of => :interests
+ belongs_to :zine, :inverse_of => :interests
+end
diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb
new file mode 100644
index 0000000000..f40bc9d0fc
--- /dev/null
+++ b/activerecord/test/models/man.rb
@@ -0,0 +1,7 @@
+class Man < ActiveRecord::Base
+ has_one :face, :inverse_of => :man
+ has_many :interests, :inverse_of => :man
+ # These are "broken" inverse_of associations for the purposes of testing
+ has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man
+ has_many :secret_interests, :class_name => 'Interest', :inverse_of => :secret_man
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index ec2f684a6e..57fa6418f1 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -10,6 +10,7 @@ class Person < ActiveRecord::Base
belongs_to :primary_contact, :class_name => 'Person'
has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id'
+ belongs_to :number1_fan, :class_name => 'Person'
named_scope :males, :conditions => { :gender => 'M' }
named_scope :females, :conditions => { :gender => 'F' }
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 238917bf30..acf53fce8b 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -28,11 +28,13 @@ class Pirate < ActiveRecord::Base
:after_add => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}"},
:before_remove => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"},
:after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
+ has_many :birds_with_reject_all_blank, :class_name => "Bird"
accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
:birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true
+ accepts_nested_attributes_for :birds_with_reject_all_blank, :reject_if => :all_blank
validates_presence_of :catchphrase
diff --git a/activerecord/test/models/zine.rb b/activerecord/test/models/zine.rb
new file mode 100644
index 0000000000..c2d0fdaf25
--- /dev/null
+++ b/activerecord/test/models/zine.rb
@@ -0,0 +1,3 @@
+class Zine < ActiveRecord::Base
+ has_many :interests, :inverse_of => :zine
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 98e6d192a5..6e8813d8ab 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -1,4 +1,3 @@
-
ActiveRecord::Schema.define do
def except(adapter_names_to_exclude)
unless [adapter_names_to_exclude].flatten.include?(adapter_name)
@@ -57,6 +56,7 @@ ActiveRecord::Schema.define do
create_table :birds, :force => true do |t|
t.string :name
+ t.string :color
t.integer :pirate_id
end
@@ -324,6 +324,7 @@ ActiveRecord::Schema.define do
t.string :first_name, :null => false
t.references :primary_contact
t.string :gender, :limit => 1
+ t.references :number1_fan
t.integer :lock_version, :null => false, :default => 0
end
@@ -467,6 +468,26 @@ ActiveRecord::Schema.define do
end
end
+ # NOTE - the following 4 tables are used by models that have :inverse_of options on the associations
+ create_table :men, :force => true do |t|
+ t.string :name
+ end
+
+ create_table :faces, :force => true do |t|
+ t.string :description
+ t.integer :man_id
+ end
+
+ create_table :interests, :force => true do |t|
+ t.string :topic
+ t.integer :man_id
+ t.integer :zine_id
+ end
+
+ create_table :zines, :force => true do |t|
+ t.string :title
+ end
+
except 'SQLite' do
# fk_test_has_fk should be before fk_test_has_pk
create_table :fk_test_has_fk, :force => true do |t|
@@ -479,3 +500,7 @@ ActiveRecord::Schema.define do
execute "ALTER TABLE fk_test_has_fk ADD CONSTRAINT fk_name FOREIGN KEY (#{quote_column_name 'fk_id'}) REFERENCES #{quote_table_name 'fk_test_has_pk'} (#{quote_column_name 'id'})"
end
end
+
+Course.connection.create_table :courses, :force => true do |t|
+ t.column :name, :string, :null => false
+end
diff --git a/activerecord/test/schema/schema2.rb b/activerecord/test/schema/schema2.rb
deleted file mode 100644
index 8527f7ba8f..0000000000
--- a/activerecord/test/schema/schema2.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-ActiveRecord::Schema.define do
-
- Course.connection.create_table :courses, :force => true do |t|
- t.column :name, :string, :null => false
- end
-end
diff --git a/activeresource/Rakefile b/activeresource/Rakefile
index bf7bbb0201..eb5b1dd1ac 100644
--- a/activeresource/Rakefile
+++ b/activeresource/Rakefile
@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require 'rake/contrib/sshpublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version')
@@ -35,6 +34,13 @@ Rake::TestTask.new { |t|
t.verbose = true
t.warning = true
}
+task :isolated_test do
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ activesupport_path = "#{File.dirname(__FILE__)}/../activesupport/lib"
+ Dir.glob("test/**/*_test.rb").all? do |file|
+ system(ruby, "-Ilib:test:#{activesupport_path}", file)
+ end or raise "Failures"
+end
# Generate the RDoc documentation
@@ -117,12 +123,14 @@ end
desc "Publish the beta gem"
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/ar", "doc").upload
end
diff --git a/activeresource/examples/simple.rb b/activeresource/examples/simple.rb
new file mode 100644
index 0000000000..b20ef61670
--- /dev/null
+++ b/activeresource/examples/simple.rb
@@ -0,0 +1,16 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'active_resource'
+require 'active_resource/http_mock'
+require 'active_support/core_ext/hash/conversions'
+
+ActiveSupport::XmlMini.backend = ENV['XMLMINI'] || 'REXML'
+ActiveResource::HttpMock.respond_to do |mock|
+ mock.get '/people/1.xml', {}, { :id => 1, :name => 'bob' }.to_xml(:root => 'person')
+end
+
+class Person < ActiveResource::Base
+ self.site = 'http://localhost/'
+end
+
+bob = Person.find(1)
+puts bob.inspect
diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb
index f0d30b1624..b46801affc 100644
--- a/activeresource/lib/active_resource.rb
+++ b/activeresource/lib/active_resource.rb
@@ -21,15 +21,9 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+require 'active_support'
begin
require 'active_model'
@@ -44,8 +38,5 @@ require 'active_resource/validations'
require 'active_resource/custom_methods'
module ActiveResource
- Base.class_eval do
- include Validations
- include CustomMethods
- end
+ autoload :Base, 'active_resource/base'
end
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 6cb5beb789..dc24e713ff 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -1,8 +1,17 @@
-require 'active_resource/connection'
-require 'cgi'
+require 'active_support'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/module/attr_accessor_with_default'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/object/misc'
require 'set'
module ActiveResource
+ autoload :Formats, 'active_resource/formats'
+ autoload :Connection, 'active_resource/connection'
+
# ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application.
#
# For an outline of what Active Resource is capable of, see link:files/vendor/rails/activeresource/README.html.
@@ -298,7 +307,7 @@ module ActiveResource
# Returns the current format, default is ActiveResource::Formats::XmlFormat.
def format
- read_inheritable_attribute(:format) || ActiveResource::Formats[:xml]
+ read_inheritable_attribute(:format) || ActiveResource::Formats::XmlFormat
end
# Sets the number of seconds after which requests to the REST API should time out.
@@ -887,7 +896,7 @@ module ActiveResource
# person.to_json(:except => ["first_name"])
# # => {"last_name": "Smith"}
def to_json(options={})
- attributes.to_json(options)
+ ActiveSupport::JSON.encode(attributes, options)
end
# Returns the serialized string representation of the resource in the configured
@@ -895,7 +904,7 @@ module ActiveResource
# applicable depend on the configured encoding format.
def encode(options={})
case self.class.format
- when ActiveResource::Formats[:xml]
+ when ActiveResource::Formats::XmlFormat
self.class.format.encode(attributes, {:root => self.class.element_name}.merge(options))
else
self.class.format.encode(attributes, options)
@@ -1020,7 +1029,7 @@ module ActiveResource
private
# Tries to find a resource for a given collection name; if it fails, then the resource is created
def find_or_create_resource_for_collection(name)
- find_or_create_resource_for(name.to_s.singularize)
+ find_or_create_resource_for(ActiveSupport::Inflector.singularize(name.to_s))
end
# Tries to find a resource in a non empty list of nested modules
@@ -1061,6 +1070,11 @@ module ActiveResource
self.class.__send__(:split_options, options)
end
+ # For compatibility with ActiveSupport::JSON.encode
+ def rails_to_json(options, *args)
+ to_json(options)
+ end
+
def method_missing(method_symbol, *arguments) #:nodoc:
method_name = method_symbol.to_s
@@ -1075,3 +1089,6 @@ module ActiveResource
end
end
end
+
+require 'active_resource/validations'
+require 'active_resource/custom_methods'
diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb
index 80d5c95b68..6661469c5b 100644
--- a/activeresource/lib/active_resource/connection.rb
+++ b/activeresource/lib/active_resource/connection.rb
@@ -1,64 +1,12 @@
+require 'active_resource/exceptions'
+require 'active_resource/formats'
+require 'active_support/core_ext/benchmark'
require 'net/https'
require 'date'
require 'time'
require 'uri'
-require 'benchmark'
module ActiveResource
- class ConnectionError < StandardError # :nodoc:
- attr_reader :response
-
- def initialize(response, message = nil)
- @response = response
- @message = message
- end
-
- def to_s
- "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
- end
- end
-
- # Raised when a Timeout::Error occurs.
- class TimeoutError < ConnectionError
- def initialize(message)
- @message = message
- end
- def to_s; @message ;end
- end
-
- # 3xx Redirection
- class Redirection < ConnectionError # :nodoc:
- def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
- end
-
- # 4xx Client Error
- class ClientError < ConnectionError; end # :nodoc:
-
- # 400 Bad Request
- class BadRequest < ClientError; end # :nodoc
-
- # 401 Unauthorized
- class UnauthorizedAccess < ClientError; end # :nodoc
-
- # 403 Forbidden
- class ForbiddenAccess < ClientError; end # :nodoc
-
- # 404 Not Found
- class ResourceNotFound < ClientError; end # :nodoc:
-
- # 409 Conflict
- class ResourceConflict < ClientError; end # :nodoc:
-
- # 5xx Server Error
- class ServerError < ConnectionError; end # :nodoc:
-
- # 405 Method Not Allowed
- class MethodNotAllowed < ClientError # :nodoc:
- def allowed_methods
- @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
- end
- end
-
# Class to handle connections to remote web services.
# This class is used by ActiveResource::Base to interface with REST
# services.
@@ -81,7 +29,7 @@ module ActiveResource
# The +site+ parameter is required and will set the +site+
# attribute to the URI for the remote resource service.
- def initialize(site, format = ActiveResource::Formats[:xml])
+ def initialize(site, format = ActiveResource::Formats::XmlFormat)
raise ArgumentError, 'Missing site URI' unless site
@user = @password = nil
self.site = site
diff --git a/activeresource/lib/active_resource/custom_methods.rb b/activeresource/lib/active_resource/custom_methods.rb
index 4647e8342c..0d05d06035 100644
--- a/activeresource/lib/active_resource/custom_methods.rb
+++ b/activeresource/lib/active_resource/custom_methods.rb
@@ -117,4 +117,8 @@ module ActiveResource
end
end
end
+
+ class Base
+ include CustomMethods
+ end
end
diff --git a/activeresource/lib/active_resource/exceptions.rb b/activeresource/lib/active_resource/exceptions.rb
new file mode 100644
index 0000000000..5e4b1d4487
--- /dev/null
+++ b/activeresource/lib/active_resource/exceptions.rb
@@ -0,0 +1,55 @@
+module ActiveResource
+ class ConnectionError < StandardError # :nodoc:
+ attr_reader :response
+
+ def initialize(response, message = nil)
+ @response = response
+ @message = message
+ end
+
+ def to_s
+ "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
+ end
+ end
+
+ # Raised when a Timeout::Error occurs.
+ class TimeoutError < ConnectionError
+ def initialize(message)
+ @message = message
+ end
+ def to_s; @message ;end
+ end
+
+ # 3xx Redirection
+ class Redirection < ConnectionError # :nodoc:
+ def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
+ end
+
+ # 4xx Client Error
+ class ClientError < ConnectionError; end # :nodoc:
+
+ # 400 Bad Request
+ class BadRequest < ClientError; end # :nodoc
+
+ # 401 Unauthorized
+ class UnauthorizedAccess < ClientError; end # :nodoc
+
+ # 403 Forbidden
+ class ForbiddenAccess < ClientError; end # :nodoc
+
+ # 404 Not Found
+ class ResourceNotFound < ClientError; end # :nodoc:
+
+ # 409 Conflict
+ class ResourceConflict < ClientError; end # :nodoc:
+
+ # 5xx Server Error
+ class ServerError < ConnectionError; end # :nodoc:
+
+ # 405 Method Not Allowed
+ class MethodNotAllowed < ClientError # :nodoc:
+ def allowed_methods
+ @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
+ end
+ end
+end
diff --git a/activeresource/lib/active_resource/formats.rb b/activeresource/lib/active_resource/formats.rb
index 28864cf588..53b75b34e7 100644
--- a/activeresource/lib/active_resource/formats.rb
+++ b/activeresource/lib/active_resource/formats.rb
@@ -1,14 +1,14 @@
module ActiveResource
module Formats
+ autoload :XmlFormat, 'active_resource/formats/xml_format'
+ autoload :JsonFormat, 'active_resource/formats/json_format'
+
# Lookup the format class from a mime type reference symbol. Example:
#
# ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
# ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
def self.[](mime_type_reference)
- ActiveResource::Formats.const_get(mime_type_reference.to_s.camelize + "Format")
+ ActiveResource::Formats.const_get(ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format")
end
end
end
-
-require 'active_resource/formats/xml_format'
-require 'active_resource/formats/json_format' \ No newline at end of file
diff --git a/activeresource/lib/active_resource/formats/json_format.rb b/activeresource/lib/active_resource/formats/json_format.rb
index 1d88fc5f16..101027d99e 100644
--- a/activeresource/lib/active_resource/formats/json_format.rb
+++ b/activeresource/lib/active_resource/formats/json_format.rb
@@ -1,3 +1,5 @@
+require 'active_support/json'
+
module ActiveResource
module Formats
module JsonFormat
diff --git a/activeresource/lib/active_resource/formats/xml_format.rb b/activeresource/lib/active_resource/formats/xml_format.rb
index 86c6cec745..3b2575cfa1 100644
--- a/activeresource/lib/active_resource/formats/xml_format.rb
+++ b/activeresource/lib/active_resource/formats/xml_format.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/conversions'
+
module ActiveResource
module Formats
module XmlFormat
diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb
index 7d7e378436..aae2d6508c 100644
--- a/activeresource/lib/active_resource/http_mock.rb
+++ b/activeresource/lib/active_resource/http_mock.rb
@@ -1,4 +1,5 @@
require 'active_resource/connection'
+require 'active_support/core_ext/kernel/reporting'
module ActiveResource
class InvalidRequestError < StandardError; end #:nodoc:
@@ -129,7 +130,11 @@ module ActiveResource
def #{method}(path, #{'body, ' if has_body}headers)
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers)
self.class.requests << request
- self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for \#{request}"))
+ if response = self.class.responses.assoc(request)
+ response[1]
+ else
+ raise InvalidRequestError.new("No response recorded for \#{request}")
+ end
end
EOE
end
diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb
index 9d3d45b010..95c5467647 100644
--- a/activeresource/lib/active_resource/validations.rb
+++ b/activeresource/lib/active_resource/validations.rb
@@ -1,3 +1,6 @@
+require 'active_resource/exceptions'
+require 'active_support/core_ext/array/wrap'
+
module ActiveResource
class ResourceInvalid < ClientError #:nodoc:
end
@@ -77,4 +80,8 @@ module ActiveResource
@errors ||= Errors.new(self)
end
end
+
+ class Base
+ include Validations
+ end
end
diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb
index ce9371d050..3398f2dac7 100644
--- a/activeresource/test/abstract_unit.rb
+++ b/activeresource/test/abstract_unit.rb
@@ -5,22 +5,16 @@ gem 'mocha', '>= 0.9.5'
require 'mocha'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
-$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
require 'active_resource'
require 'active_resource/http_mock'
$:.unshift "#{File.dirname(__FILE__)}/../test"
require 'setter_trap'
+require 'logger'
ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log")
-# Show backtraces for deprecated behavior for quicker cleanup.
-ActiveSupport::Deprecation.debug = true
-
-def uses_gem(gem_name, test_name, version = '> 0')
- gem gem_name.to_s, version
- require gem_name.to_s
- yield
+begin
+ require 'ruby-debug'
rescue LoadError
- $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again."
end
diff --git a/activeresource/test/base/custom_methods_test.rb b/activeresource/test/base/custom_methods_test.rb
index 61887f4ec7..2d81549a65 100644
--- a/activeresource/test/base/custom_methods_test.rb
+++ b/activeresource/test/base/custom_methods_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'fixtures/person'
require 'fixtures/street_address'
+require 'active_support/core_ext/hash/conversions'
class CustomMethodsTest < Test::Unit::TestCase
def setup
diff --git a/activeresource/test/base/load_test.rb b/activeresource/test/base/load_test.rb
index a475fab34e..035bd965c2 100644
--- a/activeresource/test/base/load_test.rb
+++ b/activeresource/test/base/load_test.rb
@@ -1,6 +1,8 @@
require 'abstract_unit'
require "fixtures/person"
require "fixtures/street_address"
+require 'active_support/core_ext/symbol'
+require 'active_support/core_ext/hash/conversions'
module Highrise
class Note < ActiveResource::Base
diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb
index 6ed6f1a406..82d3b2ae96 100644
--- a/activeresource/test/base_test.rb
+++ b/activeresource/test/base_test.rb
@@ -3,6 +3,7 @@ require "fixtures/person"
require "fixtures/customer"
require "fixtures/street_address"
require "fixtures/beast"
+require 'active_support/core_ext/hash/conversions'
class BaseTest < Test::Unit::TestCase
def setup
@@ -853,7 +854,7 @@ class BaseTest < Test::Unit::TestCase
def test_to_xml
matz = Person.find(1)
xml = matz.encode
- assert xml.starts_with?('<?xml version="1.0" encoding="UTF-8"?>')
+ assert xml.include?('<?xml version="1.0" encoding="UTF-8"?>')
assert xml.include?('<name>Matz</name>')
assert xml.include?('<id type="integer">1</id>')
end
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index e208f56455..27d26b2ba5 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,26 @@
*Edge*
+* Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp]
+
+* Change spelling of Kyev timezone to Kyiv #2613 [Alexander Dymo]
+
+* Add ActiveSupport.parse_json_times to disable time parsing in JSON backends that don't support it or don't need it. [rick]
+
+* Add pluggable JSON backends with support for the JSON gem. [rick]
+ Example: ActiveSupport::JSON.backend = "JSONGem"
+
+ All internal Rails JSON encoding is now handled by ActiveSupport::JSON.encode(). Use of #to_json is not recommended, as it may clash with other libraries that overwrite it. However, you can recover Rails specific functionality
+ if you really want to use #to_json.
+
+ gem 'json'
+ ActiveSupport::JSON.backend = "JSONGem"
+
+ class ActiveRecord::Base
+ alias to_json rails_to_json
+ end
+
+* require 'active_support' no longer orders the whole menu of core extensions. Ask for just what you need: e.g. require 'active_support/core/time' to use timezones, durations, and stdlib date/time extensions. [Jeremy Kemper]
+
* Removed rarely-used DRb cache store. [Jeremy Kemper]
* TimeWithZone.name returns 'Time', to further thwart type checking [Geoff Buesing]
diff --git a/activesupport/Rakefile b/activesupport/Rakefile
index f7fd52c7d8..c3ea09d424 100644
--- a/activesupport/Rakefile
+++ b/activesupport/Rakefile
@@ -21,6 +21,12 @@ Rake::TestTask.new { |t|
t.verbose = true
t.warning = true
}
+task :isolated_test do
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ Dir['test/**/*_test.rb'].all? do |file|
+ system(ruby, '-Ilib:test', file)
+ end or raise "Failures"
+end
# Create compressed packages
dist_dirs = [ "lib", "test"]
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 21b730fa0c..a20635ba62 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -22,39 +22,19 @@
#++
module ActiveSupport
- def self.load_all!
- [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom] + Core.load_all!
+ class << self
+ attr_accessor :load_all_hooks
+ def on_load_all(&hook) load_all_hooks << hook end
+ def load_all!; load_all_hooks.each { |hook| hook.call } end
end
+ self.load_all_hooks = []
- autoload :BacktraceCleaner, 'active_support/backtrace_cleaner'
- autoload :Base64, 'active_support/base64'
- autoload :BasicObject, 'active_support/basic_object'
- autoload :BufferedLogger, 'active_support/buffered_logger'
- autoload :Cache, 'active_support/cache'
- autoload :Callbacks, 'active_support/callbacks'
- autoload :NewCallbacks, 'active_support/new_callbacks'
- autoload :ConcurrentHash, 'active_support/concurrent_hash'
- autoload :Deprecation, 'active_support/deprecation'
- autoload :Gzip, 'active_support/gzip'
- autoload :Inflector, 'active_support/inflector'
- autoload :Memoizable, 'active_support/memoizable'
- autoload :MessageEncryptor, 'active_support/message_encryptor'
- autoload :MessageVerifier, 'active_support/message_verifier'
- autoload :Multibyte, 'active_support/multibyte'
- autoload :OptionMerger, 'active_support/option_merger'
- autoload :OrderedHash, 'active_support/ordered_hash'
- autoload :OrderedOptions, 'active_support/ordered_options'
- autoload :Rescuable, 'active_support/rescuable'
- autoload :SecureRandom, 'active_support/secure_random'
- autoload :StringInquirer, 'active_support/string_inquirer'
- autoload :XmlMini, 'active_support/xml_mini'
+ on_load_all do
+ [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom]
+ end
end
-require 'active_support/core/all'
-
+require 'active_support/autoload'
require 'active_support/vendor'
-require 'active_support/core_ext'
-require 'active_support/dependencies'
-require 'active_support/json'
I18n.load_path << "#{File.dirname(__FILE__)}/active_support/locale/en.yml"
diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb
new file mode 100644
index 0000000000..f537818300
--- /dev/null
+++ b/activesupport/lib/active_support/all.rb
@@ -0,0 +1,3 @@
+require 'active_support'
+require 'active_support/time'
+require 'active_support/core_ext'
diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb
new file mode 100644
index 0000000000..75706855d6
--- /dev/null
+++ b/activesupport/lib/active_support/autoload.rb
@@ -0,0 +1,26 @@
+module ActiveSupport
+ autoload :BacktraceCleaner, 'active_support/backtrace_cleaner'
+ autoload :Base64, 'active_support/base64'
+ autoload :BasicObject, 'active_support/basic_object'
+ autoload :BufferedLogger, 'active_support/buffered_logger'
+ autoload :Cache, 'active_support/cache'
+ autoload :Callbacks, 'active_support/callbacks'
+ autoload :Concern, 'active_support/concern'
+ autoload :ConcurrentHash, 'active_support/concurrent_hash'
+ autoload :DependencyModule, 'active_support/dependency_module'
+ autoload :Deprecation, 'active_support/deprecation'
+ autoload :Gzip, 'active_support/gzip'
+ autoload :Inflector, 'active_support/inflector'
+ autoload :Memoizable, 'active_support/memoizable'
+ autoload :MessageEncryptor, 'active_support/message_encryptor'
+ autoload :MessageVerifier, 'active_support/message_verifier'
+ autoload :Multibyte, 'active_support/multibyte'
+ autoload :NewCallbacks, 'active_support/new_callbacks'
+ autoload :OptionMerger, 'active_support/option_merger'
+ autoload :OrderedHash, 'active_support/ordered_hash'
+ autoload :OrderedOptions, 'active_support/ordered_options'
+ autoload :Rescuable, 'active_support/rescuable'
+ autoload :SecureRandom, 'active_support/secure_random'
+ autoload :StringInquirer, 'active_support/string_inquirer'
+ autoload :XmlMini, 'active_support/xml_mini'
+end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 4b2eebb007..feb6b1f2cf 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -2,6 +2,7 @@ require 'benchmark'
require 'active_support/core_ext/benchmark'
require 'active_support/core_ext/exception'
require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext' # FIXME: pulling in all to_param extensions
module ActiveSupport
# See ActiveSupport::Cache::Store for documentation.
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 4d8e1fdd67..38b3409ca6 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -23,7 +23,12 @@ module ActiveSupport
DELETED = "DELETED\r\n"
end
- attr_reader :addresses
+ def self.build_mem_cache(*addresses)
+ addresses = addresses.flatten
+ options = addresses.extract_options!
+ addresses = ["localhost"] if addresses.empty?
+ MemCache.new(addresses, options)
+ end
# Creates a new MemCacheStore object, with the given memcached server
# addresses. Each address is either a host name, or a host-with-port string
@@ -34,15 +39,20 @@ module ActiveSupport
# If no addresses are specified, then MemCacheStore will connect to
# localhost port 11211 (the default memcached port).
def initialize(*addresses)
- addresses = addresses.flatten
- options = addresses.extract_options!
- addresses = ["localhost"] if addresses.empty?
- @addresses = addresses
- @data = MemCache.new(addresses, options)
+ if addresses.first.respond_to?(:get)
+ @data = addresses.first
+ else
+ @data = self.class.build_mem_cache(*addresses)
+ end
extend Strategy::LocalCache
end
+ # Reads multiple keys from the cache.
+ def read_multi(*keys)
+ @data.get_multi keys
+ end
+
def read(key, options = nil) # :nodoc:
super
@data.get(key, raw?(options))
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 84d9a0e6d8..3b5fccc737 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/duplicable'
+
module ActiveSupport
module Cache
module Strategy
@@ -43,7 +45,7 @@ module ActiveSupport
elsif value.nil?
value = super
local_cache.write(key, value || NULL) if local_cache
- value
+ value.duplicable? ? value.dup : value
else
# forcing the value to be immutable
value.duplicable? ? value.dup : value
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 4bac8292e2..f049189b9a 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/extract_options'
+
module ActiveSupport
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
# before or after an alteration of the object state.
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
new file mode 100644
index 0000000000..154f8807f7
--- /dev/null
+++ b/activesupport/lib/active_support/concern.rb
@@ -0,0 +1,22 @@
+require 'active_support/dependency_module'
+
+module ActiveSupport
+ module Concern
+ include DependencyModule
+
+ def append_features(base)
+ if super
+ base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
+ base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
+ end
+ end
+
+ def included(base = nil, &block)
+ if base.nil?
+ @_included_block = block
+ else
+ super
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core.rb b/activesupport/lib/active_support/core.rb
deleted file mode 100644
index ad8db94941..0000000000
--- a/activesupport/lib/active_support/core.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module ActiveSupport
- module Core
- def self.load_all!
- [TimeWithZone]
- end
- end
-end
diff --git a/activesupport/lib/active_support/core/all.rb b/activesupport/lib/active_support/core/all.rb
deleted file mode 100644
index f397f48e9c..0000000000
--- a/activesupport/lib/active_support/core/all.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'active_support/core'
-Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each do |path|
- require "active_support/core/#{File.basename(path, '.rb')}"
-end
diff --git a/activesupport/lib/active_support/core/time.rb b/activesupport/lib/active_support/core/time.rb
deleted file mode 100644
index 43e13b5e58..0000000000
--- a/activesupport/lib/active_support/core/time.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'active_support/core/time/autoload'
-require 'active_support/core_ext/time'
-require 'active_support/core_ext/date'
-require 'active_support/core_ext/date_time'
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 81e466779e..5f1ce4142f 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,3 +1,7 @@
+require 'active_support/core_ext/hash/keys'
+require 'active_support/core_ext/hash/reverse_merge'
+require 'active_support/inflector'
+
class Array
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
# * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ")
@@ -155,7 +159,7 @@ class Array
raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
require 'builder' unless defined?(Builder)
- options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"
+ options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)) : "records"
options[:children] ||= options[:root].singularize
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index 75e481fc54..74ce85a1c2 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/extract_options'
+
# Extends the class object with class and instance accessors for class attributes,
# just like the native attr* accessors for instance attributes.
#
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 da798c67e7..fd029544c3 100644
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/blank'
+
class Class
def superclass_delegating_reader(*names)
class_name_to_stop_searching_on = superclass.name.blank? ? "Object" : superclass.name
diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
index 2f18666ab9..cca93a0b9f 100644
--- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/duplicable'
+
# Retain for backward compatibility. Methods are now included in Class.
module ClassInheritableAttributes # :nodoc:
end
@@ -216,4 +218,4 @@ class Class
extlib_inheritable_reader(*syms)
extlib_inheritable_writer(*syms)
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/class/removal.rb b/activesupport/lib/active_support/core_ext/class/removal.rb
index 10660edb2c..2dea3c24d5 100644
--- a/activesupport/lib/active_support/core_ext/class/removal.rb
+++ b/activesupport/lib/active_support/core_ext/class/removal.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/object/extending'
+require 'active_support/core_ext/module/introspection'
+
class Class #:nodoc:
# Unassociates the class with its subclasses and removes the subclasses
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 59efdbd1b6..1fe4ffb8e1 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -1,3 +1,5 @@
+require 'active_support/duration'
+
class Date
class << self
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
@@ -130,7 +132,7 @@ class Date
# Short-hand for years_since(1)
def next_year
years_since(1)
- end
+ end unless method_defined?(:next_year)
# Short-hand for months_ago(1)
def last_month
@@ -140,7 +142,7 @@ class Date
# Short-hand for months_since(1)
def next_month
months_since(1)
- end
+ end unless method_defined?(:next_month)
# Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00)
def beginning_of_week
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index f6c870035b..54facf4430 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -1,4 +1,6 @@
require 'active_support/inflector'
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/date_time/conversions'
class Date
DATE_FORMATS = {
diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb
index 83a11da1c7..004fd0ad29 100644
--- a/activesupport/lib/active_support/core_ext/date_time.rb
+++ b/activesupport/lib/active_support/core_ext/date_time.rb
@@ -1,5 +1,4 @@
-require 'date'
-
+require 'active_support/core_ext/time'
require 'active_support/core_ext/date_time/acts_like'
require 'active_support/core_ext/date_time/calculations'
require 'active_support/core_ext/date_time/conversions'
diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
index ddfa8d610d..5f01bc4fd6 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -58,7 +58,7 @@ class DateTime
# Converts self to a Ruby Date object; time portion is discarded
def to_date
::Date.new(year, month, day)
- end
+ end unless method_defined?(:to_date)
# Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class
# If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time
@@ -69,12 +69,12 @@ class DateTime
# To be able to keep Times, Dates and DateTimes interchangeable on conversions
def to_datetime
self
- end
+ end unless method_defined?(:to_datetime)
# Converts datetime to an appropriate format for use in XML
def xmlschema
strftime("%Y-%m-%dT%H:%M:%S%Z")
- end if RUBY_VERSION < '1.9'
+ end unless method_defined?(:xmlschema)
# Converts self to a floating-point number of seconds since the Unix epoch
def to_f
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index fa171720f9..2a34874d08 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -1,6 +1,7 @@
-require 'date'
+require 'active_support/time'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/array/conversions'
+require 'active_support/core_ext/hash/reverse_merge'
class Hash
# This module exists to decorate files deserialized using Hash.from_xml with
@@ -215,7 +216,7 @@ class Hash
case params.class.to_s
when "Hash"
params.inject({}) do |h,(k,v)|
- h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v)
+ h[k.to_s.tr("-", "_")] = unrename_keys(v)
h
end
when "Array"
diff --git a/activesupport/lib/active_support/core_ext/integer/even_odd.rb b/activesupport/lib/active_support/core_ext/integer/even_odd.rb
index 6d005268a3..8f9a97b44c 100644
--- a/activesupport/lib/active_support/core_ext/integer/even_odd.rb
+++ b/activesupport/lib/active_support/core_ext/integer/even_odd.rb
@@ -7,10 +7,10 @@ class Integer
# Is the integer a multiple of 2?
def even?
multiple_of? 2
- end if RUBY_VERSION < '1.9'
+ end unless method_defined?(:even?)
# Is the integer not a multiple of 2?
def odd?
!even?
- end if RUBY_VERSION < '1.9'
+ end unless method_defined?(:odd?)
end
diff --git a/activesupport/lib/active_support/core_ext/kernel/requires.rb b/activesupport/lib/active_support/core_ext/kernel/requires.rb
index 323fea49fe..d2238898d6 100644
--- a/activesupport/lib/active_support/core_ext/kernel/requires.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/requires.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/kernel/reporting'
+
module Kernel
# Require a library with fallback to RubyGems. Warnings during library
# loading are silenced to increase signal/noise for application warnings.
@@ -21,4 +23,4 @@ module Kernel
end
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index 858da7aa07..22749229a3 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -1,6 +1,7 @@
-# Adds the 'around_level' method to Logger.
+require 'active_support/core_ext/class/attribute_accessors'
-class Logger
+# Adds the 'around_level' method to Logger.
+class Logger #:nodoc:
def self.define_around_helper(level)
module_eval <<-end_eval
def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb
index fee91534e7..215c47b114 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -9,4 +9,3 @@ require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/loading'
require 'active_support/core_ext/module/model_naming'
require 'active_support/core_ext/module/synchronization'
-require 'active_support/core_ext/module/setup'
diff --git a/activesupport/lib/active_support/core_ext/module/attr_internal.rb b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
index b66c0d7500..d052bfed2d 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
@@ -22,11 +22,11 @@ class Module
alias_method :attr_internal, :attr_internal_accessor
- private
- mattr_accessor :attr_internal_naming_format
- self.attr_internal_naming_format = '@_%s'
+ class << self; attr_accessor :attr_internal_naming_format end
+ self.attr_internal_naming_format = '@_%s'
+ private
def attr_internal_ivar_name(attr)
- attr_internal_naming_format % attr
+ Module.attr_internal_naming_format % attr
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index fb4b5f0f3c..a44344806d 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -110,8 +110,10 @@ class Module
allow_nil = options[:allow_nil] && "#{to} && "
+ file, line = caller[0].split(":")
+
methods.each do |method|
- module_eval(<<-EOS, "(__DELEGATION__)", 1)
+ module_eval(<<-EOS, file, line.to_i)
def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
#{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block)
end # end
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index 7708d573fa..23a1063901 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -1,3 +1,5 @@
+require 'active_support/inflector'
+
class Module
# Returns the name of the module containing this one.
#
@@ -26,7 +28,7 @@ class Module
# p Module.new.parent # => Object
#
def parent
- parent_name ? parent_name.constantize : Object
+ parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
end
# Returns all the parents of this module according to its name, ordered from
@@ -47,7 +49,7 @@ class Module
if parent_name
parts = parent_name.split('::')
until parts.empty?
- parents << (parts * '::').constantize
+ parents << ActiveSupport::Inflector.constantize(parts * '::')
parts.pop
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/model_naming.rb b/activesupport/lib/active_support/core_ext/module/model_naming.rb
index 004b96a3c1..36fde87b23 100644
--- a/activesupport/lib/active_support/core_ext/module/model_naming.rb
+++ b/activesupport/lib/active_support/core_ext/module/model_naming.rb
@@ -1,13 +1,15 @@
+require 'active_support/inflector'
+
module ActiveSupport
class ModelName < String
attr_reader :singular, :plural, :cache_key, :partial_path
def initialize(name)
super
- @singular = underscore.tr('/', '_').freeze
- @plural = @singular.pluralize.freeze
+ @singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
+ @plural = ActiveSupport::Inflector.pluralize(@singular).freeze
@cache_key = tableize.freeze
- @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze
+ @partial_path = "#{@cache_key}/#{ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))}".freeze
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/setup.rb b/activesupport/lib/active_support/core_ext/module/setup.rb
deleted file mode 100644
index e6dfd0cf56..0000000000
--- a/activesupport/lib/active_support/core_ext/module/setup.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-class Module
- attr_accessor :_setup_block
- attr_accessor :_dependencies
-
- def setup(&blk)
- @_setup_block = blk
- end
-
- def use(mod)
- return if self < mod
-
- (mod._dependencies || []).each do |dep|
- use dep
- end
- # raise "Circular dependencies" if self < mod
- include mod
- extend mod.const_get("ClassMethods") if mod.const_defined?("ClassMethods")
- class_eval(&mod._setup_block) if mod._setup_block
- end
-
- def depends_on(mod)
- return if self < mod
- @_dependencies ||= []
- @_dependencies << mod
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
index 069db3fed0..f72d512340 100644
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/aliasing'
+
class Module
# Synchronize access around a method, delegating synchronization to a
# particular mutex. A mutex (either a Mutex, or any object that responds to
diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb
index 2955e8ff1d..d1062805c5 100644
--- a/activesupport/lib/active_support/core_ext/numeric/time.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -1,3 +1,5 @@
+require 'active_support/duration'
+
class Numeric
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
#
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index 96385d2b87..04e8f06b3d 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/try'
diff --git a/activesupport/lib/active_support/core_ext/object/conversions.rb b/activesupport/lib/active_support/core_ext/object/conversions.rb
index 278b856c45..638f0decc1 100644
--- a/activesupport/lib/active_support/core_ext/object/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/object/conversions.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/array/conversions'
+require 'active_support/core_ext/hash/conversions'
+
class Object
# Alias of <tt>to_s</tt>.
def to_param
diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb
index fb1bcdb98f..80011dfbed 100644
--- a/activesupport/lib/active_support/core_ext/object/misc.rb
+++ b/activesupport/lib/active_support/core_ext/object/misc.rb
@@ -1,80 +1,3 @@
-class Object
- # Returns +value+ after yielding +value+ to the block. This simplifies the
- # process of constructing an object, performing work on the object, and then
- # returning the object from a method. It is a Ruby-ized realization of the K
- # combinator, courtesy of Mikael Brockman.
- #
- # ==== Examples
- #
- # # Without returning
- # def foo
- # values = []
- # values << "bar"
- # values << "baz"
- # return values
- # end
- #
- # foo # => ['bar', 'baz']
- #
- # # returning with a local variable
- # def foo
- # returning values = [] do
- # values << 'bar'
- # values << 'baz'
- # end
- # end
- #
- # foo # => ['bar', 'baz']
- #
- # # returning with a block argument
- # def foo
- # returning [] do |values|
- # values << 'bar'
- # values << 'baz'
- # end
- # end
- #
- # foo # => ['bar', 'baz']
- def returning(value)
- yield(value)
- value
- end
-
- # Yields <code>x</code> to the block, and then returns <code>x</code>.
- # The primary purpose of this method is to "tap into" a method chain,
- # in order to perform operations on intermediate results within the chain.
- #
- # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
- # tap { |x| puts "array: #{x.inspect}" }.
- # select { |x| x%2 == 0 }.
- # tap { |x| puts "evens: #{x.inspect}" }.
- # map { |x| x*x }.
- # tap { |x| puts "squares: #{x.inspect}" }
- def tap
- yield self
- self
- end unless Object.respond_to?(:tap)
-
- # An elegant way to factor duplication out of options passed to a series of
- # method calls. Each method called in the block, with the block variable as
- # the receiver, will have its options merged with the default +options+ hash
- # provided. Each method called on the block variable must take an options
- # hash as its final argument.
- #
- # with_options :order => 'created_at', :class_name => 'Comment' do |post|
- # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
- # post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
- # post.has_many :all_comments
- # end
- #
- # Can also be used with an explicit receiver:
- #
- # map.with_options :controller => "people" do |people|
- # people.connect "/people", :action => "index"
- # people.connect "/people/:id", :action => "show"
- # end
- #
- def with_options(options)
- yield ActiveSupport::OptionMerger.new(self, options)
- end
-end
+require 'active_support/core_ext/object/returning'
+require 'active_support/core_ext/object/tap'
+require 'active_support/core_ext/object/with_options'
diff --git a/activesupport/lib/active_support/core_ext/object/returning.rb b/activesupport/lib/active_support/core_ext/object/returning.rb
new file mode 100644
index 0000000000..0dc2e1266a
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/returning.rb
@@ -0,0 +1,42 @@
+class Object
+ # Returns +value+ after yielding +value+ to the block. This simplifies the
+ # process of constructing an object, performing work on the object, and then
+ # returning the object from a method. It is a Ruby-ized realization of the K
+ # combinator, courtesy of Mikael Brockman.
+ #
+ # ==== Examples
+ #
+ # # Without returning
+ # def foo
+ # values = []
+ # values << "bar"
+ # values << "baz"
+ # return values
+ # end
+ #
+ # foo # => ['bar', 'baz']
+ #
+ # # returning with a local variable
+ # def foo
+ # returning values = [] do
+ # values << 'bar'
+ # values << 'baz'
+ # end
+ # end
+ #
+ # foo # => ['bar', 'baz']
+ #
+ # # returning with a block argument
+ # def foo
+ # returning [] do |values|
+ # values << 'bar'
+ # values << 'baz'
+ # end
+ # end
+ #
+ # foo # => ['bar', 'baz']
+ def returning(value)
+ yield(value)
+ value
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/object/tap.rb b/activesupport/lib/active_support/core_ext/object/tap.rb
new file mode 100644
index 0000000000..db7e715e2d
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/tap.rb
@@ -0,0 +1,16 @@
+class Object
+ # Yields <code>x</code> to the block, and then returns <code>x</code>.
+ # The primary purpose of this method is to "tap into" a method chain,
+ # in order to perform operations on intermediate results within the chain.
+ #
+ # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
+ # tap { |x| puts "array: #{x.inspect}" }.
+ # select { |x| x%2 == 0 }.
+ # tap { |x| puts "evens: #{x.inspect}" }.
+ # map { |x| x*x }.
+ # tap { |x| puts "squares: #{x.inspect}" }
+ def tap
+ yield self
+ self
+ end unless Object.respond_to?(:tap)
+end
diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb
new file mode 100644
index 0000000000..dd38b7d261
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -0,0 +1,24 @@
+class Object
+ # An elegant way to factor duplication out of options passed to a series of
+ # method calls. Each method called in the block, with the block variable as
+ # the receiver, will have its options merged with the default +options+ hash
+ # provided. Each method called on the block variable must take an options
+ # hash as its final argument.
+ #
+ # with_options :order => 'created_at', :class_name => 'Comment' do |post|
+ # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
+ # post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
+ # post.has_many :all_comments
+ # end
+ #
+ # Can also be used with an explicit receiver:
+ #
+ # map.with_options :controller => "people" do |people|
+ # people.connect "/people", :action => "index"
+ # people.connect "/people/:id", :action => "show"
+ # end
+ #
+ def with_options(options)
+ yield ActiveSupport::OptionMerger.new(self, options)
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/range/blockless_step.rb b/activesupport/lib/active_support/core_ext/range/blockless_step.rb
index f4792d03b7..db42ef5c47 100644
--- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb
+++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/aliasing'
+
class Range
begin
(1..2).step
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 39c2b1b8ed..2b9f8c70f6 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -1,10 +1,11 @@
require 'date'
+require 'active_support/core_ext/time/calculations'
class String
# 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility.
def ord
self[0]
- end if RUBY_VERSION < '1.9'
+ end unless method_defined?(:ord)
# Form can be either :utc (default) or :local.
def to_time(form = :utc)
diff --git a/activesupport/lib/active_support/core_ext/string/iterators.rb b/activesupport/lib/active_support/core_ext/string/iterators.rb
index 472de7bc24..2f8aa84024 100644
--- a/activesupport/lib/active_support/core_ext/string/iterators.rb
+++ b/activesupport/lib/active_support/core_ext/string/iterators.rb
@@ -1,11 +1,10 @@
unless '1.9'.respond_to?(:each_char)
- autoload :StringScanner, 'strscan' unless defined? :StringScanner
-
class String
# Yields a single-character string for each character in the string.
# When $KCODE = 'UTF8', multi-byte characters are yielded appropriately.
def each_char
- scanner, char = StringScanner.new(self), /./mu
+ require 'strscan' unless defined? ::StringScanner
+ scanner, char = ::StringScanner.new(self), /./mu
while c = scanner.scan(char)
yield c
end
diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb
index 0f0dfb2443..13208c6ee2 100644
--- a/activesupport/lib/active_support/core_ext/string/multibyte.rb
+++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+require 'active_support/multibyte'
class String
unless '1.9'.respond_to?(:force_encoding)
diff --git a/activesupport/lib/active_support/core_ext/symbol.rb b/activesupport/lib/active_support/core_ext/symbol.rb
index 520369452b..c103cd9dcf 100644
--- a/activesupport/lib/active_support/core_ext/symbol.rb
+++ b/activesupport/lib/active_support/core_ext/symbol.rb
@@ -1,14 +1 @@
-class Symbol
- # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
- #
- # # The same as people.collect { |p| p.name }
- # people.collect(&:name)
- #
- # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
- # people.select(&:manager?).collect(&:salary)
- #
- # This is a builtin method in Ruby 1.8.7 and later.
- def to_proc
- Proc.new { |*args| args.shift.__send__(self, *args) }
- end unless :to_proc.respond_to?(:to_proc)
-end
+require 'active_support/core_ext/symbol/to_proc'
diff --git a/activesupport/lib/active_support/core_ext/symbol/to_proc.rb b/activesupport/lib/active_support/core_ext/symbol/to_proc.rb
new file mode 100644
index 0000000000..520369452b
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/symbol/to_proc.rb
@@ -0,0 +1,14 @@
+class Symbol
+ # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
+ #
+ # # The same as people.collect { |p| p.name }
+ # people.collect(&:name)
+ #
+ # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
+ # people.select(&:manager?).collect(&:salary)
+ #
+ # This is a builtin method in Ruby 1.8.7 and later.
+ def to_proc
+ Proc.new { |*args| args.shift.__send__(self, *args) }
+ end unless :to_proc.respond_to?(:to_proc)
+end
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index 18261e139f..6d9c080442 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -63,13 +63,13 @@ class Time
# your_time.to_date # => Tue, 13 Jan 2009
def to_date
::Date.new(year, month, day)
- end
+ end unless method_defined?(:to_date)
# A method to keep Time, Date and DateTime instances interchangeable on conversions.
# In this case, it simply returns +self+.
def to_time
self
- end
+ end unless method_defined?(:to_time)
# Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
#
@@ -80,5 +80,5 @@ class Time
# your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500
def to_datetime
::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
- end
+ end unless method_defined?(:to_datetime)
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 41b7e00c0c..855b720ef1 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -1,10 +1,13 @@
require 'set'
-require 'active_support/inflector'
-require 'active_support/core_ext/name_error'
-require 'active_support/core_ext/object/blank'
+require 'thread'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/introspection'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/load_error'
+require 'active_support/core_ext/name_error'
+require 'active_support/core_ext/string/starts_ends_with'
+require 'active_support/inflector'
module ActiveSupport #:nodoc:
module Dependencies #:nodoc:
diff --git a/activesupport/lib/active_support/dependency_module.rb b/activesupport/lib/active_support/dependency_module.rb
new file mode 100644
index 0000000000..6847c0f86a
--- /dev/null
+++ b/activesupport/lib/active_support/dependency_module.rb
@@ -0,0 +1,17 @@
+module ActiveSupport
+ module DependencyModule
+ def append_features(base)
+ return false if base < self
+ (@_dependencies ||= []).each { |dep| base.send(:include, dep) }
+ super
+ end
+
+ def depends_on(*mods)
+ mods.each do |mod|
+ next if self < mod
+ @_dependencies ||= []
+ @_dependencies << mod
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index b35d4daf9a..b9eb539aa7 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/module/deprecation'
+require 'active_support/core_ext/module/aliasing'
module ActiveSupport
class << Deprecation
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 1c268d0d9c..dec56715be 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -1,7 +1,9 @@
+require 'active_support/inflector'
+
module ActiveSupport
module Deprecation
class DeprecationProxy #:nodoc:
- instance_methods.each { |m| undef_method m unless m =~ /^__/ }
+ instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ }
# Don't give a deprecation warning on inspect since test/unit and error
# logs rely on it for diagnostics.
@@ -61,7 +63,7 @@ module ActiveSupport
private
def target
- @new_const.to_s.constantize
+ ActiveSupport::Inflector.constantize(@new_const.to_s)
end
def warn(callstack, called, args)
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index f64661c5b1..a33586f77f 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -1,4 +1,5 @@
require 'active_support/basic_object'
+require 'active_support/core_ext/array/conversions'
module ActiveSupport
# Provides accurate date and time measurements using Date#advance and
diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb
index 614afa1bd6..ff70d6d76e 100644
--- a/activesupport/lib/active_support/inflector.rb
+++ b/activesupport/lib/active_support/inflector.rb
@@ -1,6 +1,7 @@
# encoding: utf-8
require 'iconv'
-
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/string/access'
require 'active_support/core_ext/string/multibyte'
module ActiveSupport
diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb
index 2bdb4a7b11..6d845182fb 100644
--- a/activesupport/lib/active_support/json.rb
+++ b/activesupport/lib/active_support/json.rb
@@ -1,11 +1,74 @@
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/attribute_accessors'
+
module ActiveSupport
# If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
mattr_accessor :use_standard_json_time_format
+ # Look for and parse json strings that look like ISO 8601 times.
+ mattr_accessor :parse_json_times
- class << self
- def escape_html_entities_in_json
- @escape_html_entities_in_json
+ module JSON
+ # matches YAML-formatted dates
+ DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
+
+ module Encoding #:nodoc:
+ mattr_accessor :escape_regex
+
+ ESCAPED_CHARS = {
+ "\010" => '\b',
+ "\f" => '\f',
+ "\n" => '\n',
+ "\r" => '\r',
+ "\t" => '\t',
+ '"' => '\"',
+ '\\' => '\\\\',
+ '>' => '\u003E',
+ '<' => '\u003C',
+ '&' => '\u0026'
+ }
+
+ def self.escape(string)
+ string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding)
+ json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
+ gsub(/([\xC0-\xDF][\x80-\xBF]|
+ [\xE0-\xEF][\x80-\xBF]{2}|
+ [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
+ s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
+ }
+ %("#{json}")
+ end
+ end
+
+ class << self
+ delegate :decode, :to => :backend
+
+ def backend
+ unless defined? @backend
+ self.backend = defined?(::JSON) ? "JSONGem" : "Yaml"
+ end
+ @backend
+ end
+
+ def backend=(name)
+ if name.is_a?(Module)
+ @backend = name
+ else
+ require "active_support/json/backends/#{name.to_s.downcase}.rb"
+ @backend = ActiveSupport::JSON::Backends::const_get(name)
+ end
+ end
+
+ def with_backend(name)
+ old_backend, self.backend = backend, name
+ yield
+ ensure
+ self.backend = old_backend
+ end
end
+ end
+
+ class << self
+ attr_reader :escape_html_entities_in_json
def escape_html_entities_in_json=(value)
ActiveSupport::JSON::Encoding.escape_regex = \
@@ -19,5 +82,6 @@ module ActiveSupport
end
end
+ActiveSupport.escape_html_entities_in_json = true
+
require 'active_support/json/encoding'
-require 'active_support/json/decoding'
diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb
new file mode 100644
index 0000000000..649e6301d1
--- /dev/null
+++ b/activesupport/lib/active_support/json/backends/jsongem.rb
@@ -0,0 +1,41 @@
+require 'json' unless defined?(JSON)
+
+module ActiveSupport
+ module JSON
+ ParseError = ::JSON::ParserError unless const_defined?(:ParseError)
+
+ module Backends
+ module JSONGem
+ extend self
+
+ # Parses a JSON string or IO and convert it into an object
+ def decode(json)
+ if json.respond_to?(:read)
+ json = json.read
+ end
+ data = ::JSON.parse(json)
+ if ActiveSupport.parse_json_times
+ convert_dates_from(data)
+ else
+ data
+ end
+ end
+
+ private
+ def convert_dates_from(data)
+ case data
+ when DATE_REGEX
+ DateTime.parse(data)
+ when Array
+ data.map! { |d| convert_dates_from(d) }
+ when Hash
+ data.each do |key, value|
+ data[key] = convert_dates_from(value)
+ end
+ else data
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb
new file mode 100644
index 0000000000..667016f45d
--- /dev/null
+++ b/activesupport/lib/active_support/json/backends/yaml.rb
@@ -0,0 +1,88 @@
+require 'active_support/core_ext/string/starts_ends_with'
+
+module ActiveSupport
+ module JSON
+ unless const_defined?(:ParseError)
+ class ParseError < StandardError
+ end
+ end
+
+ module Backends
+ module Yaml
+ extend self
+
+ # Parses a JSON string or IO and converts it into an object
+ def decode(json)
+ if json.respond_to?(:read)
+ json = json.read
+ end
+ YAML.load(convert_json_to_yaml(json))
+ rescue ArgumentError => e
+ raise ParseError, "Invalid JSON string"
+ end
+
+ protected
+ # Ensure that ":" and "," are always followed by a space
+ def convert_json_to_yaml(json) #:nodoc:
+ require 'strscan' unless defined? ::StringScanner
+ scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
+ while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
+ case char = scanner[1]
+ when '"', "'"
+ if !quoting
+ quoting = char
+ pos = scanner.pos
+ elsif quoting == char
+ if json[pos..scanner.pos-2] =~ DATE_REGEX
+ # found a date, track the exact positions of the quotes so we can remove them later.
+ # oh, and increment them for each current mark, each one is an extra padded space that bumps
+ # the position in the final YAML output
+ total_marks = marks.size
+ times << pos+total_marks << scanner.pos+total_marks
+ end
+ quoting = false
+ end
+ when ":",","
+ marks << scanner.pos - 1 unless quoting
+ end
+ end
+
+ if marks.empty?
+ json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
+ ustr = $1
+ if ustr.start_with?('u')
+ [ustr[1..-1].to_i(16)].pack("U")
+ elsif ustr == '\\'
+ '\\\\'
+ else
+ ustr
+ end
+ end
+ else
+ left_pos = [-1].push(*marks)
+ right_pos = marks << scanner.pos + scanner.rest_size
+ output = []
+ left_pos.each_with_index do |left, i|
+ scanner.pos = left.succ
+ output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
+ ustr = $1
+ if ustr.start_with?('u')
+ [ustr[1..-1].to_i(16)].pack("U")
+ elsif ustr == '\\'
+ '\\\\'
+ else
+ ustr
+ end
+ end
+ end
+ output = output * " "
+
+ times.each { |i| output[i-1] = ' ' }
+ output.gsub!(/\\\//, '/')
+ output
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
deleted file mode 100644
index c66500aa9c..0000000000
--- a/activesupport/lib/active_support/json/decoding.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-autoload :YAML, 'yaml' unless defined? YAML
-autoload :StringScanner, 'strscan' unless defined? StringScanner
-
-require 'active_support/core_ext/string/starts_ends_with'
-
-module ActiveSupport
- module JSON
- class ParseError < StandardError
- end
-
- class << self
- # Converts a JSON string into a Ruby object.
- def decode(json)
- YAML.load(convert_json_to_yaml(json))
- rescue ArgumentError => e
- raise ParseError, "Invalid JSON string"
- end
-
- protected
- # matches YAML-formatted dates
- DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/
-
- # Ensure that ":" and "," are always followed by a space
- def convert_json_to_yaml(json) #:nodoc:
- scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
- while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
- case char = scanner[1]
- when '"', "'"
- if !quoting
- quoting = char
- pos = scanner.pos
- elsif quoting == char
- if json[pos..scanner.pos-2] =~ DATE_REGEX
- # found a date, track the exact positions of the quotes so we can remove them later.
- # oh, and increment them for each current mark, each one is an extra padded space that bumps
- # the position in the final YAML output
- total_marks = marks.size
- times << pos+total_marks << scanner.pos+total_marks
- end
- quoting = false
- end
- when ":",","
- marks << scanner.pos - 1 unless quoting
- end
- end
-
- if marks.empty?
- json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
- ustr = $1
- if ustr.start_with?('u')
- [ustr[1..-1].to_i(16)].pack("U")
- elsif ustr == '\\'
- '\\\\'
- else
- ustr
- end
- end
- else
- left_pos = [-1].push(*marks)
- right_pos = marks << scanner.pos + scanner.rest_size
- output = []
- left_pos.each_with_index do |left, i|
- scanner.pos = left.succ
- output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
- ustr = $1
- if ustr.start_with?('u')
- [ustr[1..-1].to_i(16)].pack("U")
- elsif ustr == '\\'
- '\\\\'
- else
- ustr
- end
- end
- end
- output = output * " "
-
- times.each { |i| output[i-1] = ' ' }
- output.gsub!(/\\\//, '/')
- output
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb
index cc84de1388..9adb3c20e2 100644
--- a/activesupport/lib/active_support/json/encoders/date.rb
+++ b/activesupport/lib/active_support/json/encoders/date.rb
@@ -1,21 +1,22 @@
class Date
- # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the
- # ISO 8601 format is used.
- #
- # ==== Examples
- #
- # # With ActiveSupport.use_standard_json_time_format = true
- # Date.new(2005,2,1).to_json
- # # => "2005-02-01"
- #
- # # With ActiveSupport.use_standard_json_time_format = false
- # Date.new(2005,2,1).to_json
- # # => "2005/02/01"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- %("#{strftime("%Y-%m-%d")}")
- else
- %("#{strftime("%Y/%m/%d")}")
+ private
+ # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the
+ # ISO 8601 format is used.
+ #
+ # ==== Examples
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # Date.new(2005,2,1).to_json
+ # # => "2005-02-01"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
+ # Date.new(2005,2,1).to_json
+ # # => "2005/02/01"
+ def rails_to_json(*)
+ if ActiveSupport.use_standard_json_time_format
+ %("#{strftime("%Y-%m-%d")}")
+ else
+ %("#{strftime("%Y/%m/%d")}")
+ end
end
- end
end
diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb
index 6c85824105..3a29292b24 100644
--- a/activesupport/lib/active_support/json/encoders/date_time.rb
+++ b/activesupport/lib/active_support/json/encoders/date_time.rb
@@ -1,21 +1,22 @@
class DateTime
- # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the
- # ISO 8601 format is used.
- #
- # ==== Examples
- #
- # # With ActiveSupport.use_standard_json_time_format = true
- # DateTime.civil(2005,2,1,15,15,10).to_json
- # # => "2005-02-01T15:15:10+00:00"
- #
- # # With ActiveSupport.use_standard_json_time_format = false
- # DateTime.civil(2005,2,1,15,15,10).to_json
- # # => "2005/02/01 15:15:10 +0000"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- xmlschema.inspect
- else
- strftime('"%Y/%m/%d %H:%M:%S %z"')
+ private
+ # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the
+ # ISO 8601 format is used.
+ #
+ # ==== Examples
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # DateTime.civil(2005,2,1,15,15,10).to_json
+ # # => "2005-02-01T15:15:10+00:00"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
+ # DateTime.civil(2005,2,1,15,15,10).to_json
+ # # => "2005/02/01 15:15:10 +0000"
+ def rails_to_json(*)
+ if ActiveSupport.use_standard_json_time_format
+ xmlschema.inspect
+ else
+ strftime('"%Y/%m/%d %H:%M:%S %z"')
+ end
end
- end
end
diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb
index 881b1d62c1..898990a59c 100644
--- a/activesupport/lib/active_support/json/encoders/enumerable.rb
+++ b/activesupport/lib/active_support/json/encoders/enumerable.rb
@@ -1,12 +1,13 @@
module Enumerable
- # Returns a JSON string representing the enumerable. Any +options+
- # given will be passed on to its elements. For example:
- #
- # users = User.find(:all)
- # # => users.to_json(:only => :name)
- #
- # will pass the <tt>:only => :name</tt> option to each user.
- def to_json(options = {}) #:nodoc:
- "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]"
- end
+ private
+ # Returns a JSON string representing the enumerable. Any +options+
+ # given will be passed on to its elements. For example:
+ #
+ # users = User.find(:all)
+ # # => users.to_json(:only => :name)
+ #
+ # will pass the <tt>:only => :name</tt> option to each user.
+ def rails_to_json(options = nil, *args) #:nodoc:
+ "[#{map { |value| ActiveSupport::JSON.encode(value, options, *args) } * ','}]"
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb
index bf0844334b..eb975fe542 100644
--- a/activesupport/lib/active_support/json/encoders/false_class.rb
+++ b/activesupport/lib/active_support/json/encoders/false_class.rb
@@ -1,5 +1,6 @@
class FalseClass
- def to_json(options = nil) #:nodoc:
- 'false'
- end
+ private
+ def rails_to_json(*)
+ 'false'
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb
index d87b880743..4771484843 100644
--- a/activesupport/lib/active_support/json/encoders/hash.rb
+++ b/activesupport/lib/active_support/json/encoders/hash.rb
@@ -1,48 +1,51 @@
require 'active_support/core_ext/array/wrap'
class Hash
- # Returns a JSON string representing the hash.
- #
- # Without any +options+, the returned JSON string will include all
- # the hash keys. For example:
- #
- # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
- # # => {"name": "Konata Izumi", "1": 2, "age": 16}
- #
- # The keys in the JSON string are unordered due to the nature of hashes.
- #
- # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
- # attributes included, and will accept 1 or more hash keys to include/exclude.
- #
- # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age'])
- # # => {"name": "Konata Izumi", "age": 16}
- #
- # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1)
- # # => {"name": "Konata Izumi", "age": 16}
- #
- # The +options+ also filter down to any hash values. This is particularly
- # useful for converting hashes containing ActiveRecord objects or any object
- # that responds to options in their <tt>to_json</tt> method. For example:
- #
- # users = User.find(:all)
- # { :users => users, :count => users.size }.to_json(:include => :posts)
- #
- # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>,
- # allowing the posts association in the User model to be converted to JSON
- # as well.
- def to_json(options = {}) #:nodoc:
- hash_keys = self.keys
+ private
+ # Returns a JSON string representing the hash.
+ #
+ # Without any +options+, the returned JSON string will include all
+ # the hash keys. For example:
+ #
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
+ # # => {"name": "Konata Izumi", "1": 2, "age": 16}
+ #
+ # The keys in the JSON string are unordered due to the nature of hashes.
+ #
+ # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
+ # attributes included, and will accept 1 or more hash keys to include/exclude.
+ #
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age'])
+ # # => {"name": "Konata Izumi", "age": 16}
+ #
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1)
+ # # => {"name": "Konata Izumi", "age": 16}
+ #
+ # The +options+ also filter down to any hash values. This is particularly
+ # useful for converting hashes containing ActiveRecord objects or any object
+ # that responds to options in their <tt>to_json</tt> method. For example:
+ #
+ # users = User.find(:all)
+ # { :users => users, :count => users.size }.to_json(:include => :posts)
+ #
+ # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>,
+ # allowing the posts association in the User model to be converted to JSON
+ # as well.
+ def rails_to_json(options = nil, *args) #:nodoc:
+ hash_keys = self.keys
- if except = options[:except]
- hash_keys = hash_keys - Array.wrap(except)
- elsif only = options[:only]
- hash_keys = hash_keys & Array.wrap(only)
- end
+ if options
+ if except = options[:except]
+ hash_keys = hash_keys - Array.wrap(except)
+ elsif only = options[:only]
+ hash_keys = hash_keys & Array.wrap(only)
+ end
+ end
- result = '{'
- result << hash_keys.map do |key|
- "#{ActiveSupport::JSON.encode(key.to_s)}: #{ActiveSupport::JSON.encode(self[key], options)}"
- end * ', '
- result << '}'
- end
+ result = '{'
+ result << hash_keys.map do |key|
+ "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options, *args)}"
+ end * ','
+ result << '}'
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb
index 4763471ac4..8c51dba384 100644
--- a/activesupport/lib/active_support/json/encoders/nil_class.rb
+++ b/activesupport/lib/active_support/json/encoders/nil_class.rb
@@ -1,5 +1,6 @@
class NilClass
- def to_json(options = nil) #:nodoc:
- 'null'
- end
+ private
+ def rails_to_json(*)
+ 'null'
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb
index 38713fb369..c7cd0df1d7 100644
--- a/activesupport/lib/active_support/json/encoders/numeric.rb
+++ b/activesupport/lib/active_support/json/encoders/numeric.rb
@@ -1,5 +1,6 @@
class Numeric
- def to_json(options = nil) #:nodoc:
- to_s
- end
+ private
+ def rails_to_json(*)
+ to_s
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb
index 0475967aee..9cc12d91ac 100644
--- a/activesupport/lib/active_support/json/encoders/object.rb
+++ b/activesupport/lib/active_support/json/encoders/object.rb
@@ -2,7 +2,12 @@ require 'active_support/core_ext/object/instance_variables'
class Object
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
- def to_json(options = {})
- ActiveSupport::JSON.encode(instance_values, options)
+ def to_json(options = nil)
+ ActiveSupport::JSON.encode(self, options)
end
+
+ private
+ def rails_to_json(*args)
+ ActiveSupport::JSON.encode(instance_values, *args)
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb
index b6116b70b5..ee42db4d02 100644
--- a/activesupport/lib/active_support/json/encoders/regexp.rb
+++ b/activesupport/lib/active_support/json/encoders/regexp.rb
@@ -1,5 +1,6 @@
class Regexp
- def to_json(options = nil) #:nodoc:
- inspect
- end
+ private
+ def rails_to_json(*)
+ inspect
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb
index 5ef797955a..4a6b21c1c0 100644
--- a/activesupport/lib/active_support/json/encoders/string.rb
+++ b/activesupport/lib/active_support/json/encoders/string.rb
@@ -1,36 +1,6 @@
-module ActiveSupport
- module JSON
- module Encoding
- mattr_accessor :escape_regex
-
- ESCAPED_CHARS = {
- "\010" => '\b',
- "\f" => '\f',
- "\n" => '\n',
- "\r" => '\r',
- "\t" => '\t',
- '"' => '\"',
- '\\' => '\\\\',
- '>' => '\u003E',
- '<' => '\u003C',
- '&' => '\u0026'
- }
- end
- end
-end
-
-ActiveSupport.escape_html_entities_in_json = true
-
class String
- def to_json(options = nil) #:nodoc:
- json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s|
- ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s]
- }
- json.force_encoding('ascii-8bit') if respond_to?(:force_encoding)
- json.gsub(/([\xC0-\xDF][\x80-\xBF]|
- [\xE0-\xEF][\x80-\xBF]{2}|
- [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
- s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
- } + '"'
- end
+ private
+ def rails_to_json(*)
+ ActiveSupport::JSON::Encoding.escape(self)
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb
index 485112f97c..d575350a4e 100644
--- a/activesupport/lib/active_support/json/encoders/symbol.rb
+++ b/activesupport/lib/active_support/json/encoders/symbol.rb
@@ -1,5 +1,6 @@
class Symbol
- def to_json(options = {}) #:nodoc:
- ActiveSupport::JSON.encode(to_s, options)
- end
+ private
+ def rails_to_json(*args)
+ ActiveSupport::JSON.encode(to_s, *args)
+ end
end
diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb
index f45a0059e8..d434b9aace 100644
--- a/activesupport/lib/active_support/json/encoders/time.rb
+++ b/activesupport/lib/active_support/json/encoders/time.rb
@@ -1,21 +1,24 @@
+require 'active_support/core_ext/time/conversions'
+
class Time
- # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the
- # ISO 8601 format is used.
- #
- # ==== Examples
- #
- # # With ActiveSupport.use_standard_json_time_format = true
- # Time.utc(2005,2,1,15,15,10).to_json
- # # => "2005-02-01T15:15:10Z"
- #
- # # With ActiveSupport.use_standard_json_time_format = false
- # Time.utc(2005,2,1,15,15,10).to_json
- # # => "2005/02/01 15:15:10 +0000"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- xmlschema.inspect
- else
- %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
+ private
+ # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the
+ # ISO 8601 format is used.
+ #
+ # ==== Examples
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # Time.utc(2005,2,1,15,15,10).to_json
+ # # => "2005-02-01T15:15:10Z"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
+ # Time.utc(2005,2,1,15,15,10).to_json
+ # # => "2005/02/01 15:15:10 +0000"
+ def rails_to_json(*)
+ if ActiveSupport.use_standard_json_time_format
+ xmlschema.inspect
+ else
+ %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
+ end
end
- end
end
diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb
index 037d812b3f..bc25a6db78 100644
--- a/activesupport/lib/active_support/json/encoders/true_class.rb
+++ b/activesupport/lib/active_support/json/encoders/true_class.rb
@@ -1,5 +1,6 @@
class TrueClass
- def to_json(options = nil) #:nodoc:
- 'true'
- end
+ private
+ def rails_to_json(*)
+ 'true'
+ end
end
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index aaaa3cdfd2..5fefe5b88b 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -4,11 +4,13 @@ module ActiveSupport
end
# Converts a Ruby object into a JSON string.
- def self.encode(value, options = {})
- seen = (options[:seen] ||= [])
- raise CircularReferenceError, 'object references itself' if seen.include?(value)
+ def self.encode(value, options = nil, seen = nil)
+ seen ||= []
+ if seen.any? { |object| object.equal?(value) }
+ raise CircularReferenceError, 'object references itself'
+ end
seen << value
- value.send(:to_json, options)
+ value.__send__(:rails_to_json, options, seen)
ensure
seen.pop
end
diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb
index 7fd23b0a9e..daa7449b71 100644
--- a/activesupport/lib/active_support/json/variable.rb
+++ b/activesupport/lib/active_support/json/variable.rb
@@ -2,9 +2,10 @@ module ActiveSupport
module JSON
# A string that returns itself as its JSON-encoded form.
class Variable < String
- def to_json(options=nil)
- self
- end
+ private
+ def rails_to_json(*)
+ self
+ end
end
end
end
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
index 2b85fd7be4..fa6db683d4 100644
--- a/activesupport/lib/active_support/memoizable.rb
+++ b/activesupport/lib/active_support/memoizable.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/module/aliasing'
+
module ActiveSupport
module SafelyMemoizable
def safely_memoize(*symbols)
diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb
index 65a96af49a..d8d58f3bce 100644
--- a/activesupport/lib/active_support/multibyte.rb
+++ b/activesupport/lib/active_support/multibyte.rb
@@ -4,6 +4,8 @@ require 'active_support/multibyte/chars'
require 'active_support/multibyte/exceptions'
require 'active_support/multibyte/unicode_database'
+require 'active_support/core_ext/module/attribute_accessors'
+
module ActiveSupport #:nodoc:
module Multibyte
# A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 60f082bcc1..96ed35f0e0 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+require 'active_support/core_ext/string/behavior'
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
diff --git a/activesupport/lib/active_support/new_callbacks.rb b/activesupport/lib/active_support/new_callbacks.rb
index 356d70b650..f1d2ea7bb1 100644
--- a/activesupport/lib/active_support/new_callbacks.rb
+++ b/activesupport/lib/active_support/new_callbacks.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/class/inheritable_attributes'
+
module ActiveSupport
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
# before or after an alteration of the object state.
@@ -284,6 +287,14 @@ module ActiveSupport
when Proc
@klass.send(:define_method, method_name, &filter)
method_name << (filter.arity == 1 ? "(self)" : "")
+ when Method
+ @klass.send(:define_method, "#{method_name}_method") { filter }
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def #{method_name}(&blk)
+ #{method_name}_method.call(self, &blk)
+ end
+ RUBY_EVAL
+ method_name
when String
@klass.class_eval <<-RUBY_EVAL
def #{method_name}
@@ -293,23 +304,39 @@ module ActiveSupport
method_name
else
kind, name = @kind, @name
- @klass.send(:define_method, method_name) do
- filter.send("#{kind}_#{name}", self)
+ @klass.send(:define_method, "#{method_name}_object") { filter }
+
+ if kind == :around
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def #{method_name}(&blk)
+ if :#{kind} == :around && #{method_name}_object.respond_to?(:filter)
+ #{method_name}_object.send("filter", self, &blk)
+ # TODO: Deprecate this
+ elsif #{method_name}_object.respond_to?(:before) && #{method_name}_object.respond_to?(:after)
+ should_continue = #{method_name}_object.before(self)
+ yield if should_continue
+ #{method_name}_object.after(self)
+ else
+ #{method_name}_object.send("#{kind}_#{name}", self, &blk)
+ end
+ end
+ RUBY_EVAL
+ else
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def #{method_name}(&blk)
+ if #{method_name}_object.respond_to?(:#{kind})
+ #{method_name}_object.#{kind}(self, &blk)
+ else
+ #{method_name}_object.send("#{kind}_#{name}", self, &blk)
+ end
+ end
+ RUBY_EVAL
end
method_name
end
end
end
- # This method_missing is supplied to catch callbacks with keys and create
- # the appropriate callback for future use.
- def method_missing(meth, *args, &blk)
- if meth.to_s =~ /_run__([\w:]+)__(\w+)__(\w+)__callbacks/
- return self.class._create_and_run_keyed_callback($1, $2.to_sym, $3.to_sym, self, &blk)
- end
- super
- end
-
# An Array with a compile method
class CallbackChain < Array
def initialize(symbol)
@@ -322,7 +349,7 @@ module ActiveSupport
each do |callback|
method << callback.start(key, options)
end
- method << "yield self if block_given?"
+ method << "yield self if block_given? && !halted"
reverse_each do |callback|
method << callback.end(key, options)
end
@@ -353,6 +380,7 @@ module ActiveSupport
str = <<-RUBY_EVAL
def _run_#{symbol}_callbacks(key = nil)
if key
+ key = key.hash.to_s.gsub(/-/, '_')
name = "_run__\#{self.class.name.split("::").last}__#{symbol}__\#{key}__callbacks"
if respond_to?(name)
@@ -445,7 +473,9 @@ module ActiveSupport
self._#{symbol}_callbacks.delete_if {|c| c.matches?(type, :#{symbol}, filter)}
Callback.new(filter, type, options.dup, self, :#{symbol})
end
- self._#{symbol}_callbacks.push(*filters)
+ options[:prepend] ?
+ self._#{symbol}_callbacks.unshift(*filters) :
+ self._#{symbol}_callbacks.push(*filters)
_define_runner(:#{symbol},
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
options)
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index fed8094a24..8d1c0f5160 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -10,6 +10,16 @@ module ActiveSupport
@keys = []
end
+ def self.[](*args)
+ ordered_hash = new
+ args.each_with_index { |val,ind|
+ # Only every second value is a key.
+ next if ind % 2 != 0
+ ordered_hash[val] = args[ind + 1]
+ }
+ ordered_hash
+ end
+
def initialize_copy(other)
super
# make a deep copy of keys
@@ -57,6 +67,10 @@ module ActiveSupport
self
end
+ def to_a
+ @keys.map { |key| [ key, self[key] ] }
+ end
+
def each_key
@keys.each { |key| yield key }
end
diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb
new file mode 100644
index 0000000000..37c57c485a
--- /dev/null
+++ b/activesupport/lib/active_support/ruby/shim.rb
@@ -0,0 +1,24 @@
+# Backported Ruby builtins so you can code with the latest & greatest
+# but still run on any Ruby 1.8.x.
+#
+# Date next_year, next_month
+# DateTime to_date, to_datetime, xmlschema
+# Enumerable group_by, each_with_object, none?
+# Integer even?, odd?
+# Object tap
+# Process Process.daemon
+# REXML security fix
+# String ord
+# Symbol to_proc
+# Time to_date, to_time, to_datetime
+require 'active_support'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date_time/conversions'
+require 'active_support/core_ext/enumerable'
+require 'active_support/core_ext/integer/even_odd'
+require 'active_support/core_ext/object/tap'
+require 'active_support/core_ext/process/daemon'
+require 'active_support/core_ext/string/conversions'
+require 'active_support/core_ext/rexml'
+require 'active_support/core_ext/symbol/to_proc'
+require 'active_support/core_ext/time/conversions'
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 50e25ef740..bab2a401eb 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -32,6 +32,11 @@ module ActiveSupport
include ActiveSupport::Testing::Default
end
+ $tags = {}
+ def self.for_tag(tag)
+ yield if $tags[tag]
+ end
+
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index ca51adba1e..c529b92240 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrap'
+
module ActiveSupport
module Testing
module Assertions
diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb
index e9220605bd..0135185a47 100644
--- a/activesupport/lib/active_support/testing/deprecation.rb
+++ b/activesupport/lib/active_support/testing/deprecation.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
module ActiveSupport
module Testing
module Deprecation #:nodoc:
@@ -35,21 +37,19 @@ end
begin
require 'test/unit/error'
-
+rescue LoadError
+ # Using miniunit, ignore.
+else
module Test
module Unit
- class Error # :nodoc:
+ class Error #:nodoc:
# Silence warnings when reporting test errors.
def message_with_silenced_deprecation
- ActiveSupport::Deprecation.silence do
- message_without_silenced_deprecation
- end
+ ActiveSupport::Deprecation.silence { message_without_silenced_deprecation }
end
-
- alias_method_chain :message, :silenced_deprecation
+ alias_method :message_without_silenced_deprecation, :message
+ alias_method :message, :message_with_silenced_deprecation
end
end
end
-rescue LoadError
- # Using miniunit, ignore.
end
diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb
index d945c7e476..21134ff9e2 100644
--- a/activesupport/lib/active_support/testing/pending.rb
+++ b/activesupport/lib/active_support/testing/pending.rb
@@ -11,6 +11,11 @@ module ActiveSupport
@@at_exit = false
def pending(description = "", &block)
+ if description.is_a?(Symbol)
+ is_pending = $tags[description]
+ return block.call unless is_pending
+ end
+
if block_given?
failed = false
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
new file mode 100644
index 0000000000..d36a683601
--- /dev/null
+++ b/activesupport/lib/active_support/time.rb
@@ -0,0 +1,14 @@
+require 'active_support'
+require 'active_support/core_ext/time'
+require 'active_support/core_ext/date'
+require 'active_support/core_ext/date_time'
+
+module ActiveSupport
+ autoload :Duration, 'active_support/duration'
+ autoload :TimeWithZone, 'active_support/time_with_zone'
+ autoload :TimeZone, 'active_support/values/time_zone'
+
+ on_load_all do
+ [Duration, TimeWithZone, TimeZone]
+ end
+end
diff --git a/activesupport/lib/active_support/core/time/autoload.rb b/activesupport/lib/active_support/time/autoload.rb
index c9a7731b39..c9a7731b39 100644
--- a/activesupport/lib/active_support/core/time/autoload.rb
+++ b/activesupport/lib/active_support/time/autoload.rb
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index aa448debda..069842c6c3 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -118,26 +118,6 @@ module ActiveSupport
end
alias_method :iso8601, :xmlschema
- # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to
- # true, the ISO 8601 format is used.
- #
- # ==== Examples
- #
- # # With ActiveSupport.use_standard_json_time_format = true
- # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
- # # => "2005-02-01T15:15:10Z"
- #
- # # With ActiveSupport.use_standard_json_time_format = false
- # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
- # # => "2005/02/01 15:15:10 +0000"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- xmlschema.inspect
- else
- %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
- end
- end
-
def to_yaml(options = {})
if options.kind_of?(YAML::Emitter)
utc.to_yaml(options)
@@ -321,6 +301,26 @@ module ActiveSupport
end
private
+ # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to
+ # true, the ISO 8601 format is used.
+ #
+ # ==== Examples
+ #
+ # # With ActiveSupport.use_standard_json_time_format = true
+ # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
+ # # => "2005-02-01T15:15:10Z"
+ #
+ # # With ActiveSupport.use_standard_json_time_format = false
+ # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
+ # # => "2005/02/01 15:15:10 +0000"
+ def rails_to_json(*)
+ if !ActiveSupport.respond_to?(:use_standard_json_time_format) || ActiveSupport.use_standard_json_time_format
+ xmlschema.inspect
+ else
+ %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
+ end
+ end
+
def get_period_and_ensure_valid_local_time
# we don't want a Time.local instance enforcing its own DST rules as well,
# so transfer time values to a utc constructor if necessary
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index e2d759aa50..b37dae1c2a 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -92,7 +92,7 @@ module ActiveSupport
"Bucharest" => "Europe/Bucharest",
"Cairo" => "Africa/Cairo",
"Helsinki" => "Europe/Helsinki",
- "Kyev" => "Europe/Kiev",
+ "Kyiv" => "Europe/Kiev",
"Riga" => "Europe/Riga",
"Sofia" => "Europe/Sofia",
"Tallinn" => "Europe/Tallinn",
@@ -336,7 +336,7 @@ module ActiveSupport
"Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin",
"Bern", "Rome", "Stockholm", "Vienna",
"West Central Africa" ],
- [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia",
+ [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyiv", "Riga", "Sofia",
"Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk",
"Jerusalem", "Harare", "Pretoria" ],
[ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh",
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index ccd1349491..f22fbcc0e1 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/delegation'
+
module ActiveSupport
# = XmlMini
#
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
new file mode 100644
index 0000000000..1cd714d864
--- /dev/null
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -0,0 +1,166 @@
+raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/
+
+require 'jruby'
+include Java
+
+import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder
+import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory
+import java.io.StringReader unless defined? StringReader
+import org.xml.sax.InputSource unless defined? InputSource
+import org.xml.sax.Attributes unless defined? Attributes
+import org.w3c.dom.Node unless defined? Node
+
+# = XmlMini JRuby JDOM implementation
+module ActiveSupport
+ module XmlMini_JDOM #:nodoc:
+ extend self
+
+ CONTENT_KEY = '__content__'.freeze
+
+ NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
+ DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
+ PROCESSING_INSTRUCTION_NODE TEXT_NODE}
+
+ node_type_map = {}
+ NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type }
+
+ # Parse an XML Document string or IO into a simple hash using Java's jdom.
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if data.respond_to?(:read)
+ data = data.read
+ end
+
+ if data.blank?
+ {}
+ else
+ @dbf = DocumentBuilderFactory.new_instance
+ xml_string_reader = StringReader.new(data)
+ xml_input_source = InputSource.new(xml_string_reader)
+ doc = @dbf.new_document_builder.parse(xml_input_source)
+ merge_element!({}, doc.document_element)
+ end
+ end
+
+ private
+
+ # Convert an XML element and merge into the hash
+ #
+ # hash::
+ # Hash to merge the converted element into.
+ # element::
+ # XML element to merge into hash
+ def merge_element!(hash, element)
+ merge!(hash, element.tag_name, collapse(element))
+ end
+
+ # Actually converts an XML document element into a data structure.
+ #
+ # element::
+ # The document element to be collapsed.
+ def collapse(element)
+ hash = get_attributes(element)
+
+ child_nodes = element.child_nodes
+ if child_nodes.length > 0
+ for i in 0...child_nodes.length
+ child = child_nodes.item(i)
+ merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE
+ end
+ merge_texts!(hash, element) unless empty_content?(element)
+ hash
+ else
+ merge_texts!(hash, element)
+ end
+ end
+
+ # Merge all the texts of an element into the hash
+ #
+ # hash::
+ # Hash to add the converted emement to.
+ # element::
+ # XML element whose texts are to me merged into the hash
+ def merge_texts!(hash, element)
+ text_children = texts(element)
+ if text_children.join.empty?
+ hash
+ else
+ # must use value to prevent double-escaping
+ merge!(hash, CONTENT_KEY, text_children.join)
+ end
+ end
+
+ # Adds a new key/value pair to an existing Hash. If the key to be added
+ # already exists and the existing value associated with key is not
+ # an Array, it will be wrapped in an Array. Then the new value is
+ # appended to that Array.
+ #
+ # hash::
+ # Hash to add key/value pair to.
+ # key::
+ # Key to be added.
+ # value::
+ # Value to be associated with key.
+ def merge!(hash, key, value)
+ if hash.has_key?(key)
+ if hash[key].instance_of?(Array)
+ hash[key] << value
+ else
+ hash[key] = [hash[key], value]
+ end
+ elsif value.instance_of?(Array)
+ hash[key] = [value]
+ else
+ hash[key] = value
+ end
+ hash
+ end
+
+ # Converts the attributes array of an XML element into a hash.
+ # Returns an empty Hash if node has no attributes.
+ #
+ # element::
+ # XML element to extract attributes from.
+ def get_attributes(element)
+ attribute_hash = {}
+ attributes = element.attributes
+ for i in 0...attributes.length
+ attribute_hash[attributes.item(i).name] = attributes.item(i).value
+ end
+ attribute_hash
+ end
+
+ # Determines if a document element has text content
+ #
+ # element::
+ # XML element to be checked.
+ def texts(element)
+ texts = []
+ child_nodes = element.child_nodes
+ for i in 0...child_nodes.length
+ item = child_nodes.item(i)
+ if item.node_type == Node.TEXT_NODE
+ texts << item.get_data
+ end
+ end
+ texts
+ end
+
+ # Determines if a document element has text content
+ #
+ # element::
+ # XML element to be checked.
+ def empty_content?(element)
+ text = ''
+ child_nodes = element.child_nodes
+ for i in 0...child_nodes.length
+ item = child_nodes.item(i)
+ if item.node_type == Node.TEXT_NODE
+ text << item.get_data.strip
+ end
+ end
+ text.strip.length == 0
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 370205409a..d4c4dc7be5 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -5,16 +5,20 @@ module ActiveSupport
module XmlMini_LibXML #:nodoc:
extend self
- # Parse an XML Document string into a simple hash using libxml.
- # string::
- # XML Document string to parse
- def parse(string)
+ # Parse an XML Document string or IO into a simple hash using libxml.
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if data.respond_to?(:read)
+ data = data.read
+ end
+
LibXML::XML.default_keep_blanks = false
- if string.blank?
+ if data.blank?
{}
else
- LibXML::XML::Parser.string(string.strip).parse.to_hash
+ LibXML::XML::Parser.string(data.strip).parse.to_hash
end
end
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 8f9676e4f6..7337c143c9 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -5,14 +5,18 @@ module ActiveSupport
module XmlMini_Nokogiri #:nodoc:
extend self
- # Parse an XML Document string into a simple hash using libxml / nokogiri.
- # string::
- # XML Document string to parse
- def parse(string)
- if string.blank?
+ # Parse an XML Document string or IO into a simple hash using libxml / nokogiri.
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if data.respond_to?(:read)
+ data = data.read
+ end
+
+ if data.blank?
{}
else
- doc = Nokogiri::XML(string)
+ doc = Nokogiri::XML(data)
raise doc.errors.first if doc.errors.length > 0
doc.to_hash
end
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 771109514c..1184d2d6c9 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/blank'
+
# = XmlMini ReXML implementation
module ActiveSupport
module XmlMini_REXML #:nodoc:
@@ -5,16 +7,20 @@ module ActiveSupport
CONTENT_KEY = '__content__'.freeze
- # Parse an XML Document string into a simple hash
+ # Parse an XML Document string or IO into a simple hash
#
# Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
# and uses the defaults from ActiveSupport
#
- # string::
- # XML Document string to parse
- def parse(string)
+ # data::
+ # XML Document string or IO to parse
+ def parse(data)
+ if data.respond_to?(:read)
+ data = data.read
+ end
+
require 'rexml/document' unless defined?(REXML::Document)
- doc = REXML::Document.new(string)
+ doc = REXML::Document.new(data)
merge_element!({}, doc.root)
end
diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb
index d48c7fcc54..7a0ec2e910 100644
--- a/activesupport/test/buffered_logger_test.rb
+++ b/activesupport/test/buffered_logger_test.rb
@@ -4,11 +4,13 @@ require 'fileutils'
require 'active_support/buffered_logger'
class BufferedLoggerTest < Test::Unit::TestCase
+ Logger = ActiveSupport::BufferedLogger
+
def setup
@message = "A debug message"
@integer_message = 12345
@output = StringIO.new
- @logger = ActiveSupport::BufferedLogger.new(@output)
+ @logger = Logger.new(@output)
end
def test_should_log_debugging_message_when_debugging
@@ -76,9 +78,9 @@ class BufferedLoggerTest < Test::Unit::TestCase
define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_flush_at_max_buffer_size_as_failsafe" do
@logger.auto_flushing = disable
- assert_equal ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE, @logger.auto_flushing
+ assert_equal Logger::MAX_BUFFER_SIZE, @logger.auto_flushing
- (ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE - 1).times do
+ (Logger::MAX_BUFFER_SIZE - 1).times do
@logger.info 'wait for it..'
assert @output.string.empty?, @output.string
end
@@ -89,8 +91,8 @@ class BufferedLoggerTest < Test::Unit::TestCase
end
def test_should_know_if_its_loglevel_is_below_a_given_level
- ActiveSupport::BufferedLogger::Severity.constants.each do |level|
- @logger.level = ActiveSupport::BufferedLogger::Severity.const_get(level) - 1
+ Logger::Severity.constants.each do |level|
+ @logger.level = Logger::Severity.const_get(level) - 1
assert @logger.send("#{level.downcase}?"), "didn't know if it was #{level.downcase}? or below"
end
end
@@ -111,7 +113,7 @@ class BufferedLoggerTest < Test::Unit::TestCase
tmp_directory = File.join(File.dirname(__FILE__), "tmp")
log_file = File.join(tmp_directory, "development.log")
assert !File.exist?(tmp_directory)
- @logger = ActiveSupport::BufferedLogger.new(log_file)
+ @logger = Logger.new(log_file)
assert File.exist?(tmp_directory)
ensure
FileUtils.rm_rf(tmp_directory)
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 8bb0c155cf..51d04d9388 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -15,22 +15,34 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
end
def test_mem_cache_fragment_cache_store
+ MemCache.expects(:new).with(%w[localhost], {})
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
- assert_equal %w(localhost), store.addresses
+ end
+
+ def test_mem_cache_fragment_cache_store_with_given_mem_cache
+ mem_cache = MemCache.new
+ MemCache.expects(:new).never
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
+ assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
+ end
+
+ def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
+ MemCache.expects(:new).never
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, stub("memcache", :get => true)
+ assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_multiple_servers
+ MemCache.expects(:new).with(%w[localhost 192.168.1.1], {})
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
- assert_equal %w(localhost 192.168.1.1), store.addresses
end
def test_mem_cache_fragment_cache_store_with_options
+ MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :namespace => "foo" })
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo'
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
- assert_equal %w(localhost 192.168.1.1), store.addresses
- assert_equal 'foo', store.instance_variable_get('@data').instance_variable_get('@namespace')
end
def test_object_assigned_fragment_cache_store
@@ -168,6 +180,15 @@ uses_memcached 'memcached backed store' do
end
end
+ def test_stored_objects_should_not_be_frozen
+ @cache.with_local_cache do
+ @cache.write('foo', 'bar')
+ end
+ @cache.with_local_cache do
+ assert !@cache.read('foo').frozen?
+ end
+ end
+
def test_write_should_return_true_on_success
@cache.with_local_cache do
result = @cache.write('foo', 'bar')
@@ -251,6 +272,15 @@ uses_memcached 'memcached backed store' do
end
end
+ def test_multi_get
+ @cache.with_local_cache do
+ @cache.write('foo', 1)
+ @cache.write('goo', 2)
+ result = @cache.read_multi('foo', 'goo')
+ assert_equal({'foo' => 1, 'goo' => 2}, result)
+ end
+ end
+
def test_middleware
app = lambda { |env|
result = @cache.write('foo', 'bar')
diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb
new file mode 100644
index 0000000000..22f7ec2064
--- /dev/null
+++ b/activesupport/test/concern_test.rb
@@ -0,0 +1,88 @@
+require 'abstract_unit'
+require 'active_support/concern'
+
+class ConcernTest < Test::Unit::TestCase
+ module Baz
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def baz
+ "baz"
+ end
+
+ def included_ran=(value)
+ @@included_ran = value
+ end
+
+ def included_ran
+ @@included_ran
+ end
+ end
+
+ included do
+ self.included_ran = true
+ end
+
+ def baz
+ "baz"
+ end
+ end
+
+ module Bar
+ extend ActiveSupport::Concern
+
+ depends_on Baz
+
+ def bar
+ "bar"
+ end
+
+ def baz
+ "bar+" + super
+ end
+ end
+
+ module Foo
+ extend ActiveSupport::Concern
+
+ depends_on Bar, Baz
+ end
+
+ def setup
+ @klass = Class.new
+ end
+
+ def test_module_is_included_normally
+ @klass.send(:include, Baz)
+ assert_equal "baz", @klass.new.baz
+ assert_equal ConcernTest::Baz, @klass.included_modules[0]
+
+ @klass.send(:include, Baz)
+ assert_equal "baz", @klass.new.baz
+ assert_equal ConcernTest::Baz, @klass.included_modules[0]
+ end
+
+ def test_class_methods_are_extended
+ @klass.send(:include, Baz)
+ assert_equal "baz", @klass.baz
+ assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; self.included_modules; end)[0]
+ end
+
+ def test_included_block_is_ran
+ @klass.send(:include, Baz)
+ assert_equal true, @klass.included_ran
+ end
+
+ def test_modules_dependencies_are_met
+ @klass.send(:include, Bar)
+ assert_equal "bar", @klass.new.bar
+ assert_equal "bar+baz", @klass.new.baz
+ assert_equal "baz", @klass.baz
+ assert_equal [ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..1]
+ end
+
+ def test_depends_on_with_multiple_modules
+ @klass.send(:include, Foo)
+ assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2]
+ end
+end
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 112cb998b1..24d33896ce 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -1,6 +1,9 @@
require 'abstract_unit'
require 'active_support/core_ext/array'
require 'active_support/core_ext/big_decimal'
+require 'active_support/core_ext/object/conversions'
+
+require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions
class ArrayExtAccessTests < Test::Unit::TestCase
def test_from
diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/blank_test.rb
index 00fea74639..1dbbf3ff30 100644
--- a/activesupport/test/core_ext/blank_test.rb
+++ b/activesupport/test/core_ext/blank_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/object/blank'
class EmptyTrue
def empty?() true; end
diff --git a/activesupport/test/core_ext/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb
index 85d0dd89e2..2214ba9894 100644
--- a/activesupport/test/core_ext/class/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/class/attribute_accessor_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/class/attribute_accessors'
class ClassAttributeAccessorTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
index 7f859772e7..eeda468d9c 100644
--- a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
+++ b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/class/inheritable_attributes'
class ClassInheritableAttributesTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 45eb52c720..a7b179b2be 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/date_time'
class DateTimeExtCalculationsTest < Test::Unit::TestCase
def test_to_s
diff --git a/activesupport/test/core_ext/duplicable_test.rb b/activesupport/test/core_ext/duplicable_test.rb
index 8b6127f31e..6e1f876959 100644
--- a/activesupport/test/core_ext/duplicable_test.rb
+++ b/activesupport/test/core_ext/duplicable_test.rb
@@ -1,4 +1,6 @@
require 'abstract_unit'
+require 'bigdecimal'
+require 'active_support/core_ext/object/duplicable'
class DuplicableTest < Test::Unit::TestCase
NO = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56'), Class.new]
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index d4a4627e3e..6f16621ae5 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/time'
class DurationTest < ActiveSupport::TestCase
def test_inspect
@@ -41,23 +42,23 @@ class DurationTest < ActiveSupport::TestCase
end
def test_since_and_ago_with_fractional_days
- Time.stubs(:now).returns Time.local(2000)
+ t = Time.local(2000)
# since
- assert_equal 36.hours.since, 1.5.days.since
- assert_equal((24 * 1.7).hours.since, 1.7.days.since)
+ assert_equal 36.hours.since(t), 1.5.days.since(t)
+ assert_in_delta((24 * 1.7).hours.since(t), 1.7.days.since(t), 1)
# ago
- assert_equal 36.hours.ago, 1.5.days.ago
- assert_equal((24 * 1.7).hours.ago, 1.7.days.ago)
+ assert_equal 36.hours.ago(t), 1.5.days.ago(t)
+ assert_in_delta((24 * 1.7).hours.ago(t), 1.7.days.ago(t), 1)
end
def test_since_and_ago_with_fractional_weeks
- Time.stubs(:now).returns Time.local(2000)
+ t = Time.local(2000)
# since
- assert_equal((7 * 36).hours.since, 1.5.weeks.since)
- assert_equal((7 * 24 * 1.7).hours.since, 1.7.weeks.since)
+ assert_equal((7 * 36).hours.since(t), 1.5.weeks.since(t))
+ assert_in_delta((7 * 24 * 1.7).hours.since(t), 1.7.weeks.since(t), 1)
# ago
- assert_equal((7 * 36).hours.ago, 1.5.weeks.ago)
- assert_equal((7 * 24 * 1.7).hours.ago, 1.7.weeks.ago)
+ assert_equal((7 * 36).hours.ago(t), 1.5.weeks.ago(t))
+ assert_in_delta((7 * 24 * 1.7).hours.ago(t), 1.7.weeks.ago(t), 1)
end
def test_since_and_ago_anchored_to_time_now_when_time_zone_default_not_set
diff --git a/activesupport/test/core_ext/exception_test.rb b/activesupport/test/core_ext/exception_test.rb
index dabd8c7c06..e63842c0bd 100644
--- a/activesupport/test/core_ext/exception_test.rb
+++ b/activesupport/test/core_ext/exception_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/exception'
class ExceptionExtTests < Test::Unit::TestCase
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index bbb1c631f9..ece5466abb 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -1,5 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/hash'
+require 'bigdecimal'
+require 'active_support/core_ext/string/access'
class HashExtTest < Test::Unit::TestCase
def setup
@@ -644,6 +646,22 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"]
end
+ def test_all_caps_key_from_xml
+ test_xml = <<-EOT
+ <ABC3XYZ>
+ <TEST>Lorem Ipsum</TEST>
+ </ABC3XYZ>
+ EOT
+
+ expected_hash = {
+ "ABC3XYZ" => {
+ "TEST" => "Lorem Ipsum"
+ }
+ }
+
+ assert_equal expected_hash, Hash.from_xml(test_xml)
+ end
+
def test_empty_array_from_xml
blog_xml = <<-XML
<blog>
diff --git a/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb b/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
index 7c59348af9..7c0d0bb242 100644
--- a/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
+++ b/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/module/attr_accessor_with_default'
class AttrAccessorWithDefaultTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/core_ext/module/attr_internal_test.rb b/activesupport/test/core_ext/module/attr_internal_test.rb
index 52833019e7..93578c9610 100644
--- a/activesupport/test/core_ext/module/attr_internal_test.rb
+++ b/activesupport/test/core_ext/module/attr_internal_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/module/attr_internal'
class AttrInternalTest < Test::Unit::TestCase
def setup
@@ -37,8 +38,8 @@ class AttrInternalTest < Test::Unit::TestCase
end
def test_naming_format
- assert_equal '@_%s', @target.attr_internal_naming_format
- assert_nothing_raised { @target.attr_internal_naming_format = '@abc%sdef' }
+ assert_equal '@_%s', Module.attr_internal_naming_format
+ assert_nothing_raised { Module.attr_internal_naming_format = '@abc%sdef' }
@target.attr_internal :foo
assert !@instance.instance_variable_defined?('@_foo')
@@ -47,6 +48,6 @@ class AttrInternalTest < Test::Unit::TestCase
assert !@instance.instance_variable_defined?('@_foo')
assert @instance.instance_variable_defined?('@abcfoodef')
ensure
- @target.attr_internal_naming_format = '@_%s'
+ Module.attr_internal_naming_format = '@_%s'
end
end
diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb
index 96975085cf..bd9461e62c 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/module/attribute_accessors'
class ModuleAttributeAccessorTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/core_ext/module/attribute_aliasing_test.rb b/activesupport/test/core_ext/module/attribute_aliasing_test.rb
index 570ac3b8d5..f17d031662 100644
--- a/activesupport/test/core_ext/module/attribute_aliasing_test.rb
+++ b/activesupport/test/core_ext/module/attribute_aliasing_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/module/aliasing'
module AttributeAliasing
class Content
diff --git a/activesupport/test/core_ext/module/model_naming_test.rb b/activesupport/test/core_ext/module/model_naming_test.rb
index d08349dd97..da3b6c4932 100644
--- a/activesupport/test/core_ext/module/model_naming_test.rb
+++ b/activesupport/test/core_ext/module/model_naming_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/module/model_naming'
class ModelNamingTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb
index bae004809f..10913e2ade 100644
--- a/activesupport/test/core_ext/name_error_test.rb
+++ b/activesupport/test/core_ext/name_error_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/name_error'
class NameErrorTest < Test::Unit::TestCase
def test_name_error_should_set_missing_name
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index 9cca4d3aaf..992ec60302 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
-
-require 'active_support/core_ext/numeric/bytes'
+require 'active_support/time'
+require 'active_support/core_ext/numeric'
+require 'active_support/core_ext/integer'
class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase
def setup
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 c919698c0f..f0121b862d 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -1,5 +1,7 @@
require 'abstract_unit'
+require 'active_support/time'
require 'active_support/core_ext/object'
+require 'active_support/core_ext/class/removal'
class ClassA; end
class ClassB < ClassA; end
diff --git a/activesupport/test/core_ext/proc_test.rb b/activesupport/test/core_ext/proc_test.rb
index 29f85371de..dc7b2c957d 100644
--- a/activesupport/test/core_ext/proc_test.rb
+++ b/activesupport/test/core_ext/proc_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/proc'
class ProcTests < Test::Unit::TestCase
def test_bind_returns_method_with_changed_self
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 76e05e9954..2565c56b8a 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'active_support/core_ext/range'
+require 'active_support/core_ext/date/conversions'
class RangeTest < Test::Unit::TestCase
def test_to_s_from_dates
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index a357ba5852..237a843f9a 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -4,6 +4,7 @@ require 'abstract_unit'
require 'inflector_test_cases'
require 'active_support/core_ext/string'
+require 'active_support/core_ext/time'
class StringInflectionsTest < Test::Unit::TestCase
include InflectorTestCases
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 8ee4904036..1c2d0fbce4 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/time'
class TimeExtCalculationsTest < Test::Unit::TestCase
def test_seconds_since_midnight
@@ -423,10 +424,10 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.local(2005,6,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:months => 4)
assert_equal Time.local(2005,3,21,15,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3)
assert_equal Time.local(2005,3,25,3,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.5)
- assert_equal Time.local(2005,3,26,12,51,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.7)
+ assert_in_delta Time.local(2005,3,26,12,51,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.7), 1
assert_equal Time.local(2005,3,5,15,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5)
assert_equal Time.local(2005,3,6,3,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.5)
- assert_equal Time.local(2005,3,6,8,3,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.7)
+ assert_in_delta Time.local(2005,3,6,8,3,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.7), 1
assert_equal Time.local(2012,9,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 7)
assert_equal Time.local(2013,10,3,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5)
assert_equal Time.local(2013,10,17,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
@@ -445,10 +446,10 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.utc(2005,6,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:months => 4)
assert_equal Time.utc(2005,3,21,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3)
assert_equal Time.utc(2005,3,25,3,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.5)
- assert_equal Time.utc(2005,3,26,12,51,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.7)
+ assert_in_delta Time.utc(2005,3,26,12,51,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.7), 1
assert_equal Time.utc(2005,3,5,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5)
assert_equal Time.utc(2005,3,6,3,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.5)
- assert_equal Time.utc(2005,3,6,8,3,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.7)
+ assert_in_delta Time.utc(2005,3,6,8,3,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.7), 1
assert_equal Time.utc(2012,9,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 7)
assert_equal Time.utc(2013,10,3,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 19, :days => 11)
assert_equal Time.utc(2013,10,17,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 3dec4c95f4..03ae70d420 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'active_support/time_with_zone'
+require 'active_support/json'
class TimeWithZoneTest < Test::Unit::TestCase
@@ -56,12 +57,12 @@ class TimeWithZoneTest < Test::Unit::TestCase
end
def test_to_json
- assert_equal "\"1999/12/31 19:00:00 -0500\"", @twz.to_json
+ assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(@twz)
end
def test_to_json_with_use_standard_json_time_format_config_set_to_true
old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, true
- assert_equal "\"1999-12-31T19:00:00-05:00\"", @twz.to_json
+ assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(@twz)
ensure
ActiveSupport.use_standard_json_time_format = old
end
@@ -91,7 +92,7 @@ class TimeWithZoneTest < Test::Unit::TestCase
end
def test_xmlschema_with_fractional_seconds
- @twz += 0.123456 # advance the time by a fraction of a second
+ @twz += 0.1234560001 # advance the time by a fraction of a second
assert_equal "1999-12-31T19:00:00.123-05:00", @twz.xmlschema(3)
assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(6)
assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(12)
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index a21f09403f..99c53924c2 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -1,5 +1,8 @@
require 'abstract_unit'
require 'pp'
+require 'active_support/dependencies'
+require 'active_support/core_ext/module/loading'
+require 'active_support/core_ext/kernel/reporting'
module ModuleWithMissing
mattr_accessor :missing_count
diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb
index 7535f4ad7a..9868f1e87d 100644
--- a/activesupport/test/i18n_test.rb
+++ b/activesupport/test/i18n_test.rb
@@ -1,4 +1,6 @@
require 'abstract_unit'
+require 'active_support/time'
+require 'active_support/core_ext/array/conversions'
class I18nTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index 6b9fbd3156..8c4d831a39 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -1,4 +1,6 @@
require 'abstract_unit'
+require 'active_support/inflector'
+
require 'inflector_test_cases'
module Ace
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 8fe40557d6..09fd0d09ba 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -1,48 +1,78 @@
# encoding: UTF-8
require 'abstract_unit'
+require 'active_support/json'
+require 'active_support/core_ext/kernel/reporting'
-class TestJSONDecoding < Test::Unit::TestCase
+class TestJSONDecoding < ActiveSupport::TestCase
TESTS = {
%q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
- %q({returnTo:{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
%q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
%q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}},
%({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
%({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
- %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
- %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
+ %({"a": "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
+ %({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
# multibyte
%({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"},
- %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
- %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
+ %({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
+ %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
# no time zone
- %({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
+ %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
# needs to be *exact*
- %({a: " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
- %({a: "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"},
+ %({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
+ %({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"},
%([]) => [],
%({}) => {},
- %(1) => 1,
- %("") => "",
- %("\\"") => "\"",
- %(null) => nil,
- %(true) => true,
- %(false) => false,
- %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1",
- %q("\u003cunicode\u0020escape\u003e") => "<unicode escape>",
- %q("\\\\u0020skip double backslashes") => "\\u0020skip double backslashes",
- %q({a: "\u003cbr /\u003e"}) => {'a' => "<br />"},
- %q({b:["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]}
+ %({"a":1}) => {"a" => 1},
+ %({"a": ""}) => {"a" => ""},
+ %({"a":"\\""}) => {"a" => "\""},
+ %({"a": null}) => {"a" => nil},
+ %({"a": true}) => {"a" => true},
+ %({"a": false}) => {"a" => false},
+ %q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"},
+ %q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => "<unicode escape>"},
+ %q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"},
+ %q({"a": "\u003cbr /\u003e"}) => {'a' => "<br />"},
+ %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]}
}
-
- TESTS.each do |json, expected|
- define_method :"test_json_decoding_#{json}" do
- assert_nothing_raised do
- assert_equal expected, ActiveSupport::JSON.decode(json)
+
+ # load the default JSON backend
+ ActiveSupport::JSON.backend
+
+ backends = %w(Yaml)
+ begin
+ gem 'json', '>= 1.1'
+ require 'json'
+ backends << "JSONGem"
+ rescue Gem::LoadError
+ # Skip JSON gem tests
+ end
+
+ backends.each do |backend|
+ TESTS.each do |json, expected|
+ test "json decodes #{json} with the #{backend} backend" do
+ ActiveSupport.parse_json_times = true
+ silence_warnings do
+ ActiveSupport::JSON.with_backend backend do
+ assert_nothing_raised do
+ assert_equal expected, ActiveSupport::JSON.decode(json)
+ end
+ end
+ end
end
end
end
-
+
+ if backends.include?("JSONGem")
+ test "json decodes time json with time parsing disabled" do
+ ActiveSupport.parse_json_times = false
+ expected = {"a" => "2007-01-01 01:12:34 Z"}
+ ActiveSupport::JSON.with_backend "JSONGem" do
+ assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
+ end
+ end
+ end
+
def test_failed_json_decoding
assert_raise(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) }
end
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 9e6b4fa501..1a0e6d543c 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -19,14 +19,14 @@ class TestJSONEncoding < Test::Unit::TestCase
[ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
[ 'http://test.host/posts/1', %("http://test.host/posts/1")]]
- ArrayTests = [[ ['a', 'b', 'c'], %([\"a\", \"b\", \"c\"]) ],
- [ [1, 'a', :b, nil, false], %([1, \"a\", \"b\", null, false]) ]]
+ ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
+ [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
SymbolTests = [[ :a, %("a") ],
[ :this, %("this") ],
[ :"a b", %("a b") ]]
- ObjectTests = [[ Foo.new(1, 2), %({\"a\": 1, \"b\": 2}) ]]
+ ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'],
[ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']]
@@ -47,7 +47,7 @@ class TestJSONEncoding < Test::Unit::TestCase
ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/
ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/
self.class.const_get(class_tests).each do |pair|
- assert_equal pair.last, pair.first.to_json
+ assert_equal pair.last, ActiveSupport::JSON.encode(pair.first)
end
ensure
ActiveSupport.escape_html_entities_in_json = false
@@ -57,45 +57,45 @@ class TestJSONEncoding < Test::Unit::TestCase
end
def test_hash_encoding
- assert_equal %({\"a\": \"b\"}), { :a => :b }.to_json
- assert_equal %({\"a\": 1}), { 'a' => 1 }.to_json
- assert_equal %({\"a\": [1, 2]}), { 'a' => [1,2] }.to_json
- assert_equal %({"1": 2}), { 1 => 2 }.to_json
+ assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b)
+ assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1)
+ assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode('a' => [1,2])
+ assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
- sorted_json = '{' + {:a => :b, :c => :d}.to_json[1..-2].split(', ').sort.join(', ') + '}'
- assert_equal %({\"a\": \"b\", \"c\": \"d\"}), sorted_json
+ sorted_json = '{' + ActiveSupport::JSON.encode(:a => :b, :c => :d)[1..-2].split(',').sort.join(',') + '}'
+ assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json
end
def test_utf8_string_encoded_properly_when_kcode_is_utf8
with_kcode 'UTF8' do
- assert_equal '"\\u20ac2.99"', '€2.99'.to_json
- assert_equal '"\\u270e\\u263a"', '✎☺'.to_json
+ assert_equal '"\\u20ac2.99"', ActiveSupport::JSON.encode('€2.99')
+ assert_equal '"\\u270e\\u263a"', ActiveSupport::JSON.encode('✎☺')
end
end
def test_exception_raised_when_encoding_circular_reference
a = [1]
a << a
- assert_raise(ActiveSupport::JSON::CircularReferenceError) { a.to_json }
+ assert_raise(ActiveSupport::JSON::CircularReferenceError) { ActiveSupport::JSON.encode(a) }
end
def test_hash_key_identifiers_are_always_quoted
values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
- assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(values.to_json)
+ assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
end
def test_hash_should_allow_key_filtering_with_only
- assert_equal %({"a": 1}), { 'a' => 1, :b => 2, :c => 3 }.to_json(:only => 'a')
+ assert_equal %({"a":1}), ActiveSupport::JSON.encode({'a' => 1, :b => 2, :c => 3}, :only => 'a')
end
def test_hash_should_allow_key_filtering_with_except
- assert_equal %({"b": 2}), { 'foo' => 'bar', :b => 2, :c => 3 }.to_json(:except => ['foo', :c])
+ assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c])
end
def test_time_to_json_includes_local_offset
ActiveSupport.use_standard_json_time_format = true
with_env_tz 'US/Eastern' do
- assert_equal %("2005-02-01T15:15:10-05:00"), Time.local(2005,2,1,15,15,10).to_json
+ assert_equal %("2005-02-01T15:15:10-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10))
end
ensure
ActiveSupport.use_standard_json_time_format = false
@@ -109,7 +109,7 @@ class TestJSONEncoding < Test::Unit::TestCase
:latitude => 123.234
}
}
- result = hash.to_json
+ result = ActiveSupport::JSON.encode(hash)
end
end
@@ -134,6 +134,6 @@ class JsonOptionsTests < Test::Unit::TestCase
ActiveSupport::JSON.expects(:encode).with(2, json_options)
ActiveSupport::JSON.expects(:encode).with('foo', json_options)
- [1, 2, 'foo'].to_json(json_options)
+ [1, 2, 'foo'].send(:rails_to_json, json_options)
end
end
diff --git a/activesupport/test/new_callbacks_test.rb b/activesupport/test/new_callbacks_test.rb
index 5cde078b65..7bec47224d 100644
--- a/activesupport/test/new_callbacks_test.rb
+++ b/activesupport/test/new_callbacks_test.rb
@@ -19,11 +19,11 @@ module NewCallbacksTest
class << self
def callback_symbol(callback_method)
- returning(:"#{callback_method}_method") do |method_name|
- define_method(method_name) do
- history << [callback_method, :symbol]
- end
+ method_name = :"#{callback_method}_method"
+ define_method(method_name) do
+ history << [callback_method, :symbol]
end
+ method_name
end
def callback_string(callback_method)
@@ -255,6 +255,26 @@ module NewCallbacksTest
end
end
+ class HyphenatedCallbacks
+ include ActiveSupport::NewCallbacks
+ define_callbacks :save
+ attr_reader :stuff
+
+ save_callback :before, :omg, :per_key => {:if => :yes}
+
+ def yes() true end
+
+ def omg
+ @stuff = "OMG"
+ end
+
+ def save
+ _run_save_callbacks("hyphen-ated") do
+ @stuff
+ end
+ end
+ end
+
class AroundCallbacksTest < Test::Unit::TestCase
def test_save_around
around = AroundPerson.new
@@ -345,7 +365,7 @@ module NewCallbacksTest
save_callback :after, :third
- attr_reader :history
+ attr_reader :history, :saved
def initialize
@history = []
end
@@ -370,8 +390,72 @@ module NewCallbacksTest
end
def save
- _run_save_callbacks
+ _run_save_callbacks do
+ @saved = true
+ end
+ end
+ end
+
+ class CallbackObject
+ def before_save(caller)
+ caller.record << "before"
+ end
+
+ def around_save(caller)
+ caller.record << "around before"
+ yield
+ caller.record << "around after"
+ end
+ end
+
+ class UsingObjectBefore
+ include ActiveSupport::NewCallbacks
+
+ define_callbacks :save
+ save_callback :before, CallbackObject.new
+
+ attr_accessor :record
+ def initialize
+ @record = []
+ end
+
+ def save
+ _run_save_callbacks do
+ @record << "yielded"
+ end
+ end
+ end
+
+ class UsingObjectAround
+ include ActiveSupport::NewCallbacks
+
+ define_callbacks :save
+ save_callback :around, CallbackObject.new
+
+ attr_accessor :record
+ def initialize
+ @record = []
+ end
+
+ def save
+ _run_save_callbacks do
+ @record << "yielded"
+ end
+ end
+ end
+
+ class UsingObjectTest < Test::Unit::TestCase
+ def test_before_object
+ u = UsingObjectBefore.new
+ u.save
+ assert_equal ["before", "yielded"], u.record
end
+
+ def test_around_object
+ u = UsingObjectAround.new
+ u.save
+ assert_equal ["around before", "yielded", "around after"], u.record
+ end
end
class CallbackTerminatorTest < Test::Unit::TestCase
@@ -380,5 +464,19 @@ module NewCallbacksTest
terminator.save
assert_equal ["first", "second", "third", "second", "first"], terminator.history
end
+
+ def test_block_never_called_if_terminated
+ obj = CallbackTerminator.new
+ obj.save
+ assert !obj.saved
+ end
end
-end \ No newline at end of file
+
+ class HyphenatedKeyTest < Test::Unit::TestCase
+ def test_save
+ obj = HyphenatedCallbacks.new
+ obj.save
+ assert_equal obj.stuff, "OMG"
+ end
+ end
+end
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 7cd8c8a8f4..647938dd87 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -51,6 +51,10 @@ class OrderedHashTest < Test::Unit::TestCase
assert_same @ordered_hash, @ordered_hash.to_hash
end
+ def test_to_a
+ assert_equal @keys.zip(@values), @ordered_hash.to_a
+ end
+
def test_has_key
assert_equal true, @ordered_hash.has_key?('blue')
assert_equal true, @ordered_hash.key?('blue')
@@ -158,4 +162,10 @@ class OrderedHashTest < Test::Unit::TestCase
def test_inspect
assert @ordered_hash.inspect.include?(@hash.inspect)
end
+
+ def test_alternate_initialization
+ alternate = ActiveSupport::OrderedHash[1,2,3,4]
+ assert_kind_of ActiveSupport::OrderedHash, alternate
+ assert_equal [1, 3], alternate.keys
+ end
end
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index d250b10822..40d3d612e7 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/core_ext/kernel/reporting'
class AssertDifferenceTest < ActiveSupport::TestCase
def setup
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 7e8047566e..99c4310854 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/time'
class TimeZoneTest < Test::Unit::TestCase
def test_utc_to_local
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
new file mode 100644
index 0000000000..b745228994
--- /dev/null
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -0,0 +1,153 @@
+require 'abstract_unit'
+require 'active_support/xml_mini'
+
+if RUBY_PLATFORM =~ /java/
+
+class JDOMEngineTest < Test::Unit::TestCase
+ include ActiveSupport
+
+ def setup
+ @default_backend = XmlMini.backend
+ XmlMini.backend = 'JDOM'
+ end
+
+ def teardown
+ XmlMini.backend = @default_backend
+ end
+
+ # def test_file_from_xml
+ # hash = Hash.from_xml(<<-eoxml)
+ # <blog>
+ # <logo type="file" name="logo.png" content_type="image/png">
+ # </logo>
+ # </blog>
+ # eoxml
+ # assert hash.has_key?('blog')
+ # assert hash['blog'].has_key?('logo')
+ #
+ # file = hash['blog']['logo']
+ # assert_equal 'logo.png', file.original_filename
+ # assert_equal 'image/png', file.content_type
+ # end
+
+ def test_exception_thrown_on_expansion_attack
+ assert_raise NativeException do
+ attack_xml = <<-EOT
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE member [
+ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+ <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+ <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+ <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+ <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+ <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+ <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
+ ]>
+ <member>
+ &a;
+ </member>
+ EOT
+ Hash.from_xml(attack_xml)
+ end
+ end
+
+ def test_setting_JDOM_as_backend
+ XmlMini.backend = 'JDOM'
+ assert_equal XmlMini_JDOM, XmlMini.backend
+ end
+
+ def test_blank_returns_empty_hash
+ assert_equal({}, XmlMini.parse(nil))
+ assert_equal({}, XmlMini.parse(''))
+ end
+
+ def test_array_type_makes_an_array
+ assert_equal_rexml(<<-eoxml)
+ <blog>
+ <posts type="array">
+ <post>a post</post>
+ <post>another post</post>
+ </posts>
+ </blog>
+ eoxml
+ end
+
+ def test_one_node_document_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products/>
+ eoxml
+ end
+
+ def test_one_node_with_attributes_document_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products foo="bar"/>
+ eoxml
+ end
+
+ def test_products_node_with_book_node_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ <book name="awesome" id="12345" />
+ </products>
+ eoxml
+ end
+
+ def test_products_node_with_two_book_nodes_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ <book name="awesome" id="12345" />
+ <book name="america" id="67890" />
+ </products>
+ eoxml
+ end
+
+ def test_single_node_with_content_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ hello world
+ </products>
+ eoxml
+ end
+
+ def test_children_with_children
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ <book name="america" id="67890" />
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_text
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ hello everyone
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_non_adjacent_text
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ good
+ <products>
+ hello everyone
+ </products>
+ morning
+ </root>
+ eoxml
+ end
+
+ private
+ def assert_equal_rexml(xml)
+ hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
+ assert_equal(hash, XmlMini.parse(xml))
+ end
+end
+
+else
+ # don't run these test because we aren't running in JRuby
+end
diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb
index e5174a0b57..7c3a591e63 100644
--- a/activesupport/test/xml_mini/nokogiri_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'active_support/xml_mini'
+require 'active_support/core_ext/hash/conversions'
begin
gem 'nokogiri', '>= 1.1.1'
@@ -147,6 +148,19 @@ class NokogiriEngineTest < Test::Unit::TestCase
eoxml
end
+ def test_parse_from_io
+ io = StringIO.new(<<-eoxml)
+ <root>
+ good
+ <products>
+ hello everyone
+ </products>
+ morning
+ </root>
+ eoxml
+ XmlMini.parse(io)
+ end
+
private
def assert_equal_rexml(xml)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index a412d8ca05..57bb35254a 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -12,4 +12,18 @@ class REXMLEngineTest < Test::Unit::TestCase
XmlMini.backend = 'REXML'
assert_equal XmlMini_REXML, XmlMini.backend
end
+
+ def test_parse_from_io
+ XmlMini.backend = 'REXML'
+ io = StringIO.new(<<-eoxml)
+ <root>
+ good
+ <products>
+ hello everyone
+ </products>
+ morning
+ </root>
+ eoxml
+ XmlMini.parse(io)
+ end
end
diff --git a/ci/ci_setup_notes.txt b/ci/ci_setup_notes.txt
index 63f1851b19..780277c939 100644
--- a/ci/ci_setup_notes.txt
+++ b/ci/ci_setup_notes.txt
@@ -47,7 +47,7 @@ $ sudo aptitude update
* If you did not configure no-root-gem installation via ~/.gemrc as shown above, then allow no-password sudo for gem installation:
$ sudo visudo
# add this line to bottom:
-ci ALL=NOPASSWD: /usr/local/bin/geminstaller, /usr/local/bin/ruby, /usr/local/bin/gem
+ci ALL=(ALL) NOPASSWD: ALL
* Start ccrb via init script and check for default homepage at port 3333
diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb
index 7985e3c8df..2247a1b6ea 100644
--- a/ci/cruise_config.rb
+++ b/ci/cruise_config.rb
@@ -1,5 +1,5 @@
Project.configure do |project|
- project.build_command = 'ruby ci/ci_build.rb'
+ project.build_command = 'sudo update_rubygems && ruby ci/ci_build.rb'
project.email_notifier.emails = ['thewoolleyman@gmail.com']
# project.email_notifier.emails = ['thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com', 'wycats@gmail.com']
project.email_notifier.from = 'thewoolleyman+railsci@gmail.com'
diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml
index 33f1e81179..f1ef9b59a0 100644
--- a/ci/geminstaller.yml
+++ b/ci/geminstaller.yml
@@ -14,10 +14,12 @@ gems:
- name: pg
version: >= 0.7.9.2008.10.13
- name: rack
- version: '~> 0.9.0'
+ version: '~> 1.0.0'
- name: rake
version: >= 0.8.1
- name: sqlite-ruby
version: >= 2.2.3
- name: sqlite3-ruby
version: >= 1.2.2
+- name: rubygems-update
+ version: >= 1.3.3
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 98e3a861e8..782afd5aa4 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,3 +1,10 @@
+*Edge*
+
+* Ruby 1.9: use UTF-8 for default internal and external encodings. [Jeremy Kemper]
+
+* Added db/seeds.rb as a default file for storing seed data for the database. Can be loaded with rake db:seed (or created alongside the db with db:setup). (This is also known as the "Stop Putting Gawd Damn Seed Data In Your Migrations" feature) [DHH]
+
+
*2.3.2 [Final] (March 15, 2009)*
* Remove outdated script/plugin options [Pratik Naik]
diff --git a/railties/Rakefile b/railties/Rakefile
index a9adbda0b5..69c1ca762a 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -2,7 +2,6 @@ require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
require 'date'
require 'rbconfig'
@@ -29,12 +28,13 @@ task :default => :test
task :test do
Dir['test/**/*_test.rb'].all? do |file|
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
- system(ruby, '-Itest', file)
+ system(ruby, '-Itest', "-I#{File.dirname(__FILE__)}/../activesupport/lib", file)
end or raise "Failures"
end
+task :isolated_test => :test
Rake::TestTask.new("regular_test") do |t|
- t.libs << 'test'
+ t.libs << 'test' << "#{File.dirname(__FILE__)}/../activesupport/lib"
t.pattern = 'test/**/*_test.rb'
t.warning = true
t.verbose = true
@@ -200,11 +200,14 @@ task :copy_configs do
cp "configs/locales/en.yml", "#{PKG_DESTINATION}/config/locales/en.yml"
+ cp "configs/seeds.rb", "#{PKG_DESTINATION}/db/seeds.rb"
+
cp "environments/boot.rb", "#{PKG_DESTINATION}/config/boot.rb"
cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb"
cp "environments/production.rb", "#{PKG_DESTINATION}/config/environments/production.rb"
cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb"
cp "environments/test.rb", "#{PKG_DESTINATION}/config/environments/test.rb"
+
end
task :copy_binfiles do
@@ -246,7 +249,7 @@ def copy_with_rewritten_ruby_path(src_file, dest_file)
end
desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"'
-task :guides do
+task :generate_guides do
ENV["WARN_BROKEN_LINKS"] = "1" # authors can't disable this
ruby "guides/rails_guides.rb"
end
@@ -295,6 +298,7 @@ PKG_FILES = FileList[
'doc/**/*',
'dispatches/**/*',
'environments/**/*',
+ 'guides/**/*',
'helpers/**/*',
'generators/**/*',
'html/**/*',
@@ -321,7 +325,7 @@ spec = Gem::Specification.new do |s|
s.rdoc_options << '--exclude' << '.'
s.has_rdoc = false
- s.files = PKG_FILES.to_a.delete_if {|f| f.include?('.svn')}
+ s.files = PKG_FILES.to_a.delete_if {|f| f =~ %r{\.svn|guides/output}}
s.require_path = 'lib'
s.bindir = "bin" # Use these for applications.
s.executables = ["rails"]
@@ -341,12 +345,14 @@ end
# Publishing -------------------------------------------------------
desc "Publish the rails gem"
task :pgem => [:gem] 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 guides"
-task :pguides => :guides do
+task :pguides => :generate_guides do
+ require 'rake/contrib/sshpublisher'
mkdir_p 'pkg'
`tar -czf pkg/guides.gz guides/output`
Rake::SshFilePublisher.new("web.rubyonrails.org", "/u/sites/guides.rubyonrails.org/public", "pkg", "guides.gz").upload
@@ -355,6 +361,7 @@ end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
+ require 'rake/contrib/rubyforgepublisher'
require 'rubyforge'
packages = %w( gem ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
diff --git a/railties/builtin/rails_info/rails/info.rb b/railties/builtin/rails_info/rails/info.rb
index 0dd1c090c1..fdacc469f5 100644
--- a/railties/builtin/rails_info/rails/info.rb
+++ b/railties/builtin/rails_info/rails/info.rb
@@ -1,9 +1,13 @@
+require "active_support/core_ext/object/misc"
+require "cgi"
+require "active_support/core_ext/cgi"
+
module Rails
module Info
mattr_accessor :properties
class << (@@properties = [])
def names
- map &:first
+ map {|val| val.first }
end
def value_for(property_name)
@@ -53,7 +57,7 @@ module Rails
alias inspect to_s
def to_html
- returning table = '<table>' do
+ (table = '<table>').tap do
properties.each do |(name, value)|
table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
formatted_value = if value.kind_of?(Array)
@@ -108,7 +112,7 @@ module Rails
end
property 'Middleware' do
- ActionController::Dispatcher.middleware.active.map(&:inspect)
+ ActionController::Dispatcher.middleware.active.map {|middle| middle.inspect }
end
# The Rails Git revision, if it's checked out into vendor/rails.
diff --git a/railties/configs/routes.rb b/railties/configs/routes.rb
index 4f3d9d22dd..ea14ce1bfc 100644
--- a/railties/configs/routes.rb
+++ b/railties/configs/routes.rb
@@ -37,7 +37,7 @@ ActionController::Routing::Routes.draw do |map|
# Install the default routes as the lowest priority.
# Note: These default routes make all actions in every controller accessible via GET requests. You should
- # consider removing the them or commenting them out if you're using named routes and resources.
+ # consider removing or commenting them out if you're using named routes and resources.
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
diff --git a/railties/configs/seeds.rb b/railties/configs/seeds.rb
new file mode 100644
index 0000000000..3174d0cb8a
--- /dev/null
+++ b/railties/configs/seeds.rb
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
+# Major.create(:name => 'Daley', :city => cities.first)
diff --git a/railties/lib/commands/performance/profiler.rb b/railties/lib/commands/performance/profiler.rb
index fd111bae87..7df840f197 100644
--- a/railties/lib/commands/performance/profiler.rb
+++ b/railties/lib/commands/performance/profiler.rb
@@ -29,7 +29,7 @@ begin
printer_class = RubyProf::FlatPrinter
end
printer = printer_class.new(results)
- printer.print($stderr, 0)
+ printer.print($stderr)
rescue LoadError
require "prof"
$stderr.puts 'Using the old ruby-prof extension.'
diff --git a/railties/lib/commands/server.rb b/railties/lib/commands/server.rb
index ebe34a42cd..01dd33fa8c 100644
--- a/railties/lib/commands/server.rb
+++ b/railties/lib/commands/server.rb
@@ -1,4 +1,3 @@
-require 'active_support'
require 'action_controller'
require 'fileutils'
@@ -23,17 +22,17 @@ options = {
ARGV.clone.options do |opts|
opts.on("-p", "--port=port", Integer,
- "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
+ "Runs Rails on the specified port.", "Default: #{options[:Port]}") { |v| options[:Port] = v }
opts.on("-b", "--binding=ip", String,
- "Binds Rails to the specified ip.", "Default: 0.0.0.0") { |v| options[:Host] = v }
+ "Binds Rails to the specified ip.", "Default: #{options[:Host]}") { |v| options[:Host] = v }
opts.on("-c", "--config=file", String,
"Use custom rackup configuration file") { |v| options[:config] = v }
opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:detach] = true }
opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { options[:debugger] = true }
opts.on("-e", "--environment=name", String,
"Specifies the environment to run this server under (test/development/production).",
- "Default: development") { |v| options[:environment] = v }
- opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: /") { |v| options[:path] = v }
+ "Default: #{options[:environment]}") { |v| options[:environment] = v }
+ opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: #{options[:path]}") { |v| options[:path] = v }
opts.separator ""
diff --git a/railties/lib/console_app.rb b/railties/lib/console_app.rb
index d7d01d703f..75e6f11ea3 100644
--- a/railties/lib/console_app.rb
+++ b/railties/lib/console_app.rb
@@ -1,3 +1,4 @@
+require 'active_support/all'
require 'active_support/test_case'
require 'action_controller'
@@ -24,7 +25,7 @@ end
#reloads the environment
def reload!
puts "Reloading..."
- Dispatcher.cleanup_application
- Dispatcher.reload_application
+ ActionController::Dispatcher.new
+ ActionController::Dispatcher.router.reload
true
end
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index a03be59a2b..bbaa313fae 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -5,12 +5,9 @@ require 'pathname'
$LOAD_PATH.unshift File.dirname(__FILE__)
require 'railties_path'
require 'rails/version'
-require 'rails/plugin/locator'
-require 'rails/plugin/loader'
require 'rails/gem_dependency'
require 'rails/rack'
-
RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
module Rails
@@ -159,6 +156,8 @@ module Rails
add_support_load_paths
+ check_for_unbuilt_gems
+
load_gems
load_plugins
@@ -244,6 +243,7 @@ module Rails
# Set the paths from which Rails will automatically load source files, and
# the load_once paths.
def set_autoload_paths
+ require 'active_support/dependencies'
ActiveSupport::Dependencies.load_paths = configuration.load_paths.uniq
ActiveSupport::Dependencies.load_once_paths = configuration.load_once_paths.uniq
@@ -263,6 +263,7 @@ module Rails
# list. By default, all frameworks (Active Record, Active Support,
# Action Pack, Action Mailer, and Active Resource) are loaded.
def require_frameworks
+ require 'active_support/all'
configuration.frameworks.each { |framework| require(framework.to_s) }
rescue LoadError => e
# Re-raise as RuntimeError because Mongrel would swallow LoadError.
@@ -289,10 +290,12 @@ module Rails
# Adds all load paths from plugins to the global set of load paths, so that
# code from plugins can be required (explicitly or automatically via ActiveSupport::Dependencies).
def add_plugin_load_paths
+ require 'active_support/dependencies'
plugin_loader.add_plugin_load_paths
end
def add_gem_load_paths
+ require 'rails/gem_dependency'
Rails::GemDependency.add_frozen_gem_path
unless @configuration.gems.empty?
require "rubygems"
@@ -306,6 +309,25 @@ module Rails
end
end
+ def check_for_unbuilt_gems
+ unbuilt_gems = @configuration.gems.select {|gem| gem.frozen? && !gem.built? }
+ if unbuilt_gems.size > 0
+ # don't print if the gems:build rake tasks are being run
+ unless $gems_build_rake_task
+ abort <<-end_error
+The following gems have native components that need to be built
+ #{unbuilt_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "}
+
+You're running:
+ ruby #{Gem.ruby_version} at #{Gem.ruby}
+ rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '}
+
+Run `rake gems:build` to build the unbuilt gems.
+ end_error
+ end
+ end
+ end
+
def check_gem_dependencies
unloaded_gems = @configuration.gems.reject { |g| g.loaded? }
if unloaded_gems.size > 0
@@ -404,10 +426,14 @@ Run `rake gems:install` to install the missing gems.
# should override this behaviour and set the relevant +default_charset+
# on ActionController::Base.
#
- # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby
- # shebang line if you don't want UTF-8.
+ # For Ruby 1.9, UTF-8 is the default internal and external encoding.
def initialize_encoding
- $KCODE='u' if RUBY_VERSION < '1.9'
+ if RUBY_VERSION < '1.9'
+ $KCODE='u'
+ else
+ Encoding.default_internal = Encoding::UTF_8
+ Encoding.default_external = Encoding::UTF_8
+ end
end
# This initialization routine does nothing unless <tt>:active_record</tt>
@@ -423,7 +449,8 @@ Run `rake gems:install` to install the missing gems.
def initialize_database_middleware
if configuration.frameworks.include?(:active_record)
- if ActionController::Base.session_store == ActiveRecord::SessionStore
+ if configuration.frameworks.include?(:action_controller) &&
+ ActionController::Base.session_store == ActiveRecord::SessionStore
configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement
configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache
else
@@ -439,7 +466,7 @@ Run `rake gems:install` to install the missing gems.
if RAILS_CACHE.respond_to?(:middleware)
# Insert middleware to setup and teardown local cache for each request
- configuration.middleware.insert_after(:"ActionController::Failsafe", RAILS_CACHE.middleware)
+ configuration.middleware.insert_after(:"Rack::Lock", RAILS_CACHE.middleware)
end
end
end
@@ -568,7 +595,7 @@ Run `rake gems:install` to install the missing gems.
Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths
configuration.middleware.insert_before(
- :"ActionDispatch::RewindableInput",
+ :"ActionDispatch::ParamsParser",
Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?)
end
@@ -609,7 +636,6 @@ Run `rake gems:install` to install the missing gems.
return unless configuration.frameworks.include?(:action_controller)
require 'dispatcher' unless defined?(::Dispatcher)
Dispatcher.define_dispatcher_callbacks(configuration.cache_classes)
- Dispatcher.run_prepare_callbacks
end
def disable_dependency_loading
@@ -865,7 +891,7 @@ Run `rake gems:install` to install the missing gems.
# Enable threaded mode. Allows concurrent requests to controller actions and
# multiple database connections. Also disables automatic dependency loading
- # after boot, and disables reloading code on every request, as these are
+ # after boot, and disables reloading code on every request, as these are
# fundamentally incompatible with thread safety.
def threadsafe!
self.preload_frameworks = true
@@ -1037,12 +1063,14 @@ Run `rake gems:install` to install the missing gems.
end
def default_plugin_locators
+ require 'rails/plugin/locator'
locators = []
locators << Plugin::GemLocator if defined? Gem
locators << Plugin::FileSystemLocator
end
def default_plugin_loader
+ require 'rails/plugin/loader'
Plugin::Loader
end
@@ -1106,3 +1134,4 @@ class Rails::OrderedOptions < Array #:nodoc:
return false
end
end
+
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index 923ed8b31d..1605429e8b 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -1,3 +1,6 @@
+require 'active_support/backtrace_cleaner'
+require 'rails/gem_dependency'
+
module Rails
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
ERB_METHOD_SIG = /:in `_run_erb_.*/
diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb
index 3062a77104..ee3d0d81ba 100644
--- a/railties/lib/rails/gem_dependency.rb
+++ b/railties/lib/rails/gem_dependency.rb
@@ -29,6 +29,15 @@ module Rails
end
end
+ def self.from_directory_name(directory_name)
+ directory_name_parts = File.basename(directory_name).split('-')
+ name = directory_name_parts[0..-2].join('-')
+ version = directory_name_parts.last
+ self.new(name, :version => version)
+ rescue ArgumentError => e
+ raise "Unable to determine gem name and version from '#{directory_name}'"
+ end
+
def initialize(name, options = {})
require 'rubygems' unless Object.const_defined?(:Gem)
@@ -101,8 +110,12 @@ module Rails
end
def built?
- # TODO: If Rubygems ever gives us a way to detect this, we should use it
- false
+ return false unless frozen?
+ specification.extensions.each do |ext|
+ makefile = File.join(unpacked_gem_directory, File.dirname(ext), 'Makefile')
+ return false unless File.exists?(makefile)
+ end
+ true
end
def framework_gem?
@@ -155,9 +168,9 @@ module Rails
specification && File.exists?(unpacked_gem_directory)
end
- def build
+ def build(options={})
require 'rails/gem_builder'
- unless built?
+ if options[:force] || !built?
return unless File.exists?(unpacked_specification_filename)
spec = YAML::load_file(unpacked_specification_filename)
Rails::GemBuilder.new(spec, unpacked_gem_directory).build_extensions
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 80deb73bbb..49ec5c7fba 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -35,10 +35,10 @@ module Rails
def load_paths
report_nonexistant_or_empty_plugin! unless valid?
- returning [] do |load_paths|
- load_paths << lib_path if has_lib_directory?
- load_paths << app_paths if has_app_directory?
- end.flatten
+ load_paths = []
+ load_paths << lib_path if has_lib_directory?
+ load_paths << app_paths if has_app_directory?
+ load_paths.flatten
end
# Evaluates a plugin's init.rb file.
@@ -139,6 +139,7 @@ module Rails
def evaluate_init_rb(initializer)
if has_init_file?
+ require 'active_support/core_ext/kernel/reporting'
silence_warnings do
# Allow plugins to reference the current configuration object
config = initializer.configuration
diff --git a/railties/lib/rails/plugin/loader.rb b/railties/lib/rails/plugin/loader.rb
index 66e01d70da..bc22dfc591 100644
--- a/railties/lib/rails/plugin/loader.rb
+++ b/railties/lib/rails/plugin/loader.rb
@@ -24,7 +24,7 @@ module Rails
# Returns the plugins that are in engine-form (have an app/ directory)
def engines
- @engines ||= plugins.select(&:engine?)
+ @engines ||= plugins.select {|plugin| plugin.engine? }
end
# Returns all the plugins that could be found by the current locators.
@@ -66,7 +66,7 @@ module Rails
end
def engine_metal_paths
- engines.collect(&:metal_path)
+ engines.collect {|engine| engine.metal_path }
end
protected
@@ -79,18 +79,18 @@ module Rails
end
def add_engine_routing_configurations
- engines.select(&:routed?).collect(&:routing_file).each do |routing_file|
+ engines.select {|engine| engine.routed? }.map {|engine| engine.routing_file }.each do |routing_file|
ActionController::Routing::Routes.add_configuration_file(routing_file)
end
end
def add_engine_controller_paths
- ActionController::Routing.controller_paths += engines.collect(&:controller_path)
+ ActionController::Routing.controller_paths += engines.collect {|engine| engine.controller_path }
end
def add_engine_view_paths
# reverse it such that the last engine can overwrite view paths from the first, like with routes
- paths = ActionView::PathSet.new(engines.collect(&:view_path).reverse)
+ paths = ActionView::PathSet.new(engines.collect {|engine| engine.view_path }.reverse)
ActionController::Base.view_paths.concat(paths)
ActionMailer::Base.view_paths.concat(paths) if configuration.frameworks.include?(:action_mailer)
end
@@ -170,7 +170,7 @@ module Rails
# so we load all in alphabetical order. If it is an empty array, we load no plugins, if it is
# non empty, we load the named plugins in the order specified.
def registered_plugin_names
- configuration.plugins ? configuration.plugins.map(&:to_s) : nil
+ configuration.plugins ? configuration.plugins.map {|plugin| plugin.to_s } : nil
end
def loaded?(plugin_name)
diff --git a/railties/lib/rails/plugin/locator.rb b/railties/lib/rails/plugin/locator.rb
index a6fc388a8e..1057c004e0 100644
--- a/railties/lib/rails/plugin/locator.rb
+++ b/railties/lib/rails/plugin/locator.rb
@@ -25,7 +25,7 @@ module Rails
end
def plugin_names
- plugins.map(&:name)
+ plugins.map {|plugin| plugin.name }
end
end
diff --git a/railties/lib/rails/rack/metal.rb b/railties/lib/rails/rack/metal.rb
index adc43da864..b031be29af 100644
--- a/railties/lib/rails/rack/metal.rb
+++ b/railties/lib/rails/rack/metal.rb
@@ -1,4 +1,6 @@
require 'active_support/ordered_hash'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/dependencies'
module Rails
module Rack
@@ -26,7 +28,7 @@ module Rails
load_list.map do |requested_metal|
if metal = all_metals[requested_metal]
- require metal
+ require_dependency metal
requested_metal.constantize
end
end.compact
diff --git a/railties/lib/rails_generator.rb b/railties/lib/rails_generator.rb
index 9f0ffc1562..85400932dd 100644
--- a/railties/lib/rails_generator.rb
+++ b/railties/lib/rails_generator.rb
@@ -21,16 +21,11 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-$:.unshift(File.dirname(__FILE__))
-$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
-
-begin
- require 'active_support'
-rescue LoadError
- require 'rubygems'
- gem 'activesupport'
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+require 'active_support/all'
+$:.unshift(File.dirname(__FILE__))
require 'rails_generator/base'
require 'rails_generator/lookup'
require 'rails_generator/commands'
diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
index 2c31d89538..c8c2239f34 100644
--- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb
+++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
@@ -125,6 +125,7 @@ class AppGenerator < Rails::Generator::Base
create_database_configuration_file(m)
create_routes_file(m)
create_locale_file(m)
+ create_seeds_file(m)
create_initializer_files(m)
create_environment_files(m)
end
@@ -176,6 +177,10 @@ class AppGenerator < Rails::Generator::Base
m.file "configs/routes.rb", "config/routes.rb"
end
+ def create_seeds_file(m)
+ m.file "configs/seeds.rb", "db/seeds.rb"
+ end
+
def create_initializer_files(m)
%w(
backtrace_silencers
diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb
index 3b49b1fa92..0e24d11950 100644
--- a/railties/lib/rails_generator/generators/applications/app/template_runner.rb
+++ b/railties/lib/rails_generator/generators/applications/app/template_runner.rb
@@ -227,7 +227,7 @@ module Rails
#
def generate(what, *args)
log 'generating', what
- argument = args.map(&:to_s).flatten.join(" ")
+ argument = args.map {|arg| arg.to_s }.flatten.join(" ")
in_root { run_ruby_script("script/generate #{what} #{argument}", false) }
end
diff --git a/railties/lib/rails_generator/generators/components/model/templates/model.rb b/railties/lib/rails_generator/generators/components/model/templates/model.rb
index 6fcf393bdf..0656b06dfe 100644
--- a/railties/lib/rails_generator/generators/components/model/templates/model.rb
+++ b/railties/lib/rails_generator/generators/components/model/templates/model.rb
@@ -1,5 +1,5 @@
class <%= class_name %> < ActiveRecord::Base
-<% attributes.select(&:reference?).each do |attribute| -%>
+<% attributes.select {|attr| attr.reference? }.each do |attribute| -%>
belongs_to :<%= attribute.name %>
<% end -%>
end
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/USAGE b/railties/lib/rails_generator/generators/components/model_subclass/USAGE
new file mode 100644
index 0000000000..a4b558a401
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/model_subclass/USAGE
@@ -0,0 +1,13 @@
+Description:
+ Create a model subclass of parent, used for Single Table Inheritance.
+
+ Both subclass and parent name can be either CamelCased or under_scored.
+
+ This generates a model class in app/models and a unit test in test/unit.
+
+Examples:
+ `./script/generate model_subclass admin user`
+
+ creates an Admin model, which will inheritate from User model, test:
+ Model: app/models/admin.rb
+ Test: test/unit/admin_test.rb
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb b/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb
new file mode 100644
index 0000000000..e8ac3da2cd
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb
@@ -0,0 +1,32 @@
+class ModelSubclassGenerator < Rails::Generator::NamedBase
+ default_options :skip_unit_test => false
+
+ def manifest
+ record do |m|
+ # Check for class naming collisions.
+ m.class_collisions class_name, "#{class_name}Test"
+
+ # Model and test directories.
+ m.directory File.join('app/models', class_path)
+ m.directory File.join('test/unit', class_path)
+
+ # Model class and unit test
+ m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb"), :assigns => assigns
+ m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb"), :assigns => assigns
+
+ end
+ end
+
+ protected
+ def banner
+ "Usage: #{$0} #{spec.name} Subclass Parent"
+ end
+
+ def assigns
+ {:parent_class_name => parent_class_name}
+ end
+
+ def parent_class_name
+ @args.first.try(:camelize) || usage
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb b/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb
new file mode 100644
index 0000000000..d0037b322b
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb
@@ -0,0 +1,3 @@
+class <%= class_name %> < <%= parent_class_name %>
+
+end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb b/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb
new file mode 100644
index 0000000000..3e0bc29d3a
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class <%= class_name %>Test < ActiveSupport::TestCase
+ # Replace this with your real tests.
+ test "the truth" do
+ assert true
+ end
+end
diff --git a/railties/lib/rails_generator/scripts.rb b/railties/lib/rails_generator/scripts.rb
index 9b1a99838a..3763b75dba 100644
--- a/railties/lib/rails_generator/scripts.rb
+++ b/railties/lib/rails_generator/scripts.rb
@@ -57,7 +57,7 @@ module Rails
usage << <<end_blurb
-More are available at http://wiki.rubyonrails.org/rails/pages/AvailableGenerators
+You can also install additional generators for your own use:
1. Download, for example, login_generator.zip
2. Unzip to directory #{Dir.user_home}/.rails/generators/login
to use the generator with all your Rails apps
diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake
index 9588fabb2d..cdab5d8bb0 100644
--- a/railties/lib/tasks/databases.rake
+++ b/railties/lib/tasks/databases.rake
@@ -156,8 +156,8 @@ namespace :db do
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
- desc 'Drops and recreates the database from db/schema.rb for the current environment.'
- task :reset => ['db:drop', 'db:create', 'db:schema:load']
+ desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
+ task :reset => [ 'db:drop', 'db:setup' ]
desc "Retrieves the charset for the current environment's database"
task :charset => :environment do
@@ -206,6 +206,15 @@ namespace :db do
end
end
+ desc 'Create the database, load the schema, and initialize with the seed data'
+ task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ]
+
+ desc 'Load the seed data from db/seeds.rb'
+ task :seed => :environment do
+ seed_file = File.join(Rails.root, 'db', 'seeds.rb')
+ load(seed_file) if File.exist?(seed_file)
+ end
+
namespace :fixtures do
desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task :load => :environment do
diff --git a/railties/lib/tasks/gems.rake b/railties/lib/tasks/gems.rake
index ed07bf2016..e496e1a04f 100644
--- a/railties/lib/tasks/gems.rake
+++ b/railties/lib/tasks/gems.rake
@@ -20,18 +20,25 @@ namespace :gems do
desc "Build any native extensions for unpacked gems"
task :build do
$gems_build_rake_task = true
- Rake::Task['gems:unpack'].invoke
- current_gems.each &:build
+ frozen_gems.each {|gem| gem.build }
+ end
+
+ namespace :build do
+ desc "Force the build of all gems"
+ task :force do
+ $gems_build_rake_task = true
+ frozen_gems.each { |gem| gem.build(:force => true) }
+ end
end
desc "Installs all required gems."
task :install => :base do
- current_gems.each &:install
+ current_gems.each {|gem| gem.install }
end
desc "Unpacks all required gems into vendor/gems."
task :unpack => :install do
- current_gems.each &:unpack
+ current_gems.each {|gem| gem.unpack }
end
namespace :unpack do
@@ -43,7 +50,7 @@ namespace :gems do
desc "Regenerate gem specifications in correct format."
task :refresh_specs => :base do
- current_gems.each &:refresh
+ current_gems.each {|gem| gem.refresh }
end
end
@@ -53,6 +60,12 @@ def current_gems
gems
end
+def frozen_gems
+ Dir[File.join(RAILS_ROOT, 'vendor', 'gems', '*-*')].map do |gem_dir|
+ Rails::GemDependency.from_directory_name(gem_dir)
+ end
+end
+
def print_gem_status(gem, indent=1)
code = case
when gem.framework_gem? then 'R'
diff --git a/railties/lib/tasks/misc.rake b/railties/lib/tasks/misc.rake
index 9e6f96db5b..fb2fc31dc1 100644
--- a/railties/lib/tasks/misc.rake
+++ b/railties/lib/tasks/misc.rake
@@ -12,10 +12,10 @@ end
desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.'
task :secret do
+ require 'active_support/secure_random'
puts ActiveSupport::SecureRandom.hex(64)
end
-require 'active_support'
namespace :time do
namespace :zones do
desc 'Displays names of all time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6'
@@ -30,6 +30,8 @@ namespace :time do
desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time'
task :local do
+ require 'active_support'
+ require 'active_support/time'
jan_offset = Time.now.beginning_of_year.utc_offset
jul_offset = Time.now.beginning_of_year.change(:month => 7).utc_offset
offset = jan_offset < jul_offset ? jan_offset : jul_offset
@@ -38,6 +40,8 @@ namespace :time do
# to find UTC -06:00 zones, OFFSET can be set to either -6, -6:00 or 21600
def build_time_zone_list(method, offset = ENV['OFFSET'])
+ require 'active_support'
+ require 'active_support/time'
if offset
offset = if offset.to_s.match(/(\+|-)?(\d+):(\d+)/)
sign = $1 == '-' ? -1 : 1
diff --git a/railties/test/boot_test.rb b/railties/test/boot_test.rb
index 08fcc82e6f..2dd68857c3 100644
--- a/railties/test/boot_test.rb
+++ b/railties/test/boot_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'initializer'
require "#{File.dirname(__FILE__)}/../environments/boot"
+require 'rails/gem_dependency'
class BootTest < Test::Unit::TestCase
def test_boot_returns_if_booted
diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb
index 189ad02b76..ff27af5572 100644
--- a/railties/test/gem_dependency_test.rb
+++ b/railties/test/gem_dependency_test.rb
@@ -1,4 +1,5 @@
require 'plugin_test_helper'
+require 'rails/gem_dependency'
class Rails::GemDependency
public :install_command, :unpack_command
@@ -18,7 +19,7 @@ class GemDependencyTest < Test::Unit::TestCase
def test_configuration_adds_gem_dependency
config = Rails::Configuration.new
config.gem "xaws-s3x", :lib => "aws/s3", :version => "0.4.0"
- assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect(&:install_command)
+ assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect { |g| g.install_command }
end
def test_gem_creates_install_command
@@ -144,4 +145,46 @@ class GemDependencyTest < Test::Unit::TestCase
end
end
+ def test_gem_ignores_development_dependencies
+ dummy_gem = Rails::GemDependency.new "dummy-gem-k"
+ dummy_gem.add_load_paths
+ dummy_gem.load
+ assert_equal 1, dummy_gem.dependencies.size
+ end
+
+ def test_gem_guards_against_duplicate_unpacks
+ dummy_gem = Rails::GemDependency.new "dummy-gem-a"
+ dummy_gem.stubs(:frozen?).returns(true)
+ dummy_gem.expects(:unpack_base).never
+ dummy_gem.unpack
+ end
+
+ def test_gem_does_not_unpack_framework_gems
+ dummy_gem = Rails::GemDependency.new "dummy-gem-a"
+ dummy_gem.stubs(:framework_gem?).returns(true)
+ dummy_gem.expects(:unpack_base).never
+ dummy_gem.unpack
+ end
+
+ def test_gem_from_directory_name
+ dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1')
+ assert_equal 'dummy-gem', dummy_gem.name
+ assert_equal '= 1.1', dummy_gem.version_requirements.to_s
+ end
+
+ def test_gem_from_invalid_directory_name
+ assert_raises RuntimeError do
+ dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem')
+ end
+ assert_raises RuntimeError do
+ dummy_gem = Rails::GemDependency.from_directory_name('dummy')
+ end
+ end
+
+ def test_gem_determines_build_status
+ assert_equal true, Rails::GemDependency.new("dummy-gem-a").built?
+ assert_equal true, Rails::GemDependency.new("dummy-gem-i").built?
+ assert_equal false, Rails::GemDependency.new("dummy-gem-j").built?
+ end
+
end
diff --git a/railties/test/generators/rails_model_subclass_generator_test.rb b/railties/test/generators/rails_model_subclass_generator_test.rb
new file mode 100644
index 0000000000..30066b5a3c
--- /dev/null
+++ b/railties/test/generators/rails_model_subclass_generator_test.rb
@@ -0,0 +1,15 @@
+require 'generators/generator_test_helper'
+
+class RailsModelSubclassGeneratorTest < GeneratorTestCase
+
+ def test_model_subclass_generates_resources
+ run_generator('model_subclass', %w(Car Product))
+
+ assert_generated_model_for :car, "Product"
+ assert_generated_unit_test_for :car
+ end
+
+ def test_model_subclass_must_have_a_parent_class_name
+ assert_raise(Rails::Generator::UsageError) { run_generator('model_subclass', %w(Car)) }
+ end
+end \ No newline at end of file
diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb
index d77a045e56..bdda319916 100644
--- a/railties/test/initializer_test.rb
+++ b/railties/test/initializer_test.rb
@@ -309,7 +309,7 @@ class InitializerSetupI18nTests < Test::Unit::TestCase
config.i18n.load_path << "my/other/locale.yml"
Rails::Initializer.run(:initialize_i18n, config)
- assert_equal [
+ assert_equal [
File.expand_path(File.dirname(__FILE__) + "/../../activesupport/lib/active_support/locale/en.yml"),
File.expand_path(File.dirname(__FILE__) + "/../../actionpack/lib/action_view/locale/en.yml"),
File.expand_path(File.dirname(__FILE__) + "/../../activerecord/lib/active_record/locale/en.yml"),
@@ -363,17 +363,31 @@ class InitializerDatabaseMiddlewareTest < Test::Unit::TestCase
ensure
ActionController::Base.session_store = store
end
+
+ def test_ensure_database_middleware_doesnt_use_action_controller_on_initializing
+ @config.frameworks -= [:action_controller]
+ store = ActionController::Base.session_store
+ ActionController::Base.session_store = ActiveRecord::SessionStore
+
+ @config.middleware.expects(:use).with(ActiveRecord::ConnectionAdapters::ConnectionManagement)
+ @config.middleware.expects(:use).with(ActiveRecord::QueryCache)
+
+ Rails::Initializer.run(:initialize_database_middleware, @config)
+ ensure
+ ActionController::Base.session_store = store
+ @config.frameworks += [:action_controller]
+ end
end
class InitializerViewPathsTest < Test::Unit::TestCase
def setup
@config = Rails::Configuration.new
@config.frameworks = [:action_view, :action_controller, :action_mailer]
-
+
ActionController::Base.stubs(:view_paths).returns(stub)
ActionMailer::Base.stubs(:view_paths).returns(stub)
end
-
+
def test_load_view_paths_doesnt_perform_anything_when_action_view_not_in_frameworks
@config.frameworks -= [:action_view]
ActionController::Base.view_paths.expects(:load!).never
@@ -390,4 +404,5 @@ class RailsRootTest < Test::Unit::TestCase
def test_rails_dot_root_should_be_a_pathname
assert_equal File.join(RAILS_ROOT, 'app', 'controllers'), Rails.root.join('app', 'controllers').to_s
end
-end \ No newline at end of file
+end
+
diff --git a/railties/test/plugin_loader_test.rb b/railties/test/plugin_loader_test.rb
index b270748dd6..c647d7b478 100644
--- a/railties/test/plugin_loader_test.rb
+++ b/railties/test/plugin_loader_test.rb
@@ -132,8 +132,8 @@ class TestPluginLoader < Test::Unit::TestCase
@loader.send :add_engine_view_paths
- assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionController::Base.view_paths
- assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionMailer::Base.view_paths
+ assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionController::Base.view_paths.map { |p| p.to_s }
+ assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionMailer::Base.view_paths.map { |p| p.to_s }
end
def test_should_add_plugin_load_paths_to_Dependencies_load_once_paths
diff --git a/railties/test/plugin_locator_test.rb b/railties/test/plugin_locator_test.rb
index 471d9fc7c3..855ac7d82f 100644
--- a/railties/test/plugin_locator_test.rb
+++ b/railties/test/plugin_locator_test.rb
@@ -42,15 +42,15 @@ class PluginFileSystemLocatorTest < Test::Unit::TestCase
end
def test_should_return_all_plugins_found_under_the_set_plugin_paths
- assert_equal ["a", "acts_as_chunky_bacon", "engine", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map(&:name).sort
+ assert_equal ["a", "acts_as_chunky_bacon", "engine", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map { |p| p.name }.sort
end
def test_should_find_plugins_only_under_the_plugin_paths_set_in_configuration
@configuration.plugin_paths = [File.join(plugin_fixture_root_path, "default")]
- assert_equal ["acts_as_chunky_bacon", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map(&:name).sort
+ assert_equal ["acts_as_chunky_bacon", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map { |p| p.name }.sort
@configuration.plugin_paths = [File.join(plugin_fixture_root_path, "alternate")]
- assert_equal ["a"], @locator.plugins.map(&:name)
+ assert_equal ["a"], @locator.plugins.map { |p| p.name }
end
def test_should_not_raise_any_error_and_return_no_plugins_if_the_plugin_path_value_does_not_exist
diff --git a/railties/test/plugin_test_helper.rb b/railties/test/plugin_test_helper.rb
index f8c094d19f..55d1a1fa96 100644
--- a/railties/test/plugin_test_helper.rb
+++ b/railties/test/plugin_test_helper.rb
@@ -12,7 +12,7 @@ RAILS_ROOT = '.' unless defined?(RAILS_ROOT)
class Test::Unit::TestCase
private
def plugin_fixture_root_path
- File.join(File.dirname(__FILE__), 'fixtures', 'plugins')
+ File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'plugins'))
end
def only_load_the_following_plugins!(plugins)
@@ -24,6 +24,6 @@ class Test::Unit::TestCase
end
def assert_plugins(list_of_names, array_of_plugins, message=nil)
- assert_equal list_of_names.map(&:to_s), array_of_plugins.map(&:name), message
+ assert_equal list_of_names.map { |n| n.to_s }, array_of_plugins.map { |p| p.name }, message
end
-end \ No newline at end of file
+end
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index 971cba89d0..fdcc7a1ef6 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -14,6 +14,8 @@ unless defined?(Rails) && defined?(Rails::Info)
end
end
+require "active_support/core_ext/kernel/reporting"
+
class InfoTest < ActiveSupport::TestCase
def setup
Rails.send :remove_const, :Info
diff --git a/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification
new file mode 100644
index 0000000000..b3f7930948
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification
@@ -0,0 +1,29 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-h
+version: !ruby/object:Gem::Version
+ version: 1.3.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+dependencies:
+files:
+- lib
+- lib/dummy-gem-h.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem H
diff --git a/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb b/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb
new file mode 100644
index 0000000000..0f91234936
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb
@@ -0,0 +1 @@
+DUMMY_GEM_H_VERSION="1.0.0"
diff --git a/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification
new file mode 100644
index 0000000000..50b4969da5
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification
@@ -0,0 +1,41 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-i
+version: !ruby/object:Gem::Version
+ version: 1.3.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: dummy-gem-i
+ type: :runtime
+ version_requirement:
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 1.0.0
+ version:
+extensions:
+- ext/dummy-gem-i/extconf.rb
+files:
+- lib
+- lib/dummy-gem-i.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem G
diff --git a/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile b/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile
diff --git a/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb b/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb
new file mode 100644
index 0000000000..2f9a376c2c
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb
@@ -0,0 +1 @@
+DUMMY_GEM_I_VERSION="1.0.0"
diff --git a/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification
new file mode 100644
index 0000000000..2c456546fc
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification
@@ -0,0 +1,41 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-j
+version: !ruby/object:Gem::Version
+ version: 1.3.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: dummy-gem-j
+ type: :runtime
+ version_requirement:
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 1.0.0
+ version:
+extensions:
+- ext/dummy-gem-j/extconf.rb
+files:
+- lib
+- lib/dummy-gem-j.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem G
diff --git a/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb b/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb
new file mode 100644
index 0000000000..8ecd363ff8
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb
@@ -0,0 +1 @@
+DUMMY_GEM_J_VERSION="1.0.0"
diff --git a/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification
new file mode 100644
index 0000000000..20edd0f856
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification
@@ -0,0 +1,49 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-k
+version: !ruby/object:Gem::Version
+ version: 1.3.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: dummy-gem-k
+ type: :runtime
+ version_requirement:
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 1.0.0
+ version:
+- !ruby/object:Gem::Dependency
+ name: dummy-gem-h
+ type: :development
+ version_requirement:
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 1.0.0
+ version:
+files:
+- lib
+- lib/dummy-gem-k.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem I
diff --git a/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb b/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb
new file mode 100644
index 0000000000..97fb1d69ce
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb
@@ -0,0 +1 @@
+DUMMY_GEM_K_VERSION="1.0.0"
diff --git a/tools/profile b/tools/profile
new file mode 100755
index 0000000000..927467bc4e
--- /dev/null
+++ b/tools/profile
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+# Example:
+# tools/profile_requires activesupport/lib/active_support.rb
+# tools/profile_requires activeresource/examples/simple.rb
+abort 'Use REE so you can profile memory and object allocation' unless GC.respond_to?(:enable_stats)
+
+ENV['NO_RELOAD'] ||= '1'
+ENV['RAILS_ENV'] ||= 'development'
+
+GC.enable_stats
+require 'rubygems'
+Gem.source_index
+require 'benchmark'
+
+module RequireProfiler
+ def require(file, *args) RequireProfiler.profile(file) { super } end
+ def load(file, *args) RequireProfiler.profile(file) { super } end
+
+ @depth, @stats = 0, []
+ class << self
+ attr_accessor :depth
+ attr_accessor :stats
+
+ def profile(file)
+ stats << [file, depth]
+ self.depth += 1
+ heap_before, objects_before = GC.allocated_size, ObjectSpace.allocated_objects
+ result = nil
+ elapsed = Benchmark.realtime { result = yield }
+ heap_after, objects_after = GC.allocated_size, ObjectSpace.allocated_objects
+ self.depth -= 1
+ stats.pop if stats.last.first == file
+ stats << [file, depth, elapsed, heap_after - heap_before, objects_after - objects_before] if result
+ result
+ end
+ end
+end
+
+GC.start
+before = GC.allocated_size
+before_rss = `ps -o rss= -p #{Process.pid}`.to_i
+before_live_objects = ObjectSpace.live_objects
+
+path = ARGV.shift
+if mode = ARGV.shift
+ require 'ruby-prof'
+ RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
+ RubyProf.start
+else
+ Object.instance_eval { include RequireProfiler }
+end
+
+elapsed = Benchmark.realtime { require path }
+results = RubyProf.stop if mode
+
+GC.start
+after_live_objects = ObjectSpace.live_objects
+after_rss = `ps -o rss= -p #{Process.pid}`.to_i
+after = GC.allocated_size
+usage = (after - before) / 1024.0
+
+if mode
+ File.open("#{File.basename(path, '.rb')}.#{mode}.callgrind", 'w') do |out|
+ RubyProf::CallTreePrinter.new(results).print(out)
+ end
+end
+
+RequireProfiler.stats.each do |file, depth, sec, bytes, objects|
+ if sec
+ puts "%10.2f KB %10d obj %8.1f ms %s%s" % [bytes / 1024.0, objects, sec * 1000, ' ' * depth, file]
+ else
+ puts "#{' ' * (42 + depth)}#{file}"
+ end
+end
+puts "%10.2f KB %10d obj %8.1f ms %d KB RSS" % [usage, after_live_objects - before_live_objects, elapsed * 1000, after_rss - before_rss]