aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml10
-rw-r--r--Gemfile84
-rw-r--r--RAILS_VERSION2
-rw-r--r--README.rdoc7
-rw-r--r--RELEASING_RAILS.rdoc44
-rwxr-xr-xRakefile25
-rw-r--r--actionmailer/CHANGELOG449
-rw-r--r--actionmailer/CHANGELOG.md449
-rw-r--r--actionmailer/MIT-LICENSE2
-rw-r--r--actionmailer/README.rdoc10
-rwxr-xr-xactionmailer/Rakefile1
-rw-r--r--actionmailer/actionmailer.gemspec6
-rw-r--r--actionmailer/lib/action_mailer.rb3
-rw-r--r--actionmailer/lib/action_mailer/base.rb30
-rw-r--r--actionmailer/lib/action_mailer/log_subscriber.rb6
-rw-r--r--actionmailer/lib/action_mailer/mail_helper.rb6
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb5
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb17
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb2
-rw-r--r--actionmailer/lib/action_mailer/version.rb4
-rw-r--r--actionmailer/lib/rails/generators/mailer/USAGE2
-rw-r--r--actionmailer/test/abstract_unit.rb15
-rw-r--r--actionmailer/test/asset_host_test.rb2
-rw-r--r--actionmailer/test/base_test.rb4
-rw-r--r--actionmailer/test/mail_helper_test.rb44
-rw-r--r--actionmailer/test/mail_layout_test.rb2
-rw-r--r--actionpack/CHANGELOG5689
-rw-r--r--actionpack/CHANGELOG.md5913
-rw-r--r--actionpack/MIT-LICENSE2
-rw-r--r--actionpack/README.rdoc6
-rwxr-xr-xactionpack/Rakefile18
-rw-r--r--actionpack/actionpack.gemspec11
-rw-r--r--actionpack/examples/performance.rb185
-rw-r--r--actionpack/examples/views/_collection.erb3
-rw-r--r--actionpack/examples/views/_hello.erb1
-rw-r--r--actionpack/examples/views/_hundred_partials.erb3
-rw-r--r--actionpack/examples/views/_partial.erb10
-rw-r--r--actionpack/examples/views/_ten_partials.erb10
-rw-r--r--actionpack/examples/views/hashes/_hash.erb3
-rw-r--r--actionpack/examples/views/my_hashes/_my_hash.erb3
-rw-r--r--actionpack/examples/views/template.html.erb1
-rw-r--r--actionpack/lib/abstract_controller/asset_paths.rb2
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb14
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb167
-rw-r--r--actionpack/lib/abstract_controller/logger.rb3
-rw-r--r--actionpack/lib/abstract_controller/railties/routes_helpers.rb4
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb28
-rw-r--r--actionpack/lib/abstract_controller/view_paths.rb4
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb13
-rw-r--r--actionpack/lib/action_controller/caching.rb1
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb20
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb51
-rw-r--r--actionpack/lib/action_controller/deprecated.rb6
-rw-r--r--actionpack/lib/action_controller/deprecated/integration_test.rb3
-rw-r--r--actionpack/lib/action_controller/deprecated/performance_test.rb2
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb7
-rw-r--r--actionpack/lib/action_controller/metal.rb10
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb58
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb61
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb3
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb2
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb2
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb5
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb25
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb2
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb7
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb44
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb18
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb5
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb20
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb8
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rescue.rb13
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb11
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb5
-rw-r--r--actionpack/lib/action_controller/railtie.rb8
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb7
-rw-r--r--actionpack/lib/action_controller/test_case.rb120
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb2
-rw-r--r--actionpack/lib/action_dispatch.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb42
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/rack_cache.rb5
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb47
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb35
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/closed_error.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb82
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb78
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb54
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/public_exceptions.rb30
-rw-r--r--actionpack/lib/action_dispatch/middleware/reloader.rb41
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb104
-rw-r--r--actionpack/lib/action_dispatch/middleware/request_id.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/rescue.rb26
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb173
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb21
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb19
-rw-r--r--actionpack/lib/action_dispatch/routing.rb10
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb166
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb10
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb121
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb127
-rw-r--r--actionpack/lib/action_dispatch/routing/routes_proxy.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb32
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/dom.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb28
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb10
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb23
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb12
-rw-r--r--actionpack/lib/action_pack.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb4
-rw-r--r--actionpack/lib/action_view.rb3
-rw-r--r--actionpack/lib/action_view/asset_paths.rb8
-rw-r--r--actionpack/lib/action_view/base.rb18
-rw-r--r--actionpack/lib/action_view/buffers.rb2
-rw-r--r--actionpack/lib/action_view/flows.rb5
-rw-r--r--actionpack/lib/action_view/helpers.rb5
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/asset_paths.rb7
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb115
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb11
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb10
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/benchmark_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb39
-rw-r--r--actionpack/lib/action_view/helpers/csrf_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb134
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb405
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb253
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb45
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb213
-rw-r--r--actionpack/lib/action_view/helpers/output_safety_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb25
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb1
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb51
-rw-r--r--actionpack/lib/action_view/helpers/tags.rb33
-rw-r--r--actionpack/lib/action_view/helpers/tags/base.rb147
-rw-r--r--actionpack/lib/action_view/helpers/tags/check_box.rb58
-rw-r--r--actionpack/lib/action_view/helpers/tags/checkable.rb16
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb37
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_helpers.rb81
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb30
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_select.rb28
-rw-r--r--actionpack/lib/action_view/helpers/tags/date_field.rb15
-rw-r--r--actionpack/lib/action_view/helpers/tags/date_select.rb70
-rw-r--r--actionpack/lib/action_view/helpers/tags/datetime_select.rb8
-rw-r--r--actionpack/lib/action_view/helpers/tags/email_field.rb8
-rw-r--r--actionpack/lib/action_view/helpers/tags/file_field.rb12
-rw-r--r--actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb24
-rw-r--r--actionpack/lib/action_view/helpers/tags/hidden_field.rb12
-rw-r--r--actionpack/lib/action_view/helpers/tags/label.rb65
-rw-r--r--actionpack/lib/action_view/helpers/tags/number_field.rb19
-rw-r--r--actionpack/lib/action_view/helpers/tags/password_field.rb12
-rw-r--r--actionpack/lib/action_view/helpers/tags/radio_button.rb31
-rw-r--r--actionpack/lib/action_view/helpers/tags/range_field.rb8
-rw-r--r--actionpack/lib/action_view/helpers/tags/search_field.rb24
-rw-r--r--actionpack/lib/action_view/helpers/tags/select.rb41
-rw-r--r--actionpack/lib/action_view/helpers/tags/tel_field.rb8
-rw-r--r--actionpack/lib/action_view/helpers/tags/text_area.rb20
-rw-r--r--actionpack/lib/action_view/helpers/tags/text_field.rb30
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_select.rb8
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_zone_select.rb20
-rw-r--r--actionpack/lib/action_view/helpers/tags/url_field.rb8
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb15
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb84
-rw-r--r--actionpack/lib/action_view/locale/en.yml4
-rw-r--r--actionpack/lib/action_view/log_subscriber.rb3
-rw-r--r--actionpack/lib/action_view/lookup_context.rb43
-rw-r--r--actionpack/lib/action_view/railtie.rb4
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb16
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb42
-rw-r--r--actionpack/lib/action_view/renderer/streaming_template_renderer.rb5
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb32
-rw-r--r--actionpack/lib/action_view/template.rb124
-rw-r--r--actionpack/lib/action_view/template/error.rb7
-rw-r--r--actionpack/lib/action_view/template/handlers.rb13
-rw-r--r--actionpack/lib/action_view/template/handlers/builder.rb11
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb26
-rw-r--r--actionpack/lib/action_view/template/resolver.rb1
-rw-r--r--actionpack/lib/action_view/test_case.rb58
-rw-r--r--actionpack/lib/sprockets/assets.rake22
-rw-r--r--actionpack/lib/sprockets/compressors.rb42
-rw-r--r--actionpack/lib/sprockets/helpers/rails_helper.rb27
-rw-r--r--actionpack/lib/sprockets/railtie.rb7
-rw-r--r--actionpack/lib/sprockets/static_compiler.rb7
-rw-r--r--actionpack/test/abstract/layouts_test.rb61
-rw-r--r--actionpack/test/abstract/translation_test.rb4
-rw-r--r--actionpack/test/abstract_unit.rb67
-rw-r--r--actionpack/test/activerecord/active_record_store_test.rb10
-rw-r--r--actionpack/test/activerecord/polymorphic_routes_test.rb22
-rw-r--r--actionpack/test/assertions/response_assertions_test.rb55
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb33
-rw-r--r--actionpack/test/controller/addresses_render_test.rb35
-rw-r--r--actionpack/test/controller/assert_select_test.rb4
-rw-r--r--actionpack/test/controller/base_test.rb77
-rw-r--r--actionpack/test/controller/caching_test.rb93
-rw-r--r--actionpack/test/controller/capture_test.rb6
-rw-r--r--actionpack/test/controller/content_type_test.rb12
-rw-r--r--actionpack/test/controller/default_url_options_with_filter_test.rb2
-rw-r--r--actionpack/test/controller/filters_test.rb16
-rw-r--r--actionpack/test/controller/flash_hash_test.rb81
-rw-r--r--actionpack/test/controller/flash_test.rb44
-rw-r--r--actionpack/test/controller/force_ssl_test.rb41
-rw-r--r--actionpack/test/controller/helper_test.rb7
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb2
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb45
-rw-r--r--actionpack/test/controller/integration_test.rb37
-rw-r--r--actionpack/test/controller/layout_test.rb2
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb13
-rw-r--r--actionpack/test/controller/mime_responds_test.rb31
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb6
-rw-r--r--actionpack/test/controller/new_base/render_layout_test.rb36
-rw-r--r--actionpack/test/controller/new_base/render_streaming_test.rb4
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb6
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb62
-rw-r--r--actionpack/test/controller/record_identifier_test.rb2
-rw-r--r--actionpack/test/controller/redirect_test.rb12
-rw-r--r--actionpack/test/controller/render_json_test.rb3
-rw-r--r--actionpack/test/controller/render_test.rb108
-rw-r--r--actionpack/test/controller/render_xml_test.rb2
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb9
-rw-r--r--actionpack/test/controller/rescue_test.rb21
-rw-r--r--actionpack/test/controller/resources_test.rb1
-rw-r--r--actionpack/test/controller/routing_test.rb223
-rw-r--r--actionpack/test/controller/runner_test.rb2
-rw-r--r--actionpack/test/controller/selector_test.rb2
-rw-r--r--actionpack/test/controller/send_file_test.rb2
-rw-r--r--actionpack/test/controller/show_exceptions_test.rb71
-rw-r--r--actionpack/test/controller/test_case_test.rb (renamed from actionpack/test/controller/test_test.rb)82
-rw-r--r--actionpack/test/controller/url_for_integration_test.rb6
-rw-r--r--actionpack/test/controller/url_for_test.rb19
-rw-r--r--actionpack/test/controller/view_paths_test.rb16
-rw-r--r--actionpack/test/controller/webservice_test.rb2
-rw-r--r--actionpack/test/dispatch/callbacks_test.rb11
-rw-r--r--actionpack/test/dispatch/cookies_test.rb113
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb157
-rw-r--r--actionpack/test/dispatch/middleware_stack_test.rb6
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb37
-rw-r--r--actionpack/test/dispatch/mount_test.rb2
-rw-r--r--actionpack/test/dispatch/prefix_generation_test.rb7
-rw-r--r--actionpack/test/dispatch/reloader_test.rb37
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb18
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb2
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb2
-rw-r--r--actionpack/test/dispatch/request/xml_params_parsing_test.rb26
-rw-r--r--actionpack/test/dispatch/request_id_test.rb2
-rw-r--r--actionpack/test/dispatch/request_test.rb56
-rw-r--r--actionpack/test/dispatch/response_test.rb17
-rw-r--r--actionpack/test/dispatch/routing_test.rb2241
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/mem_cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb131
-rw-r--r--actionpack/test/dispatch/static_test.rb35
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb8
-rw-r--r--actionpack/test/fixtures/addresses/list.erb1
-rw-r--r--actionpack/test/fixtures/company.rb4
-rw-r--r--actionpack/test/fixtures/developer.rb2
-rw-r--r--actionpack/test/fixtures/developers.yml2
-rw-r--r--actionpack/test/fixtures/public/foo/foo!bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo$bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo&bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo'bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo(bar).html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo*bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo+bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo,bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo:bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo;bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo=bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/foo@bar.html1
-rw-r--r--actionpack/test/fixtures/public/foo/こんにちは.html1
-rw-r--r--actionpack/test/fixtures/sprockets/app/fonts/dir/font.ttf0
-rw-r--r--actionpack/test/fixtures/sprockets/app/fonts/font.ttf0
-rw-r--r--actionpack/test/fixtures/test/_content_tag_nested_in_content_tag.erb3
-rw-r--r--actionpack/test/fixtures/test/_label_with_block.erb4
-rw-r--r--actionpack/test/lib/controller/fake_controllers.rb31
-rw-r--r--actionpack/test/lib/controller/fake_models.rb14
-rw-r--r--actionpack/test/lib/testing_sandbox.rb15
-rw-r--r--actionpack/test/template/active_model_helper_test.rb25
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb185
-rw-r--r--actionpack/test/template/atom_feed_helper_test.rb7
-rw-r--r--actionpack/test/template/benchmark_helper_test.rb24
-rw-r--r--actionpack/test/template/capture_helper_test.rb85
-rw-r--r--actionpack/test/template/compiled_templates_test.rb6
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb16
-rw-r--r--actionpack/test/template/date_helper_test.rb74
-rw-r--r--actionpack/test/template/erb_util_test.rb16
-rw-r--r--actionpack/test/template/form_collections_helper_test.rb327
-rw-r--r--actionpack/test/template/form_helper_test.rb247
-rw-r--r--actionpack/test/template/form_options_helper_test.rb86
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb27
-rw-r--r--actionpack/test/template/html-scanner/cdata_node_test.rb2
-rw-r--r--actionpack/test/template/html-scanner/document_test.rb2
-rw-r--r--actionpack/test/template/html-scanner/node_test.rb2
-rw-r--r--actionpack/test/template/html-scanner/sanitizer_test.rb11
-rw-r--r--actionpack/test/template/html-scanner/tag_node_test.rb2
-rw-r--r--actionpack/test/template/html-scanner/text_node_test.rb2
-rw-r--r--actionpack/test/template/html-scanner/tokenizer_test.rb2
-rw-r--r--actionpack/test/template/javascript_helper_test.rb7
-rw-r--r--actionpack/test/template/log_subscriber_test.rb4
-rw-r--r--actionpack/test/template/lookup_context_test.rb8
-rw-r--r--actionpack/test/template/number_helper_i18n_test.rb7
-rw-r--r--actionpack/test/template/number_helper_test.rb26
-rw-r--r--actionpack/test/template/output_buffer_test.rb16
-rw-r--r--actionpack/test/template/output_safety_helper_test.rb2
-rw-r--r--actionpack/test/template/record_tag_helper_test.rb85
-rw-r--r--actionpack/test/template/render_test.rb122
-rw-r--r--actionpack/test/template/sanitize_helper_test.rb2
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb37
-rw-r--r--actionpack/test/template/sprockets_helper_with_routes_test.rb57
-rw-r--r--actionpack/test/template/streaming_render_test.rb2
-rw-r--r--actionpack/test/template/tag_helper_test.rb10
-rw-r--r--actionpack/test/template/template_error_test.rb4
-rw-r--r--actionpack/test/template/template_test.rb102
-rw-r--r--actionpack/test/template/test_case_test.rb2
-rw-r--r--actionpack/test/template/text_helper_test.rb69
-rw-r--r--actionpack/test/template/translation_helper_test.rb18
-rw-r--r--actionpack/test/template/url_helper_test.rb54
-rw-r--r--actionpack/test/ts_isolated.rb6
-rw-r--r--activemodel/CHANGELOG109
-rw-r--r--activemodel/CHANGELOG.md118
-rw-r--r--activemodel/MIT-LICENSE2
-rw-r--r--activemodel/README.rdoc21
-rwxr-xr-xactivemodel/Rakefile1
-rw-r--r--activemodel/activemodel.gemspec5
-rw-r--r--activemodel/lib/active_model.rb3
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb124
-rw-r--r--activemodel/lib/active_model/callbacks.rb8
-rw-r--r--activemodel/lib/active_model/configuration.rb134
-rw-r--r--activemodel/lib/active_model/conversion.rb6
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/errors.rb27
-rw-r--r--activemodel/lib/active_model/lint.rb40
-rw-r--r--activemodel/lib/active_model/locale/en.yml1
-rw-r--r--activemodel/lib/active_model/mass_assignment_security.rb48
-rw-r--r--activemodel/lib/active_model/mass_assignment_security/permission_set.rb2
-rw-r--r--activemodel/lib/active_model/mass_assignment_security/sanitizer.rb24
-rw-r--r--activemodel/lib/active_model/naming.rb54
-rw-r--r--activemodel/lib/active_model/observing.rb8
-rw-r--r--activemodel/lib/active_model/secure_password.rb2
-rw-r--r--activemodel/lib/active_model/serialization.rb19
-rw-r--r--activemodel/lib/active_model/serializers/json.rb3
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb5
-rw-r--r--activemodel/lib/active_model/test_case.rb12
-rw-r--r--activemodel/lib/active_model/translation.rb18
-rw-r--r--activemodel/lib/active_model/validations.rb10
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb7
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb2
-rw-r--r--activemodel/lib/active_model/validations/length.rb27
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb5
-rw-r--r--activemodel/lib/active_model/validations/presence.rb6
-rw-r--r--activemodel/lib/active_model/validations/validates.rb23
-rw-r--r--activemodel/lib/active_model/validations/with.rb2
-rw-r--r--activemodel/lib/active_model/validator.rb3
-rw-r--r--activemodel/lib/active_model/version.rb4
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb26
-rw-r--r--activemodel/test/cases/configuration_test.rb154
-rw-r--r--activemodel/test/cases/errors_test.rb19
-rw-r--r--activemodel/test/cases/helper.rb2
-rw-r--r--activemodel/test/cases/lint_test.rb6
-rw-r--r--activemodel/test/cases/mass_assignment_security/sanitizer_test.rb4
-rw-r--r--activemodel/test/cases/mass_assignment_security_test.rb7
-rw-r--r--activemodel/test/cases/naming_test.rb77
-rw-r--r--activemodel/test/cases/serialization_test.rb89
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb8
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb2
-rw-r--r--activemodel/test/cases/translation_test.rb10
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb7
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb12
-rw-r--r--activemodel/test/cases/validations/format_validation_test.rb24
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb12
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb102
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb7
-rw-r--r--activemodel/test/cases/validations/validates_test.rb6
-rw-r--r--activemodel/test/cases/validations_test.rb22
-rw-r--r--activemodel/test/models/blog_post.rb8
-rw-r--r--activerecord/CHANGELOG6572
-rw-r--r--activerecord/CHANGELOG.md6874
-rw-r--r--activerecord/MIT-LICENSE2
-rw-r--r--activerecord/README.rdoc10
-rwxr-xr-xactiverecord/Rakefile18
-rw-r--r--activerecord/activerecord.gemspec7
-rw-r--r--activerecord/examples/performance.rb23
-rw-r--r--activerecord/lib/active_record.rb34
-rw-r--r--activerecord/lib/active_record/associations.rb33
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb9
-rw-r--r--activerecord/lib/active_record/associations/association.rb5
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb27
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb43
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb12
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb21
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb21
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb40
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb17
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb77
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb4
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb7
-rw-r--r--activerecord/lib/active_record/associations/join_helper.rb10
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb14
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb221
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb202
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb14
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb103
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb7
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb177
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb115
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb94
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb32
-rw-r--r--activerecord/lib/active_record/autosave_association.rb28
-rw-r--r--activerecord/lib/active_record/base.rb1846
-rw-r--r--activerecord/lib/active_record/callbacks.rb53
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb366
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb161
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb64
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb147
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb103
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb69
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb83
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb90
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb96
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb243
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb505
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb50
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb90
-rw-r--r--activerecord/lib/active_record/connection_handling.rb96
-rw-r--r--activerecord/lib/active_record/core.rb361
-rw-r--r--activerecord/lib/active_record/counter_cache.rb11
-rw-r--r--activerecord/lib/active_record/dynamic_finder_match.rb82
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb79
-rw-r--r--activerecord/lib/active_record/dynamic_scope_match.rb4
-rw-r--r--activerecord/lib/active_record/explain.rb82
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb24
-rw-r--r--activerecord/lib/active_record/fixtures.rb109
-rw-r--r--activerecord/lib/active_record/fixtures/file.rb5
-rw-r--r--activerecord/lib/active_record/identity_map.rb35
-rw-r--r--activerecord/lib/active_record/inheritance.rb184
-rw-r--r--activerecord/lib/active_record/integration.rb49
-rw-r--r--activerecord/lib/active_record/locale/en.yml3
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb54
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb22
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb6
-rw-r--r--activerecord/lib/active_record/migration.rb274
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb14
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb17
-rw-r--r--activerecord/lib/active_record/model.rb109
-rw-r--r--activerecord/lib/active_record/model_schema.rb334
-rw-r--r--activerecord/lib/active_record/named_scope.rb200
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb4
-rw-r--r--activerecord/lib/active_record/null_relation.rb10
-rw-r--r--activerecord/lib/active_record/persistence.rb67
-rw-r--r--activerecord/lib/active_record/query_cache.rb45
-rw-r--r--activerecord/lib/active_record/querying.rb68
-rw-r--r--activerecord/lib/active_record/railtie.rb34
-rw-r--r--activerecord/lib/active_record/railties/databases.rake186
-rw-r--r--activerecord/lib/active_record/railties/jdbcmysql_error.rb2
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb26
-rw-r--r--activerecord/lib/active_record/reflection.rb19
-rw-r--r--activerecord/lib/active_record/relation.rb94
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb33
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb49
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb6
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb81
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb116
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb17
-rw-r--r--activerecord/lib/active_record/result.rb34
-rw-r--r--activerecord/lib/active_record/sanitization.rb194
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb15
-rw-r--r--activerecord/lib/active_record/schema_migration.rb34
-rw-r--r--activerecord/lib/active_record/scoping.rb152
-rw-r--r--activerecord/lib/active_record/scoping/default.rb142
-rw-r--r--activerecord/lib/active_record/scoping/named.rb202
-rw-r--r--activerecord/lib/active_record/serialization.rb4
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb6
-rw-r--r--activerecord/lib/active_record/session_store.rb14
-rw-r--r--activerecord/lib/active_record/store.rb12
-rw-r--r--activerecord/lib/active_record/test_case.rb75
-rw-r--r--activerecord/lib/active_record/timestamp.rb2
-rw-r--r--activerecord/lib/active_record/transactions.rb14
-rw-r--r--activerecord/lib/active_record/translation.rb22
-rw-r--r--activerecord/lib/active_record/validations/associated.rb5
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb26
-rw-r--r--activerecord/lib/active_record/version.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb12
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb6
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/migration.rb8
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb9
-rw-r--r--activerecord/test/assets/test.txt1
-rw-r--r--activerecord/test/cases/adapter_test.rb255
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb16
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb16
-rw-r--r--activerecord/test/cases/adapters/mysql/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/explain_test.rb26
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb15
-rw-r--r--activerecord/test/cases/adapters/postgresql/explain_test.rb28
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb149
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb36
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/view_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/explain_test.rb26
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb4
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb5
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb6
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb157
-rw-r--r--activerecord/test/cases/associations/extension_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb53
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb114
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb40
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb67
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb2
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb4
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb13
-rw-r--r--activerecord/test/cases/associations_test.rb23
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb16
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb132
-rw-r--r--activerecord/test/cases/autosave_association_test.rb21
-rw-r--r--activerecord/test/cases/base_test.rb321
-rw-r--r--activerecord/test/cases/binary_test.rb8
-rw-r--r--activerecord/test/cases/calculations_test.rb48
-rw-r--r--activerecord/test/cases/callbacks_test.rb14
-rw-r--r--activerecord/test/cases/column_definition_test.rb9
-rw-r--r--activerecord/test/cases/column_test.rb29
-rw-r--r--activerecord/test/cases/connection_adapters/abstract_adapter_test.rb54
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb5
-rw-r--r--activerecord/test/cases/connection_adapters/connection_specification_test.rb12
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb44
-rw-r--r--activerecord/test/cases/connection_management_test.rb53
-rw-r--r--activerecord/test/cases/connection_pool_test.rb123
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb41
-rw-r--r--activerecord/test/cases/counter_cache_test.rb20
-rw-r--r--activerecord/test/cases/dirty_test.rb14
-rw-r--r--activerecord/test/cases/dup_test.rb8
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb48
-rw-r--r--activerecord/test/cases/explain_test.rb115
-rw-r--r--activerecord/test/cases/finder_test.rb31
-rw-r--r--activerecord/test/cases/fixtures_test.rb111
-rw-r--r--activerecord/test/cases/helper.rb38
-rw-r--r--activerecord/test/cases/identity_map/middleware_test.rb3
-rw-r--r--activerecord/test/cases/inclusion_test.rb113
-rw-r--r--activerecord/test/cases/inheritance_test.rb5
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb12
-rw-r--r--activerecord/test/cases/locking_test.rb55
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb3
-rw-r--r--activerecord/test/cases/mass_assignment_security_test.rb63
-rw-r--r--activerecord/test/cases/method_scoping_test.rb12
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb328
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb188
-rw-r--r--activerecord/test/cases/migration/column_positioning_test.rb60
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb18
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb70
-rw-r--r--activerecord/test/cases/migration/helper.rb64
-rw-r--r--activerecord/test/cases/migration/index_test.rb185
-rw-r--r--activerecord/test/cases/migration/logger_test.rb37
-rw-r--r--activerecord/test/cases/migration/rename_column_test.rb194
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb72
-rw-r--r--activerecord/test/cases/migration/table_and_index_test.rb24
-rw-r--r--activerecord/test/cases/migration_test.rb2717
-rw-r--r--activerecord/test/cases/migrator_test.rb377
-rw-r--r--activerecord/test/cases/modules_test.rb2
-rw-r--r--activerecord/test/cases/multiple_db_test.rb13
-rw-r--r--activerecord/test/cases/named_scope_test.rb6
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb98
-rw-r--r--activerecord/test/cases/primary_keys_test.rb64
-rw-r--r--activerecord/test/cases/query_cache_test.rb19
-rw-r--r--activerecord/test/cases/reaper_test.rb81
-rw-r--r--activerecord/test/cases/reflection_test.rb24
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb8
-rw-r--r--activerecord/test/cases/relation_test.rb23
-rw-r--r--activerecord/test/cases/relations_test.rb84
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb41
-rw-r--r--activerecord/test/cases/serialization_test.rb9
-rw-r--r--activerecord/test/cases/session_store/session_test.rb10
-rw-r--r--activerecord/test/cases/store_test.rb17
-rw-r--r--activerecord/test/cases/test_case.rb10
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb3
-rw-r--r--activerecord/test/cases/unconnected_test.rb6
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb41
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb64
-rw-r--r--activerecord/test/cases/validations_test.rb2
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb42
-rw-r--r--activerecord/test/fixtures/admin/randomly_named_a9.yml7
-rw-r--r--activerecord/test/fixtures/admin/randomly_named_b0.yml7
-rw-r--r--activerecord/test/fixtures/colleges.yml3
-rw-r--r--activerecord/test/fixtures/courses.yml1
-rw-r--r--activerecord/test/fixtures/developers.yml2
-rw-r--r--activerecord/test/fixtures/dog_lovers.yml4
-rw-r--r--activerecord/test/fixtures/dogs.yml3
-rw-r--r--activerecord/test/fixtures/other_topics.yml42
-rw-r--r--activerecord/test/fixtures/randomly_named_a9.yml7
-rw-r--r--activerecord/test/fixtures/teapots.yml3
-rw-r--r--activerecord/test/migrations/broken/100_migration_that_raises_exception.rb10
-rw-r--r--activerecord/test/migrations/duplicate/3_foo.rb7
-rw-r--r--activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb7
-rw-r--r--activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb7
-rw-r--r--activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb12
-rw-r--r--activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb9
-rw-r--r--activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb12
-rw-r--r--activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb9
-rw-r--r--activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb8
-rw-r--r--activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb12
-rw-r--r--activerecord/test/migrations/rename/1_we_need_things.rb11
-rw-r--r--activerecord/test/migrations/rename/2_rename_things.rb9
-rw-r--r--activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb9
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb (renamed from activerecord/test/migrations/duplicate/1_people_have_last_names.rb)4
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb (renamed from activerecord/test/migrations/duplicate/2_we_need_reminders.rb)0
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb (renamed from activerecord/test/migrations/duplicate/3_innocent_jointable.rb)0
-rw-r--r--activerecord/test/models/admin/randomly_named_c1.rb3
-rw-r--r--activerecord/test/models/admin/user.rb1
-rw-r--r--activerecord/test/models/arunit2_model.rb3
-rw-r--r--activerecord/test/models/author.rb5
-rw-r--r--activerecord/test/models/bird.rb5
-rw-r--r--activerecord/test/models/car.rb2
-rw-r--r--activerecord/test/models/college.rb5
-rw-r--r--activerecord/test/models/company.rb6
-rw-r--r--activerecord/test/models/country.rb2
-rw-r--r--activerecord/test/models/course.rb5
-rw-r--r--activerecord/test/models/dashboard.rb4
-rw-r--r--activerecord/test/models/dog.rb4
-rw-r--r--activerecord/test/models/dog_lover.rb4
-rw-r--r--activerecord/test/models/joke.rb4
-rw-r--r--activerecord/test/models/keyboard.rb2
-rw-r--r--activerecord/test/models/legacy_thing.rb2
-rw-r--r--activerecord/test/models/liquid.rb2
-rw-r--r--activerecord/test/models/minivan.rb2
-rw-r--r--activerecord/test/models/mixed_case_monkey.rb2
-rw-r--r--activerecord/test/models/owner.rb2
-rw-r--r--activerecord/test/models/parrot.rb3
-rw-r--r--activerecord/test/models/person.rb7
-rw-r--r--activerecord/test/models/pet.rb2
-rw-r--r--activerecord/test/models/post.rb10
-rw-r--r--activerecord/test/models/randomly_named_c1.rb3
-rw-r--r--activerecord/test/models/speedometer.rb4
-rw-r--r--activerecord/test/models/string_key_object.rb2
-rw-r--r--activerecord/test/models/subscriber.rb2
-rw-r--r--activerecord/test/models/teapot.rb35
-rw-r--r--activerecord/test/models/topic.rb11
-rw-r--r--activerecord/test/models/toy.rb2
-rw-r--r--activerecord/test/models/treaty.rb2
-rw-r--r--activerecord/test/models/warehouse_thing.rb4
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb11
-rw-r--r--activerecord/test/schema/schema.rb35
-rw-r--r--activerecord/test/support/connection.rb11
-rw-r--r--activeresource/CHANGELOG337
-rw-r--r--activeresource/CHANGELOG.md334
-rw-r--r--activeresource/MIT-LICENSE2
-rw-r--r--activeresource/README.rdoc4
-rwxr-xr-xactiveresource/Rakefile3
-rw-r--r--activeresource/activeresource.gemspec4
-rw-r--r--activeresource/lib/active_resource.rb2
-rw-r--r--activeresource/lib/active_resource/base.rb156
-rw-r--r--activeresource/lib/active_resource/connection.rb46
-rw-r--r--activeresource/lib/active_resource/custom_methods.rb54
-rw-r--r--activeresource/lib/active_resource/exceptions.rb32
-rw-r--r--activeresource/lib/active_resource/http_mock.rb21
-rw-r--r--activeresource/lib/active_resource/validations.rb44
-rw-r--r--activeresource/lib/active_resource/version.rb4
-rw-r--r--activeresource/test/abstract_unit.rb7
-rw-r--r--activeresource/test/cases/authorization_test.rb179
-rw-r--r--activeresource/test/cases/base/custom_methods_test.rb2
-rw-r--r--activeresource/test/cases/base/equality_test.rb2
-rw-r--r--activeresource/test/cases/base/load_test.rb2
-rw-r--r--activeresource/test/cases/base_errors_test.rb36
-rw-r--r--activeresource/test/cases/base_test.rb6
-rw-r--r--activeresource/test/cases/connection_test.rb3
-rw-r--r--activeresource/test/cases/finder_test.rb2
-rw-r--r--activeresource/test/cases/format_test.rb8
-rw-r--r--activeresource/test/cases/observing_test.rb2
-rw-r--r--activeresource/test/fixtures/street_address.rb2
-rw-r--r--activesupport/CHANGELOG1571
-rw-r--r--activesupport/CHANGELOG.md1631
-rw-r--r--activesupport/MIT-LICENSE2
-rw-r--r--activesupport/README.rdoc4
-rw-r--r--activesupport/activesupport.gemspec7
-rwxr-xr-x[-rw-r--r--]activesupport/bin/generate_tables0
-rw-r--r--activesupport/lib/active_support.rb8
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb14
-rw-r--r--activesupport/lib/active_support/base64.rb42
-rw-r--r--activesupport/lib/active_support/basic_object.rb25
-rw-r--r--activesupport/lib/active_support/buffered_logger.rb166
-rw-r--r--activesupport/lib/active_support/cache.rb135
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb7
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb44
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb299
-rw-r--r--activesupport/lib/active_support/concern.rb10
-rw-r--r--activesupport/lib/active_support/configurable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/grouping.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/random_access.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/array/uniq_by.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb20
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb85
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/date/freeze.rb33
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb58
-rw-r--r--activesupport/lib/active_support/core_ext/exception.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/file.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/file/path.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/float.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/float/rounding.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/integer/inflections.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/io.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/debugger.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/singleton_class.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/anonymous.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb38
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb37
-rw-r--r--activesupport/lib/active_support/core_ext/module/method_names.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/module/qualified_const.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb43
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_json.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/process.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/process/daemon.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/range.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/range/blockless_step.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/range/cover.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/range/include_range.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/range/overlaps.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/rexml.rb46
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb112
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/string/encoding.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/multibyte.rb110
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb59
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb29
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb30
-rw-r--r--activesupport/lib/active_support/dependencies.rb93
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb7
-rw-r--r--activesupport/lib/active_support/duration.rb1
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb114
-rw-r--r--activesupport/lib/active_support/gzip.rb3
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb4
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb13
-rw-r--r--activesupport/lib/active_support/inflections.rb22
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb8
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb154
-rw-r--r--activesupport/lib/active_support/json/encoding.rb29
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb6
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb4
-rw-r--r--activesupport/lib/active_support/logger.rb53
-rw-r--r--activesupport/lib/active_support/memoizable.rb116
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb73
-rw-r--r--activesupport/lib/active_support/message_verifier.rb13
-rw-r--r--activesupport/lib/active_support/multibyte.rb25
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb344
-rw-r--r--activesupport/lib/active_support/multibyte/exceptions.rb8
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb81
-rw-r--r--activesupport/lib/active_support/multibyte/utils.rb60
-rw-r--r--activesupport/lib/active_support/notifications.rb105
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb188
-rw-r--r--activesupport/lib/active_support/ordered_options.rb5
-rw-r--r--activesupport/lib/active_support/railtie.rb6
-rw-r--r--activesupport/lib/active_support/ruby/shim.rb9
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb4
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb79
-rw-r--r--activesupport/lib/active_support/test_case.rb45
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb5
-rw-r--r--activesupport/lib/active_support/testing/deprecation.rb19
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb58
-rw-r--r--activesupport/lib/active_support/testing/pending.rb52
-rw-r--r--activesupport/lib/active_support/testing/performance.rb88
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb36
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby/mri.rb57
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby/yarv.rb57
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb88
-rw-r--r--activesupport/lib/active_support/time.rb2
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb15
-rw-r--r--activesupport/lib/active_support/values/unicode_tables.datbin813343 -> 904408 bytes
-rw-r--r--activesupport/lib/active_support/version.rb4
-rw-r--r--activesupport/lib/active_support/whiny_nil.rb60
-rw-r--r--activesupport/lib/active_support/xml_mini.rb9
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb8
-rw-r--r--activesupport/test/abstract_unit.rb34
-rw-r--r--activesupport/test/benchmarkable_test.rb53
-rw-r--r--activesupport/test/broadcast_logger_test.rb82
-rw-r--r--activesupport/test/buffered_logger_test.rb163
-rw-r--r--activesupport/test/caching_test.rb235
-rw-r--r--activesupport/test/callback_inheritance_test.rb25
-rw-r--r--activesupport/test/callbacks_test.rb123
-rw-r--r--activesupport/test/class_cache_test.rb43
-rw-r--r--activesupport/test/clean_logger_test.rb39
-rw-r--r--activesupport/test/concern_test.rb9
-rw-r--r--activesupport/test/constantize_test_cases.rb5
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb70
-rw-r--r--activesupport/test/core_ext/base64_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/bigdecimal_test.rb2
-rw-r--r--activesupport/test/core_ext/blank_test.rb2
-rw-r--r--activesupport/test/core_ext/class/attribute_accessor_test.rb2
-rw-r--r--activesupport/test/core_ext/class/delegating_attributes_test.rb2
-rw-r--r--activesupport/test/core_ext/class_test.rb2
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb22
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb39
-rw-r--r--activesupport/test/core_ext/duplicable_test.rb4
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb11
-rw-r--r--activesupport/test/core_ext/file_test.rb6
-rw-r--r--activesupport/test/core_ext/float_ext_test.rb26
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb15
-rw-r--r--activesupport/test/core_ext/integer_ext_test.rb15
-rw-r--r--activesupport/test/core_ext/io_test.rb23
-rw-r--r--activesupport/test/core_ext/kernel_test.rb13
-rw-r--r--activesupport/test/core_ext/load_error_test.rb4
-rw-r--r--activesupport/test/core_ext/module/attr_internal_test.rb2
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_test.rb2
-rw-r--r--activesupport/test/core_ext/module/attribute_aliasing_test.rb2
-rw-r--r--activesupport/test/core_ext/module/remove_method_test.rb29
-rw-r--r--activesupport/test/core_ext/module/synchronization_test.rb89
-rw-r--r--activesupport/test/core_ext/module_test.rb10
-rw-r--r--activesupport/test/core_ext/name_error_test.rb2
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/object/inclusion_test.rb12
-rw-r--r--activesupport/test/core_ext/object/to_param_test.rb2
-rw-r--r--activesupport/test/core_ext/object/to_query_test.rb2
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/proc_test.rb2
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb31
-rw-r--r--activesupport/test/core_ext/regexp_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb75
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb62
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb18
-rw-r--r--activesupport/test/core_ext/uri_ext_test.rb10
-rw-r--r--activesupport/test/dependencies_test.rb83
-rw-r--r--activesupport/test/deprecation/proxy_wrappers_test.rb2
-rw-r--r--activesupport/test/deprecation_test.rb20
-rw-r--r--activesupport/test/descendants_tracker_with_autoloading_test.rb5
-rw-r--r--activesupport/test/descendants_tracker_without_autoloading_test.rb5
-rw-r--r--activesupport/test/file_update_checker_test.rb61
-rw-r--r--activesupport/test/flush_cache_on_private_memoization_test.rb45
-rw-r--r--activesupport/test/gzip_test.rb7
-rw-r--r--activesupport/test/i18n_test.rb2
-rw-r--r--activesupport/test/inflector_test.rb16
-rw-r--r--activesupport/test/inflector_test_cases.rb8
-rw-r--r--activesupport/test/isolation_test.rb182
-rw-r--r--activesupport/test/json/encoding_test.rb34
-rw-r--r--activesupport/test/load_paths_test.rb2
-rw-r--r--activesupport/test/log_subscriber_test.rb4
-rw-r--r--activesupport/test/memoizable_test.rb290
-rw-r--r--activesupport/test/message_encryptor_test.rb61
-rw-r--r--activesupport/test/message_verifier_test.rb6
-rw-r--r--activesupport/test/multibyte_chars_test.rb75
-rw-r--r--activesupport/test/multibyte_conformance.rb2
-rw-r--r--activesupport/test/multibyte_test_helpers.rb5
-rw-r--r--activesupport/test/multibyte_unicode_database_test.rb2
-rw-r--r--activesupport/test/multibyte_utils_test.rb137
-rw-r--r--activesupport/test/notifications_test.rb20
-rw-r--r--activesupport/test/option_merger_test.rb2
-rw-r--r--activesupport/test/ordered_hash_test.rb36
-rw-r--r--activesupport/test/ordered_options_test.rb2
-rw-r--r--activesupport/test/rescuable_test.rb2
-rw-r--r--activesupport/test/safe_buffer_test.rb18
-rw-r--r--activesupport/test/string_inquirer_test.rb2
-rw-r--r--activesupport/test/tagged_logging_test.rb4
-rw-r--r--activesupport/test/test_case_test.rb64
-rw-r--r--activesupport/test/test_test.rb21
-rw-r--r--activesupport/test/time_zone_test.rb2
-rw-r--r--activesupport/test/transliterate_test.rb2
-rw-r--r--activesupport/test/ts_isolated.rb5
-rw-r--r--activesupport/test/whiny_nil_test.rb54
-rw-r--r--activesupport/test/xml_mini/jdom_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/libxml_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/libxmlsax_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/nokogiri_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/nokogirisax_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini_test.rb2
-rwxr-xr-xci/travis.rb2
-rw-r--r--install.rb2
-rw-r--r--load_paths.rb2
-rw-r--r--rails.gemspec4
-rw-r--r--railties/CHANGELOG2390
-rw-r--r--railties/CHANGELOG.md2445
-rw-r--r--railties/MIT-LICENSE2
-rw-r--r--railties/README.rdoc4
-rw-r--r--railties/guides/assets/images/rails_guides_kindle_cover.jpgbin0 -> 31785 bytes
-rw-r--r--railties/guides/assets/stylesheets/fixes.css2
-rw-r--r--railties/guides/assets/stylesheets/kindle.css11
-rw-r--r--railties/guides/assets/stylesheets/main.css8
-rw-r--r--railties/guides/code/getting_started/Gemfile23
-rw-r--r--railties/guides/code/getting_started/README.rdoc (renamed from railties/guides/code/getting_started/README)10
-rw-r--r--railties/guides/code/getting_started/app/assets/javascripts/application.js12
-rw-r--r--railties/guides/code/getting_started/app/assets/stylesheets/application.css16
-rw-r--r--railties/guides/code/getting_started/app/controllers/posts_controller.rb4
-rw-r--r--railties/guides/code/getting_started/app/views/layouts/application.html.erb4
-rw-r--r--railties/guides/code/getting_started/config/application.rb21
-rw-r--r--railties/guides/code/getting_started/config/environments/development.rb24
-rw-r--r--railties/guides/code/getting_started/config/environments/production.rb42
-rw-r--r--railties/guides/code/getting_started/config/environments/test.rb26
-rw-r--r--railties/guides/code/getting_started/config/initializers/inflections.rb5
-rw-r--r--railties/guides/code/getting_started/config/routes.rb8
-rw-r--r--railties/guides/code/getting_started/public/500.html1
-rw-r--r--railties/guides/rails_guides/generator.rb112
-rw-r--r--railties/guides/rails_guides/helpers.rb16
-rw-r--r--railties/guides/rails_guides/indexer.rb3
-rw-r--r--railties/guides/rails_guides/levenshtein.rb2
-rw-r--r--railties/guides/source/3_1_release_notes.textile109
-rw-r--r--railties/guides/source/3_2_release_notes.textile540
-rw-r--r--railties/guides/source/_license.html.erb2
-rw-r--r--railties/guides/source/_welcome.html.erb19
-rw-r--r--railties/guides/source/action_controller_overview.textile2
-rw-r--r--railties/guides/source/action_mailer_basics.textile15
-rw-r--r--railties/guides/source/action_view_overview.textile103
-rw-r--r--railties/guides/source/active_model_basics.textile2
-rw-r--r--railties/guides/source/active_record_basics.textile4
-rw-r--r--railties/guides/source/active_record_querying.textile223
-rw-r--r--railties/guides/source/active_record_validations_callbacks.textile51
-rw-r--r--railties/guides/source/active_resource_basics.textile2
-rw-r--r--railties/guides/source/active_support_core_extensions.textile258
-rw-r--r--railties/guides/source/api_documentation_guidelines.textile7
-rw-r--r--railties/guides/source/asset_pipeline.textile204
-rw-r--r--railties/guides/source/association_basics.textile13
-rw-r--r--railties/guides/source/caching_with_rails.textile36
-rw-r--r--railties/guides/source/command_line.textile42
-rw-r--r--railties/guides/source/configuring.textile43
-rw-r--r--railties/guides/source/contributing_to_ruby_on_rails.textile26
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/documents.yaml157
-rw-r--r--railties/guides/source/engines.textile12
-rw-r--r--railties/guides/source/form_helpers.textile16
-rw-r--r--railties/guides/source/generators.textile18
-rw-r--r--railties/guides/source/getting_started.textile209
-rw-r--r--railties/guides/source/i18n.textile8
-rw-r--r--railties/guides/source/index.html.erb189
-rw-r--r--railties/guides/source/initialization.textile42
-rw-r--r--railties/guides/source/kindle/KINDLE.md26
-rw-r--r--railties/guides/source/kindle/copyright.html.erb1
-rw-r--r--railties/guides/source/kindle/layout.html.erb27
-rw-r--r--railties/guides/source/kindle/rails_guides.opf.erb52
-rw-r--r--railties/guides/source/kindle/toc.html.erb24
-rw-r--r--railties/guides/source/kindle/toc.ncx.erb64
-rw-r--r--railties/guides/source/kindle/welcome.html.erb5
-rw-r--r--railties/guides/source/layout.html.erb59
-rw-r--r--railties/guides/source/layouts_and_rendering.textile67
-rw-r--r--railties/guides/source/migrations.textile563
-rw-r--r--railties/guides/source/performance_testing.textile4
-rw-r--r--railties/guides/source/plugins.textile37
-rw-r--r--railties/guides/source/rails_application_templates.textile27
-rw-r--r--railties/guides/source/rails_on_rack.textile1
-rw-r--r--railties/guides/source/routing.textile4
-rw-r--r--railties/guides/source/ruby_on_rails_guides_guidelines.textile16
-rw-r--r--railties/guides/source/security.textile2
-rw-r--r--railties/guides/source/testing.textile18
-rw-r--r--railties/lib/rails.rb23
-rw-r--r--railties/lib/rails/application.rb138
-rw-r--r--railties/lib/rails/application/bootstrap.rb35
-rw-r--r--railties/lib/rails/application/configuration.rb80
-rw-r--r--railties/lib/rails/application/finisher.rb58
-rw-r--r--railties/lib/rails/application/railties.rb2
-rw-r--r--railties/lib/rails/application/route_inspector.rb105
-rw-r--r--railties/lib/rails/application/routes_reloader.rb21
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb13
-rw-r--r--railties/lib/rails/code_statistics.rb27
-rw-r--r--railties/lib/rails/commands.rb2
-rw-r--r--railties/lib/rails/commands/benchmarker.rb2
-rw-r--r--railties/lib/rails/commands/console.rb75
-rw-r--r--railties/lib/rails/commands/dbconsole.rb10
-rw-r--r--railties/lib/rails/commands/plugin.rb542
-rw-r--r--railties/lib/rails/commands/profiler.rb2
-rw-r--r--railties/lib/rails/commands/server.rb9
-rw-r--r--railties/lib/rails/console/app.rb49
-rw-r--r--railties/lib/rails/console/helpers.rb14
-rw-r--r--railties/lib/rails/deprecation.rb18
-rw-r--r--railties/lib/rails/engine.rb83
-rw-r--r--railties/lib/rails/engine/configuration.rb4
-rw-r--r--railties/lib/rails/engine/railties.rb9
-rw-r--r--railties/lib/rails/generators.rb11
-rw-r--r--railties/lib/rails/generators/actions.rb47
-rw-r--r--railties/lib/rails/generators/app_base.rb76
-rw-r--r--railties/lib/rails/generators/base.rb28
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb6
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb65
-rw-r--r--railties/lib/rails/generators/named_base.rb17
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile10
-rw-r--r--railties/lib/rails/generators/rails/app/templates/README10
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb28
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt25
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt48
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt24
-rw-r--r--railties/lib/rails/generators/rails/app/templates/gitignore1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/index.html2
-rw-r--r--railties/lib/rails/generators/rails/controller/templates/controller.rb2
-rw-r--r--railties/lib/rails/generators/rails/migration/migration_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/model/model_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb57
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/gitignore8
-rw-r--r--railties/lib/rails/generators/rails/scaffold/USAGE24
-rw-r--r--railties/lib/rails/generators/rails/task/USAGE9
-rw-r--r--railties/lib/rails/generators/rails/task/task_generator.rb12
-rw-r--r--railties/lib/rails/generators/rails/task/templates/task.rb8
-rw-r--r--railties/lib/rails/generators/resource_helpers.rb4
-rw-r--r--railties/lib/rails/generators/test_case.rb4
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb2
-rw-r--r--railties/lib/rails/performance_test_help.rb2
-rw-r--r--railties/lib/rails/plugin.rb91
-rw-r--r--railties/lib/rails/rack/debugger.rb2
-rw-r--r--railties/lib/rails/rack/log_tailer.rb10
-rw-r--r--railties/lib/rails/railtie.rb12
-rw-r--r--railties/lib/rails/railtie/configuration.rb12
-rw-r--r--railties/lib/rails/ruby_version_check.rb14
-rw-r--r--railties/lib/rails/rubyprof_ext.rb2
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb19
-rw-r--r--railties/lib/rails/tasks/documentation.rake56
-rw-r--r--railties/lib/rails/tasks/statistics.rake1
-rw-r--r--railties/lib/rails/test_help.rb24
-rw-r--r--railties/lib/rails/test_unit/sub_test_task.rb8
-rw-r--r--railties/lib/rails/test_unit/testing.rake87
-rw-r--r--railties/lib/rails/version.rb4
-rw-r--r--railties/railties.gemspec4
-rw-r--r--railties/test/abstract_unit.rb3
-rw-r--r--railties/test/application/asset_debugging_test.rb2
-rw-r--r--railties/test/application/assets_test.rb73
-rw-r--r--railties/test/application/build_original_fullpath_test.rb27
-rw-r--r--railties/test/application/configuration_test.rb44
-rw-r--r--railties/test/application/console_test.rb16
-rw-r--r--railties/test/application/generators_test.rb2
-rw-r--r--railties/test/application/initializers/boot_test.rb2
-rw-r--r--railties/test/application/initializers/check_ruby_version_test.rb39
-rw-r--r--railties/test/application/initializers/frameworks_test.rb9
-rw-r--r--railties/test/application/initializers/hooks_test.rb2
-rw-r--r--railties/test/application/initializers/i18n_test.rb5
-rw-r--r--railties/test/application/initializers/load_path_test.rb2
-rw-r--r--railties/test/application/initializers/notifications_test.rb2
-rw-r--r--railties/test/application/loading_test.rb171
-rw-r--r--railties/test/application/middleware/best_practices_test.rb2
-rw-r--r--railties/test/application/middleware/cache_test.rb6
-rw-r--r--railties/test/application/middleware/cookies_test.rb47
-rw-r--r--railties/test/application/middleware/exceptions_test.rb116
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb2
-rw-r--r--railties/test/application/middleware/sendfile_test.rb2
-rw-r--r--railties/test/application/middleware/session_test.rb30
-rw-r--r--railties/test/application/middleware/show_exceptions_test.rb41
-rw-r--r--railties/test/application/middleware_test.rb35
-rw-r--r--railties/test/application/paths_test.rb4
-rw-r--r--railties/test/application/rack/logger_test.rb2
-rw-r--r--railties/test/application/rackup_test.rb4
-rw-r--r--railties/test/application/rake/migrations_test.rb157
-rw-r--r--railties/test/application/rake/notes_test.rb56
-rw-r--r--railties/test/application/rake_test.rb97
-rw-r--r--railties/test/application/route_inspect_test.rb61
-rw-r--r--railties/test/application/routing_test.rb2
-rw-r--r--railties/test/application/runner_test.rb2
-rw-r--r--railties/test/application/test_test.rb27
-rw-r--r--railties/test/application/url_generation_test.rb2
-rw-r--r--railties/test/backtrace_cleaner_test.rb28
-rw-r--r--railties/test/commands/console_test.rb87
-rw-r--r--railties/test/fixtures/lib/generators/usage_template/USAGE1
-rw-r--r--railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb5
-rw-r--r--railties/test/generators/actions_test.rb47
-rw-r--r--railties/test/generators/app_generator_test.rb80
-rw-r--r--railties/test/generators/generated_attribute_test.rb7
-rw-r--r--railties/test/generators/mailer_generator_test.rb29
-rw-r--r--railties/test/generators/migration_generator_test.rb62
-rw-r--r--railties/test/generators/model_generator_test.rb68
-rw-r--r--railties/test/generators/named_base_test.rb9
-rw-r--r--railties/test/generators/namespaced_generators_test.rb6
-rw-r--r--railties/test/generators/orm_test.rb38
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb57
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb13
-rw-r--r--railties/test/generators/shared_generator_tests.rb2
-rw-r--r--railties/test/generators/task_generator_test.rb12
-rw-r--r--railties/test/generators_test.rb12
-rw-r--r--railties/test/isolation/abstract_unit.rb21
-rw-r--r--railties/test/railties/engine_test.rb174
-rw-r--r--railties/test/railties/generators_test.rb4
-rw-r--r--railties/test/railties/mounted_engine_test.rb2
-rw-r--r--railties/test/railties/plugin_ordering_test.rb76
-rw-r--r--railties/test/railties/plugin_test.rb123
-rw-r--r--railties/test/railties/railtie_test.rb2
-rw-r--r--railties/test/railties/shared_tests.rb23
-rw-r--r--tasks/release.rb13
-rw-r--r--version.rb4
1130 files changed, 44662 insertions, 36664 deletions
diff --git a/.gitignore b/.gitignore
index aa14cee911..52a431867c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
# Check out http://help.github.com/ignore-files/ for how to set that up.
debug.log
+.Gemfile
/.bundle
/.rbenv-version
/.rvmrc
diff --git a/.travis.yml b/.travis.yml
index 4a27f7788d..68d5c594ae 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,14 +1,13 @@
script: 'ci/travis.rb'
rvm:
- - 1.8.7
- - 1.9.2
- #- 1.9.3 # Disable 1.9.3 builds until Travis is on 1.9.3-rc1.
+ - 1.9.3
env:
- "GEM=railties"
- "GEM=ap,am,amo,ares,as"
- "GEM=ar:mysql"
- "GEM=ar:mysql2"
- "GEM=ar:sqlite3"
+ - "GEM=ar:postgresql"
notifications:
email: false
irc:
@@ -16,4 +15,9 @@ notifications:
on_failure: always
channels:
- "irc.freenode.org#rails-contrib"
+ campfire:
+ on_success: change
+ on_failure: always
+ rooms:
+ - secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
bundler_args: --path vendor/bundle
diff --git a/Gemfile b/Gemfile
index 4ac6bf764d..3046a97573 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,99 +1,93 @@
-source "http://rubygems.org"
+source 'https://rubygems.org'
gemspec
if ENV['AREL']
- gem "arel", :path => ENV['AREL']
+ gem 'arel', :path => ENV['AREL']
+else
+ gem 'arel'
end
-gem "bcrypt-ruby", "~> 3.0.0"
-gem "jquery-rails"
+gem 'bcrypt-ruby', '~> 3.0.0'
+gem 'jquery-rails'
if ENV['JOURNEY']
- gem "journey", :path => ENV['JOURNEY']
+ gem 'journey', :path => ENV['JOURNEY']
else
- gem "journey", :git => "git://github.com/rails/journey"
+ gem 'journey', :git => "git://github.com/rails/journey"
end
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
-gem "uglifier", ">= 1.0.3", :require => false
+gem 'uglifier', '>= 1.0.3', :require => false
-gem "rake", ">= 0.8.7"
-gem "mocha", ">= 0.9.8"
+gem 'rake', '>= 0.8.7'
+gem 'mocha', '>= 0.9.8'
group :doc do
- gem "rdoc", "~> 3.4"
- gem "sdoc", "~> 0.3"
- gem "RedCloth", "~> 4.2" if RUBY_VERSION < "1.9.3"
- gem "w3c_validators"
+ # The current sdoc cannot generate GitHub links due
+ # to a bug, but the PR that fixes it has been there
+ # for some weeks unapplied. As a temporary solution
+ # this is our own fork with the fix.
+ gem 'sdoc', :git => 'git://github.com/fxn/sdoc.git'
+ gem 'RedCloth', '~> 4.2'
+ gem 'w3c_validators'
end
# AS
-gem "memcache-client", ">= 1.8.5"
+gem 'memcache-client', '>= 1.8.5'
-platforms :mri_18 do
- gem "system_timer"
- gem "ruby-debug", ">= 0.10.3" unless ENV['TRAVIS']
- gem "json"
-end
-
-platforms :mri_19 do
- # TODO: Remove the conditional when ruby-debug19 supports Ruby >= 1.9.3
- gem "ruby-debug19", :require => "ruby-debug" unless RUBY_VERSION > "1.9.2" || ENV['TRAVIS']
-end
+# Add your own local bundler stuff
+local_gemfile = File.dirname(__FILE__) + "/.Gemfile"
+instance_eval File.read local_gemfile if File.exists? local_gemfile
platforms :mri do
group :test do
- gem "ruby-prof" if RUBY_VERSION < "1.9.3"
+ gem 'ruby-prof'
end
end
platforms :ruby do
- if ENV["RB_FSEVENT"]
- gem "rb-fsevent"
- end
- gem "json"
- gem "yajl-ruby"
- gem "nokogiri", ">= 1.4.5"
+ gem 'json'
+ gem 'yajl-ruby'
+ gem 'nokogiri', '>= 1.4.5'
# AR
- gem "sqlite3", "~> 1.3.4"
+ gem 'sqlite3', '~> 1.3.5'
group :db do
- gem "pg", ">= 0.11.0" unless ENV['TRAVIS'] # once pg is on travis this can be removed
- gem "mysql", ">= 2.8.1"
- gem "mysql2", ">= 0.3.6"
+ gem 'pg', '>= 0.11.0'
+ gem 'mysql', '>= 2.8.1'
+ gem 'mysql2', '>= 0.3.10'
end
end
platforms :jruby do
- gem "ruby-debug", ">= 0.10.3"
- gem "json"
- gem "activerecord-jdbcsqlite3-adapter", ">= 1.2.0"
+ gem 'json'
+ gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.0'
# This is needed by now to let tests work on JRuby
# TODO: When the JRuby guys merge jruby-openssl in
# jruby this will be removed
- gem "jruby-openssl"
+ gem 'jruby-openssl'
group :db do
- gem "activerecord-jdbcmysql-adapter", ">= 1.2.0"
- gem "activerecord-jdbcpostgresql-adapter", ">= 1.2.0"
+ gem 'activerecord-jdbcmysql-adapter', '>= 1.2.0'
+ gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.0'
end
end
# gems that are necessary for ActiveRecord tests with Oracle database
if ENV['ORACLE_ENHANCED_PATH'] || ENV['ORACLE_ENHANCED']
platforms :ruby do
- gem "ruby-oci8", ">= 2.0.4"
+ gem 'ruby-oci8', '>= 2.0.4'
end
if ENV['ORACLE_ENHANCED_PATH']
- gem "activerecord-oracle_enhanced-adapter", :path => ENV['ORACLE_ENHANCED_PATH']
+ gem 'activerecord-oracle_enhanced-adapter', :path => ENV['ORACLE_ENHANCED_PATH']
else
- gem "activerecord-oracle_enhanced-adapter", :git => "git://github.com/rsim/oracle-enhanced.git"
+ gem 'activerecord-oracle_enhanced-adapter', :git => 'git://github.com/rsim/oracle-enhanced.git'
end
end
# A gem necessary for ActiveRecord tests with IBM DB
-gem "ibm_db" if ENV['IBM_DB']
+gem 'ibm_db' if ENV['IBM_DB']
diff --git a/RAILS_VERSION b/RAILS_VERSION
index dd8b7cd23b..e1e048d8f0 100644
--- a/RAILS_VERSION
+++ b/RAILS_VERSION
@@ -1 +1 @@
-3.2.0.beta
+4.0.0.beta
diff --git a/README.rdoc b/README.rdoc
index 0def4e5d12..78640b39aa 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -67,9 +67,12 @@ We encourage you to contribute to Ruby on Rails! Please check out the {Contribut
guide}[http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how
to proceed. {Join us}[http://contributors.rubyonrails.org]!
-== Travis Build Status {<img src="https://secure.travis-ci.org/rails/rails.png"/>}[http://travis-ci.org/rails/rails]
+== Build Status {<img src="https://secure.travis-ci.org/rails/rails.png"/>}[http://travis-ci.org/rails/rails]
+
+== Dependency Status {<img src="https://gemnasium.com/rails/rails.png?travis"/>}[https://gemnasium.com/rails/rails]
== License
-Ruby on Rails is released under the MIT license.
+Ruby on Rails is released under the MIT license:
+* http://www.opensource.org/licenses/MIT
diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc
index cbc9d0e1de..7a77f9bba2 100644
--- a/RELEASING_RAILS.rdoc
+++ b/RELEASING_RAILS.rdoc
@@ -81,8 +81,20 @@ You can review the commits for the 3.0.10 release like this:
[aaron@higgins rails (3-0-10)]$ git log v3.0.9..
+If you're doing a stable branch release, you should also ensure that all of
+the CHANGELOG entries in the stable branch are also synced to the master
+branch.
+
=== Update the RAILS_VERSION file to include the RC.
+=== Build and test the gem.
+
+Run `rake install` to generate the gems and install them locally. Then try
+generating a new app and ensure that nothing explodes.
+
+This will stop you from looking silly when you push an RC to rubygems.org and
+then realise it is broken.
+
=== Release the gem.
IMPORTANT: Due to YAML parse problems on the rubygems.org server, it is safest
@@ -150,6 +162,7 @@ Today, do this stuff in this order:
* Apply security patches to the release branch
* Update CHANGELOG with security fixes.
* Update RAILS_VERSION to remove the rc
+* Build and test the gem
* Release the gems
* Email security lists
* Email general announcement lists
@@ -184,3 +197,34 @@ There are two simple steps for fixing the CI:
2. Fix it
Repeat these steps until the CI is green.
+
+=== Manually trigger docs generation
+
+We have a post-receive hook in GitHub that calls the docs server on pushes.
+It triggers generation and publication of edge docs, updates the contrib app,
+and generates and publishes stable docs if a new stable tag is detected.
+
+The hook unfortunately is not invoked by tag pushing, so once the new stable
+tag has been pushed to origin, please run
+
+ rake publish_docs
+
+You should see something like this:
+
+ Rails master hook tasks scheduled:
+
+ * updates the local checkout
+ * updates Rails Contributors
+ * generates and publishes edge docs
+
+ If a new stable tag is detected it also
+
+ * generates and publishes stable docs
+
+ This needs typically a few minutes.
+
+Note you do not need to specify the tag, the docs server figures it out.
+
+Also, don't worry if you call that multiple times or the hook is triggered
+again by some immediate regular push, if the scripts are running new calls
+are just queued (in a queue of size 1).
diff --git a/Rakefile b/Rakefile
index a47d415abc..03b8a952c3 100755
--- a/Rakefile
+++ b/Rakefile
@@ -77,9 +77,10 @@ RDoc::Task.new do |rdoc|
rdoc_main.gsub!(/^(?=\S).*?\b(?=Rails)\b/) { "#$&\\" }
rdoc_main.gsub!(%r{link:/rails/rails/blob/master/(\w+)/README\.rdoc}, "link:files/\\1/README_rdoc.html")
- # Remove Travis build status image from API pages. Only GitHub README page gets this image
- # https build image is used to avoid GitHub caching: http://about.travis-ci.org/docs/user/status-images
- rdoc_main.gsub!(%r{^== Travis.*}, '')
+ # Remove Travis and Gemnasium status images from API pages. Only GitHub
+ # README page gets these images. Travis' https build image is used to avoid
+ # GitHub caching: http://about.travis-ci.org/docs/user/status-images
+ rdoc_main.gsub!(%r{^== (Build|Dependency) Status.*}, '')
File.open(RDOC_MAIN, 'w') do |f|
f.write(rdoc_main)
@@ -93,28 +94,28 @@ RDoc::Task.new do |rdoc|
rdoc.options << '-f' << 'sdoc'
rdoc.options << '-T' << 'rails'
- rdoc.options << '-c' << 'utf-8'
+ rdoc.options << '-e' << 'UTF-8'
rdoc.options << '-g' # SDoc flag, link methods to GitHub
rdoc.options << '-m' << RDOC_MAIN
- rdoc.rdoc_files.include('railties/CHANGELOG')
+ rdoc.rdoc_files.include('railties/CHANGELOG.md')
rdoc.rdoc_files.include('railties/MIT-LICENSE')
rdoc.rdoc_files.include('railties/README.rdoc')
rdoc.rdoc_files.include('railties/lib/**/*.rb')
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/**/*.rb')
rdoc.rdoc_files.include('activerecord/README.rdoc')
- rdoc.rdoc_files.include('activerecord/CHANGELOG')
+ rdoc.rdoc_files.include('activerecord/CHANGELOG.md')
rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb')
rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*')
rdoc.rdoc_files.include('activeresource/README.rdoc')
- rdoc.rdoc_files.include('activeresource/CHANGELOG')
+ rdoc.rdoc_files.include('activeresource/CHANGELOG.md')
rdoc.rdoc_files.include('activeresource/lib/active_resource.rb')
rdoc.rdoc_files.include('activeresource/lib/active_resource/*')
rdoc.rdoc_files.include('actionpack/README.rdoc')
- rdoc.rdoc_files.include('actionpack/CHANGELOG')
+ rdoc.rdoc_files.include('actionpack/CHANGELOG.md')
rdoc.rdoc_files.include('actionpack/lib/abstract_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
@@ -122,18 +123,18 @@ RDoc::Task.new do |rdoc|
rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
rdoc.rdoc_files.include('actionmailer/README.rdoc')
- rdoc.rdoc_files.include('actionmailer/CHANGELOG')
+ rdoc.rdoc_files.include('actionmailer/CHANGELOG.md')
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/mail_helper.rb')
rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
rdoc.rdoc_files.include('activesupport/README.rdoc')
- rdoc.rdoc_files.include('activesupport/CHANGELOG')
+ rdoc.rdoc_files.include('activesupport/CHANGELOG.md')
rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb')
rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*')
rdoc.rdoc_files.include('activemodel/README.rdoc')
- rdoc.rdoc_files.include('activemodel/CHANGELOG')
+ rdoc.rdoc_files.include('activemodel/CHANGELOG.md')
rdoc.rdoc_files.include('activemodel/lib/active_model/**/*.rb')
end
@@ -189,7 +190,7 @@ end
#
desc 'Publishes docs, run this AFTER a new stable tag has been pushed'
task :publish_docs do
- Net::HTTP.new('rails-hooks.hashref.com').start do |http|
+ Net::HTTP.new('api.rubyonrails.org', 8080).start do |http|
request = Net::HTTP::Post.new('/rails-master-hook')
response = http.request(request)
puts response.body
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG
deleted file mode 100644
index 14c887eb53..0000000000
--- a/actionmailer/CHANGELOG
+++ /dev/null
@@ -1,449 +0,0 @@
-*Rails 3.1.0 (August 30, 2011)*
-
-* No changes
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-* remove AM delegating register_observer and register_interceptor to Mail [Josh Kalderimis]
-
-
-*Rails 3.0.6 (April 5, 2011)
-
-* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 [Santiago Pastorino]
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* No changes.
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* No changes
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* No Changes, just a version bump.
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* subject is automatically looked up on I18n using mailer_name and action_name as scope as in t(".subject") [JK]
-
-* Changed encoding behaviour of mail, so updated tests in actionmailer and bumped mail version to 2.2.1 [ML]
-
-* Added ability to pass Proc objects to the defaults hash [ML]
-
-* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead [ML]
-
-* Lot of updates to various test cases that now work better with the new Mail and so have different expectations
-
-* Added interceptors and observers from Mail [ML]
-
- ActionMailer::Base.register_interceptor calls Mail.register_interceptor
- ActionMailer::Base.register_observer calls Mail.register_observer
-
-* Mail::Part now no longer has nil as a default charset, it is always set to something, and defaults to UTF-8
-
-* Added explict setting of charset in set_fields! method to make sure Mail has the user defined default
-
-* Removed quoting.rb and refactored for Mail to take responsibility of all quoting and auto encoding requirements for the header.
-
-* Fixed several tests which had incorrect encoding.
-
-* Changed all utf-8 to UTF-8 for consistency
-
-* Whole new API added with tests. See base.rb for full details. Old API is deprecated.
-
-* The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted
-
-* Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'}
-
-* ActionMailer::Base :default_implicit_parts_order now is in the sequence of the order you want, no reversing of ordering takes place. The default order now is text/plain, then text/enriched, then text/html and then any other part that is not one of these three.
-
-* Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc
-
-* Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want.
-* Mail::Message#set_content_type does not exist, it is simply Mail::Message#content_type
-
-* Every mail message gets a unique message_id unless you specify one, had to change all the tests that check for equality with expected.encoded == actual.encoded to first replace their message_ids with control values
-
-* Mail now has a proper concept of parts, remove the ActionMailer::Part and ActionMailer::PartContainer classes
-
-* Calling #encoded on any object returns it as a string ready to go into the output stream of an email, this means it includes the \r\n at the end of the lines and the object is pre-wrapped with \r\n\t if it is a header field. Also, the "encoded" value includes the field name if it is a header field.
-
-* Attachments are only the actual attachment, with filename etc. A part contains an attachment. The part has the content_type etc. So attachments.last.content_type is invalid. But parts.last.content_type
-
-* There is no idea of a "sub_head" in Mail. A part is just a Message with some extra functionality, so it just has a "header" like a normal mail message
-
-
-*2.3.2 [Final] (March 15, 2009)*
-
-* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
-
-* Fixed RFC-2045 quoted-printable bug #1421 [squadette]
-
-* Fixed that no body charset would be set when there are attachments present #740 [Paweł Kondzior]
-
-
-*2.2.1 [RC2] (November 14th, 2008)*
-
-* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 [Grant Hollingworth]
-
-
-*2.2.0 [RC1] (October 24th, 2008)*
-
-* Add layout functionality to mailers [Pratik Naik]
-
- Mailer layouts behaves just like controller layouts, except layout names need to
- have '_mailer' postfix for them to be automatically picked up.
-
-
-*2.1.0 (May 31st, 2008)*
-
-* Fixed that a return-path header would be ignored #7572 [joost]
-
-* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
-
-* Updated TMail to version 1.2.1 [Mikel Lindsaar]
-
-* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
-
-
-*2.0.2* (December 16th, 2007)
-
-* Included in Rails 2.0.2
-
-
-*2.0.1* (December 7th, 2007)
-
-* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [Rick Olson]
-
- * Pass the template_root as an array as ActionView's view_path
- * Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
-
-* Fixed that partials would be broken when using text.plain.erb as the extension #10130 [java]
-
-* Update README to use new smtp settings configuration API. Closes #10060 [psq]
-
-* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [Zach Dennis]
-
-* Update TMail to v1.1.0. Use an updated version of TMail if available. [Mikel Lindsaar]
-
-* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Michael Koziarski]
-
-* Fix silent failure of rxml templates. #9879 [jstewart]
-
-* Fix attachment decoding when using the TMail C extension. #7861 [orangechicken]
-
-* Increase mail delivery test coverage. #8692 [Kamal Fariz Mahyuddin]
-
-* Register alternative template engines using ActionMailer::Base.register_template_extension('haml'). #7534 [cwd, Josh Peek]
-
-* Only load ActionController::UrlWriter if ActionController is present [Rick Olson]
-
-* Make sure parsed emails recognized attachments nested inside multipart parts. #6714 [Jamis Buck]
-
-* Allow mailer actions named send by using __send__ internally. #6467 [iGEL]
-
-* Add assert_emails and assert_no_emails to test the number of emails delivered. #6479 [Jonathan Viney]
- # Assert total number of emails delivered:
- assert_emails 0
- ContactMailer.deliver_contact
- assert_emails 1
-
- # Assert number of emails delivered within a block:
- assert_emails 1 do
- post :signup, :name => 'Jonathan'
- end
-
-
-*1.3.3* (March 12th, 2007)
-
-* Depend on Action Pack 1.13.3
-
-
-*1.3.2* (February 5th, 2007)
-
-* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Michael Koziarski]
-
-
-*1.3.1* (January 16th, 2007)
-
-* Depend on Action Pack 1.13.1
-
-
-*1.3.0* (January 16th, 2007)
-
-* Make mime version default to 1.0. closes #2323 [ror@andreas-s.net]
-
-* Make sure quoted-printable text is decoded correctly when only portions of the text are encoded. closes #3154. [jon@siliconcircus.com]
-
-* Make sure DOS newlines in quoted-printable text are normalized to unix newlines before unquoting. closes #4166 and #4452. [Jamis Buck]
-
-* Fixed that iconv decoding should catch InvalidEncoding #3153 [jon@siliconcircus.com]
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [David Heinemeier Hansson]
-
-* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-
-* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 [somekool@gmail.com]
-
-* Resolve action naming collision. #5520 [ssinghi@kreeti.com]
-
-* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
-
-* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
-
-* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-
-* Correct spurious documentation example code which results in a SyntaxError. [Marcel Molina Jr.]
-
-
-*1.2.1* (April 6th, 2006)
-
-* Be part of Rails 1.1.1
-
-
-*1.2.0* (March 27th, 2006)
-
-* Nil charset caused subject line to be improperly quoted in implicitly multipart messages #2662 [ehalvorsen+rails@runbox.com]
-
-* Parse content-type apart before using it so that sub-parts of the header can be set correctly #2918 [Jamis Buck]
-
-* Make custom headers work in subparts #4034 [elan@bluemandrill.com]
-
-* Template paths with dot chars in them no longer mess up implicit template selection for multipart messages #3332 [Chad Fowler]
-
-* Make sure anything with content-disposition of "attachment" is passed to the attachment presenter when parsing an email body [Jamis Buck]
-
-* Make sure TMail#attachments includes anything with content-disposition of "attachment", regardless of content-type [Jamis Buck]
-
-
-*1.1.5* (December 13th, 2005)
-
-* Become part of Rails 1.0
-
-
-*1.1.4* (December 7th, 2005)
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-* Stricter matching for implicitly multipart filenames excludes files ending in unsupported extensions (such as foo.rhtml.bak) and without a two-part content type (such as foo.text.rhtml or foo.text.really.plain.rhtml). #2398 [Dave Burt <dave@burt.id.au>, Jeremy Kemper]
-
-
-*1.1.3* (November 7th, 2005)
-
-* Allow Mailers to have custom initialize methods that set default instance variables for all mail actions #2563 [mrj@bigpond.net.au]
-
-
-*1.1.2* (October 26th, 2005)
-
-* Upgraded to Action Pack 1.10.2
-
-
-*1.1.1* (October 19th, 2005)
-
-* Upgraded to Action Pack 1.10.1
-
-
-*1.1.0* (October 16th, 2005)
-
-* Update and extend documentation (rdoc)
-
-* Minero Aoki made TMail available to Rails/ActionMailer under the MIT license (instead of LGPL) [RubyConf '05]
-
-* Austin Ziegler made Text::Simple available to Rails/ActionMailer under a MIT-like licens [See rails ML, subject "Text::Format Licence Exception" on Oct 15, 2005]
-
-* Fix vendor require paths to prevent files being required twice
-
-* Don't add charset to content-type header for a part that contains subparts (for AOL compatibility) #2013 [John Long]
-
-* Preserve underscores when unquoting message bodies #1930
-
-* Encode multibyte characters correctly #1894
-
-* Multipart messages specify a MIME-Version header automatically #2003 [John Long]
-
-* Add a unified render method to ActionMailer (delegates to ActionView::Base#render)
-
-* Move mailer initialization to a separate (overridable) method, so that subclasses may alter the various defaults #1727
-
-* Look at content-location header (if available) to determine filename of attachments #1670
-
-* ActionMailer::Base.deliver(email) had been accidentally removed, but was documented in the Rails book #1849
-
-* Fix problem with sendmail delivery where headers should be delimited by \n characters instead of \r\n, which confuses some mail readers #1742 [Kent Sibilev]
-
-
-*1.0.1* (11 July, 2005)
-
-* Bind to Action Pack 1.9.1
-
-
-*1.0.0* (6 July, 2005)
-
-* Avoid adding nil header values #1392
-
-* Better multipart support with implicit multipart/alternative and sorting of subparts [John Long]
-
-* Allow for nested parts in multipart mails #1570 [Flurin Egger]
-
-* Normalize line endings in outgoing mail bodies to "\n" #1536 [John Long]
-
-* Allow template to be explicitly specified #1448 [tuxie@dekadance.se]
-
-* Allow specific "multipart/xxx" content-type to be set on multipart messages #1412 [Flurin Egger]
-
-* Unquoted @ characters in headers are now accepted in spite of RFC 822 #1206
-
-* Helper support (borrowed from ActionPack)
-
-* Silently ignore Errno::EINVAL errors when converting text.
-
-* Don't cause an error when parsing an encoded attachment name #1340 [lon@speedymac.com]
-
-* Nested multipart message parts are correctly processed in TMail::Mail#body
-
-* BCC headers are removed when sending via SMTP #1402
-
-* Added 'content_type' accessor, to allow content type to be set on a per-message basis. content_type defaults to "text/plain".
-
-* Silently ignore Iconv::IllegalSequence errors when converting text #1341 [lon@speedymac.com]
-
-* Support attachments and multipart messages.
-
-* Added new accessors for the various mail properties.
-
-* Fix to only perform the charset conversion if a 'from' and a 'to' charset are given (make no assumptions about what the charset was) #1276 [Jamis Buck]
-
-* Fix attachments and content-type problems #1276 [Jamis Buck]
-
-* Fixed the TMail#body method to look at the content-transfer-encoding header and unquote the body according to the rules it specifies #1265 [Jamis Buck]
-
-* Added unquoting even if the iconv lib can't be loaded--in that case, only the charset conversion is skipped #1265 [Jamis Buck]
-
-* Added automatic decoding of base64 bodies #1214 [Jamis Buck]
-
-* Added that delivery errors are caught in a way so the mail is still returned whether the delivery was successful or not
-
-* Fixed that email address like "Jamis Buck, M.D." <wild.medicine@example.net> would cause the quoter to generate emails resulting in "bad address" errors from the mail server #1220 [Jamis Buck]
-
-
-*0.9.1* (20th April, 2005)
-
-* Depend on Action Pack 1.8.1
-
-
-*0.9.0* (19th April, 2005)
-
-* Added that deliver_* will now return the email that was sent
-
-* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck]
-
-* Fixed quoting for all address headers, not just to #955 [Jamis Buck]
-
-* Fixed unquoting of emails that doesn't have an explicit charset #1036 [wolfgang@stufenlos.net]
-
-
-*0.8.1* (27th March, 2005)
-
-* Fixed that if charset was found that the end of a mime part declaration TMail would throw an error #919 [lon@speedymac.com]
-
-* Fixed that TMail::Unquoter would fail to recognize quoting method if it was in lowercase #919 [lon@speedymac.com]
-
-* Fixed that TMail::Encoder would fail when it attempts to parse e-mail addresses which are encoded using something other than the messages encoding method #919 [lon@speedymac.com]
-
-* Added rescue for missing iconv library and throws warnings if subject/body is called on a TMail object without it instead
-
-
-*0.8.0* (22th March, 2005)
-
-* Added framework support for processing incoming emails with an Action Mailer class. See example in README.
-
-
-*0.7.1* (7th March, 2005)
-
-* Bind to newest Action Pack (1.5.1)
-
-
-*0.7.0* (24th February, 2005)
-
-* Added support for charsets for both subject and body. The default charset is now UTF-8 #673 [Jamis Buck]. Examples:
-
- def iso_charset(recipient)
- @recipients = recipient
- @subject = "testing iso charsets"
- @from = "system@loudthinking.com"
- @body = "Nothing to see here."
- @charset = "iso-8859-1"
- end
-
- def unencoded_subject(recipient)
- @recipients = recipient
- @subject = "testing unencoded subject"
- @from = "system@loudthinking.com"
- @body = "Nothing to see here."
- @encode_subject = false
- @charset = "iso-8859-1"
- end
-
-
-*0.6.1* (January 18th, 2005)
-
-* Fixed sending of emails to use Tmail#from not the deprecated Tmail#from_address
-
-
-*0.6* (January 17th, 2005)
-
-* Fixed that bcc and cc should be settable through @bcc and @cc -- not just @headers["Bcc"] and @headers["Cc"] #453 [Eric Hodel]
-
-* Fixed Action Mailer to be "warnings safe" so you can run with ruby -w and not get framework warnings #453 [Eric Hodel]
-
-
-*0.5*
-
-* Added access to custom headers, like cc, bcc, and reply-to #268 [Andreas Schwarz]. Example:
-
- def post_notification(recipients, post)
- @recipients = recipients
- @from = post.author.email_address_with_name
- @headers["bcc"] = SYSTEM_ADMINISTRATOR_EMAIL
- @headers["reply-to"] = "notifications@example.com"
- @subject = "[#{post.account.name} #{post.title}]"
- @body["post"] = post
- end
-
-*0.4* (5)
-
-* Consolidated the server configuration options into Base#server_settings= and expanded that with controls for authentication and more [Marten]
- NOTE: This is an API change that could potentially break your application if you used the old application form. Please do change!
-
-* Added Base#deliveries as an accessor for an array of emails sent out through that ActionMailer class when using the :test delivery option. [Jeremy Kemper]
-
-* Added Base#perform_deliveries= which can be set to false to turn off the actual delivery of the email through smtp or sendmail.
- This is especially useful for functional testing that shouldn't send off real emails, but still trigger delivery_* methods.
-
-* Added option to specify delivery method with Base#delivery_method=. Default is :smtp and :sendmail is currently the only other option.
- Sendmail is assumed to be present at "/usr/sbin/sendmail" if that option is used. [Kent Sibilev]
-
-* Dropped "include TMail" as it added to much baggage into the default namespace (like Version) [Chad Fowler]
-
-
-*0.3*
-
-* First release
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
new file mode 100644
index 0000000000..57af4ee6a4
--- /dev/null
+++ b/actionmailer/CHANGELOG.md
@@ -0,0 +1,449 @@
+## Rails 3.1.0 (August 30, 2011) ##
+
+* No changes
+
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* remove AM delegating register_observer and register_interceptor to Mail *Josh Kalderimis*
+
+
+* Rails 3.0.6 (April 5, 2011)
+
+* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 *Santiago Pastorino*
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* No changes.
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* No changes
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* No Changes, just a version bump.
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* subject is automatically looked up on I18n using mailer_name and action_name as scope as in t(".subject") *JK*
+
+* Changed encoding behaviour of mail, so updated tests in actionmailer and bumped mail version to 2.2.1 *ML*
+
+* Added ability to pass Proc objects to the defaults hash *ML*
+
+* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead *ML*
+
+* Lot of updates to various test cases that now work better with the new Mail and so have different expectations
+
+* Added interceptors and observers from Mail *ML*
+
+ ActionMailer::Base.register_interceptor calls Mail.register_interceptor
+ ActionMailer::Base.register_observer calls Mail.register_observer
+
+* Mail::Part now no longer has nil as a default charset, it is always set to something, and defaults to UTF-8
+
+* Added explict setting of charset in set_fields! method to make sure Mail has the user defined default
+
+* Removed quoting.rb and refactored for Mail to take responsibility of all quoting and auto encoding requirements for the header.
+
+* Fixed several tests which had incorrect encoding.
+
+* Changed all utf-8 to UTF-8 for consistency
+
+* Whole new API added with tests. See base.rb for full details. Old API is deprecated.
+
+* The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted
+
+* Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'}
+
+* ActionMailer::Base :default_implicit_parts_order now is in the sequence of the order you want, no reversing of ordering takes place. The default order now is text/plain, then text/enriched, then text/html and then any other part that is not one of these three.
+
+* Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc
+
+* Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want.
+* Mail::Message#set_content_type does not exist, it is simply Mail::Message#content_type
+
+* Every mail message gets a unique message_id unless you specify one, had to change all the tests that check for equality with expected.encoded == actual.encoded to first replace their message_ids with control values
+
+* Mail now has a proper concept of parts, remove the ActionMailer::Part and ActionMailer::PartContainer classes
+
+* Calling #encoded on any object returns it as a string ready to go into the output stream of an email, this means it includes the \r\n at the end of the lines and the object is pre-wrapped with \r\n\t if it is a header field. Also, the "encoded" value includes the field name if it is a header field.
+
+* Attachments are only the actual attachment, with filename etc. A part contains an attachment. The part has the content_type etc. So attachments.last.content_type is invalid. But parts.last.content_type
+
+* There is no idea of a "sub_head" in Mail. A part is just a Message with some extra functionality, so it just has a "header" like a normal mail message
+
+
+## 2.3.2 Final (March 15, 2009) ##
+
+* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 *Matt Jones*
+
+* Fixed RFC-2045 quoted-printable bug #1421 *squadette*
+
+* Fixed that no body charset would be set when there are attachments present #740 *Paweł Kondzior*
+
+
+## 2.2.1 RC2 (November 14th, 2008) ##
+
+* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 *Grant Hollingworth*
+
+
+## 2.2.0 RC1 (October 24th, 2008) ##
+
+* Add layout functionality to mailers *Pratik Naik*
+
+ Mailer layouts behaves just like controller layouts, except layout names need to
+ have '_mailer' postfix for them to be automatically picked up.
+
+
+## 2.1.0 (May 31st, 2008) ##
+
+* Fixed that a return-path header would be ignored #7572 *joost*
+
+* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 *iaddict, Tarmo Tänav*
+
+* Updated TMail to version 1.2.1 *Mikel Lindsaar*
+
+* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 *jamesgolick*
+
+
+## 2.0.2 (December 16th, 2007) ##
+
+* Included in Rails 2.0.2
+
+
+## 2.0.1 (December 7th, 2007) ##
+
+* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 *Rick Olson*
+
+ * Pass the template_root as an array as ActionView's view_path
+ * Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
+
+* Fixed that partials would be broken when using text.plain.erb as the extension #10130 *java*
+
+* Update README to use new smtp settings configuration API. Closes #10060 *psq*
+
+* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 *Zach Dennis*
+
+* Update TMail to v1.1.0. Use an updated version of TMail if available. *Mikel Lindsaar*
+
+* Introduce a new base test class for testing Mailers. ActionMailer::TestCase *Michael Koziarski*
+
+* Fix silent failure of rxml templates. #9879 *jstewart*
+
+* Fix attachment decoding when using the TMail C extension. #7861 *orangechicken*
+
+* Increase mail delivery test coverage. #8692 *Kamal Fariz Mahyuddin*
+
+* Register alternative template engines using ActionMailer::Base.register_template_extension('haml'). #7534 *cwd, Josh Peek*
+
+* Only load ActionController::UrlWriter if ActionController is present *Rick Olson*
+
+* Make sure parsed emails recognized attachments nested inside multipart parts. #6714 *Jamis Buck*
+
+* Allow mailer actions named send by using __send__ internally. #6467 *iGEL*
+
+* Add assert_emails and assert_no_emails to test the number of emails delivered. #6479 *Jonathan Viney*
+ # Assert total number of emails delivered:
+ assert_emails 0
+ ContactMailer.deliver_contact
+ assert_emails 1
+
+ # Assert number of emails delivered within a block:
+ assert_emails 1 do
+ post :signup, :name => 'Jonathan'
+ end
+
+
+## 1.3.3 (March 12th, 2007) ##
+
+* Depend on Action Pack 1.13.3
+
+
+## 1.3.2 (February 5th, 2007) ##
+
+* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. *Michael Koziarski*
+
+
+## 1.3.1 (January 16th, 2007) ##
+
+* Depend on Action Pack 1.13.1
+
+
+## 1.3.0 (January 16th, 2007) ##
+
+* Make mime version default to 1.0. closes #2323 *ror@andreas-s.net*
+
+* Make sure quoted-printable text is decoded correctly when only portions of the text are encoded. closes #3154. *jon@siliconcircus.com*
+
+* Make sure DOS newlines in quoted-printable text are normalized to unix newlines before unquoting. closes #4166 and #4452. *Jamis Buck*
+
+* Fixed that iconv decoding should catch InvalidEncoding #3153 *jon@siliconcircus.com*
+
+* Tighten rescue clauses. #5985 *james@grayproductions.net*
+
+* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. *David Heinemeier Hansson*
+
+* Replace Reloadable with Reloadable::Deprecated. *Nicholas Seckar*
+
+* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 *somekool@gmail.com*
+
+* Resolve action naming collision. #5520 *ssinghi@kreeti.com*
+
+* ActionMailer::Base documentation rewrite. Closes #4991 *Kevin Clark, Marcel Molina Jr.*
+
+* Replace alias method chaining with Module#alias_method_chain. *Marcel Molina Jr.*
+
+* Replace Ruby's deprecated append_features in favor of included. *Marcel Molina Jr.*
+
+* Correct spurious documentation example code which results in a SyntaxError. *Marcel Molina Jr.*
+
+
+## 1.2.1 (April 6th, 2006) ##
+
+* Be part of Rails 1.1.1
+
+
+## 1.2.0 (March 27th, 2006) ##
+
+* Nil charset caused subject line to be improperly quoted in implicitly multipart messages #2662 *ehalvorsen+rails@runbox.com*
+
+* Parse content-type apart before using it so that sub-parts of the header can be set correctly #2918 *Jamis Buck*
+
+* Make custom headers work in subparts #4034 *elan@bluemandrill.com*
+
+* Template paths with dot chars in them no longer mess up implicit template selection for multipart messages #3332 *Chad Fowler*
+
+* Make sure anything with content-disposition of "attachment" is passed to the attachment presenter when parsing an email body *Jamis Buck*
+
+* Make sure TMail#attachments includes anything with content-disposition of "attachment", regardless of content-type *Jamis Buck*
+
+
+## 1.1.5 (December 13th, 2005) ##
+
+* Become part of Rails 1.0
+
+
+## 1.1.4 (December 7th, 2005) ##
+
+* Rename Version constant to VERSION. #2802 *Marcel Molina Jr.*
+
+* Stricter matching for implicitly multipart filenames excludes files ending in unsupported extensions (such as foo.rhtml.bak) and without a two-part content type (such as foo.text.rhtml or foo.text.really.plain.rhtml). #2398 *Dave Burt <dave@burt.id.au>, Jeremy Kemper*
+
+
+## 1.1.3 (November 7th, 2005) ##
+
+* Allow Mailers to have custom initialize methods that set default instance variables for all mail actions #2563 *mrj@bigpond.net.au*
+
+
+## 1.1.2 (October 26th, 2005) ##
+
+* Upgraded to Action Pack 1.10.2
+
+
+## 1.1.1 (October 19th, 2005) ##
+
+* Upgraded to Action Pack 1.10.1
+
+
+## 1.1.0 (October 16th, 2005) ##
+
+* Update and extend documentation (rdoc)
+
+* Minero Aoki made TMail available to Rails/ActionMailer under the MIT license (instead of LGPL) *RubyConf '05*
+
+* Austin Ziegler made Text::Simple available to Rails/ActionMailer under a MIT-like licens *See rails ML, subject "Text::Format Licence Exception" on Oct 15, 2005*
+
+* Fix vendor require paths to prevent files being required twice
+
+* Don't add charset to content-type header for a part that contains subparts (for AOL compatibility) #2013 *John Long*
+
+* Preserve underscores when unquoting message bodies #1930
+
+* Encode multibyte characters correctly #1894
+
+* Multipart messages specify a MIME-Version header automatically #2003 *John Long*
+
+* Add a unified render method to ActionMailer (delegates to ActionView::Base#render)
+
+* Move mailer initialization to a separate (overridable) method, so that subclasses may alter the various defaults #1727
+
+* Look at content-location header (if available) to determine filename of attachments #1670
+
+* ActionMailer::Base.deliver(email) had been accidentally removed, but was documented in the Rails book #1849
+
+* Fix problem with sendmail delivery where headers should be delimited by \n characters instead of \r\n, which confuses some mail readers #1742 *Kent Sibilev*
+
+
+## 1.0.1 (11 July, 2005) ##
+
+* Bind to Action Pack 1.9.1
+
+
+## 1.0.0 (6 July, 2005) ##
+
+* Avoid adding nil header values #1392
+
+* Better multipart support with implicit multipart/alternative and sorting of subparts *John Long*
+
+* Allow for nested parts in multipart mails #1570 *Flurin Egger*
+
+* Normalize line endings in outgoing mail bodies to "\n" #1536 *John Long*
+
+* Allow template to be explicitly specified #1448 *tuxie@dekadance.se*
+
+* Allow specific "multipart/xxx" content-type to be set on multipart messages #1412 *Flurin Egger*
+
+* Unquoted @ characters in headers are now accepted in spite of RFC 822 #1206
+
+* Helper support (borrowed from ActionPack)
+
+* Silently ignore Errno::EINVAL errors when converting text.
+
+* Don't cause an error when parsing an encoded attachment name #1340 *lon@speedymac.com*
+
+* Nested multipart message parts are correctly processed in TMail::Mail#body
+
+* BCC headers are removed when sending via SMTP #1402
+
+* Added 'content_type' accessor, to allow content type to be set on a per-message basis. content_type defaults to "text/plain".
+
+* Silently ignore Iconv::IllegalSequence errors when converting text #1341 *lon@speedymac.com*
+
+* Support attachments and multipart messages.
+
+* Added new accessors for the various mail properties.
+
+* Fix to only perform the charset conversion if a 'from' and a 'to' charset are given (make no assumptions about what the charset was) #1276 *Jamis Buck*
+
+* Fix attachments and content-type problems #1276 *Jamis Buck*
+
+* Fixed the TMail#body method to look at the content-transfer-encoding header and unquote the body according to the rules it specifies #1265 *Jamis Buck*
+
+* Added unquoting even if the iconv lib can't be loaded--in that case, only the charset conversion is skipped #1265 *Jamis Buck*
+
+* Added automatic decoding of base64 bodies #1214 *Jamis Buck*
+
+* Added that delivery errors are caught in a way so the mail is still returned whether the delivery was successful or not
+
+* Fixed that email address like "Jamis Buck, M.D." <wild.medicine@example.net> would cause the quoter to generate emails resulting in "bad address" errors from the mail server #1220 *Jamis Buck*
+
+
+## 0.9.1 (20th April, 2005) ##
+
+* Depend on Action Pack 1.8.1
+
+
+## 0.9.0 (19th April, 2005) ##
+
+* Added that deliver_* will now return the email that was sent
+
+* Added that quoting to UTF-8 only happens if the characters used are in that range #955 *Jamis Buck*
+
+* Fixed quoting for all address headers, not just to #955 *Jamis Buck*
+
+* Fixed unquoting of emails that doesn't have an explicit charset #1036 *wolfgang@stufenlos.net*
+
+
+## 0.8.1 (27th March, 2005) ##
+
+* Fixed that if charset was found that the end of a mime part declaration TMail would throw an error #919 *lon@speedymac.com*
+
+* Fixed that TMail::Unquoter would fail to recognize quoting method if it was in lowercase #919 *lon@speedymac.com*
+
+* Fixed that TMail::Encoder would fail when it attempts to parse e-mail addresses which are encoded using something other than the messages encoding method #919 *lon@speedymac.com*
+
+* Added rescue for missing iconv library and throws warnings if subject/body is called on a TMail object without it instead
+
+
+## 0.8.0 (22th March, 2005) ##
+
+* Added framework support for processing incoming emails with an Action Mailer class. See example in README.
+
+
+## 0.7.1 (7th March, 2005) ##
+
+* Bind to newest Action Pack (1.5.1)
+
+
+## 0.7.0 (24th February, 2005) ##
+
+* Added support for charsets for both subject and body. The default charset is now UTF-8 #673 [Jamis Buck]. Examples:
+
+ def iso_charset(recipient)
+ @recipients = recipient
+ @subject = "testing iso charsets"
+ @from = "system@loudthinking.com"
+ @body = "Nothing to see here."
+ @charset = "iso-8859-1"
+ end
+
+ def unencoded_subject(recipient)
+ @recipients = recipient
+ @subject = "testing unencoded subject"
+ @from = "system@loudthinking.com"
+ @body = "Nothing to see here."
+ @encode_subject = false
+ @charset = "iso-8859-1"
+ end
+
+
+## 0.6.1 (January 18th, 2005) ##
+
+* Fixed sending of emails to use Tmail#from not the deprecated Tmail#from_address
+
+
+## 0.6 (January 17th, 2005) ##
+
+* Fixed that bcc and cc should be settable through @bcc and @cc -- not just @headers["Bcc"] and @headers["Cc"] #453 *Eric Hodel*
+
+* Fixed Action Mailer to be "warnings safe" so you can run with ruby -w and not get framework warnings #453 *Eric Hodel*
+
+
+## 0.5 ##
+
+* Added access to custom headers, like cc, bcc, and reply-to #268 [Andreas Schwarz]. Example:
+
+ def post_notification(recipients, post)
+ @recipients = recipients
+ @from = post.author.email_address_with_name
+ @headers["bcc"] = SYSTEM_ADMINISTRATOR_EMAIL
+ @headers["reply-to"] = "notifications@example.com"
+ @subject = "[#{post.account.name} #{post.title}]"
+ @body["post"] = post
+ end
+
+## 0.4 (5) ##
+
+* Consolidated the server configuration options into Base#server_settings= and expanded that with controls for authentication and more *Marten*
+ NOTE: This is an API change that could potentially break your application if you used the old application form. Please do change!
+
+* Added Base#deliveries as an accessor for an array of emails sent out through that ActionMailer class when using the :test delivery option. *Jeremy Kemper*
+
+* Added Base#perform_deliveries= which can be set to false to turn off the actual delivery of the email through smtp or sendmail.
+ This is especially useful for functional testing that shouldn't send off real emails, but still trigger delivery_* methods.
+
+* Added option to specify delivery method with Base#delivery_method=. Default is :smtp and :sendmail is currently the only other option.
+ Sendmail is assumed to be present at "/usr/sbin/sendmail" if that option is used. *Kent Sibilev*
+
+* Dropped "include TMail" as it added to much baggage into the default namespace (like Version) *Chad Fowler*
+
+
+## 0.3 ##
+
+* First release
diff --git a/actionmailer/MIT-LICENSE b/actionmailer/MIT-LICENSE
index 7ad1051066..810daf856c 100644
--- a/actionmailer/MIT-LICENSE
+++ b/actionmailer/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2011 David Heinemeier Hansson
+Copyright (c) 2004-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index dc74b590f7..4c2516db59 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -42,7 +42,7 @@ So the corresponding body template for the method above could look like this:
Thank you for signing up!
-And if the recipient was given as "david@loudthinking.com", the email
+If the recipient was given as "david@loudthinking.com", the email
generated would look like this:
Date: Mon, 25 Jan 2010 22:48:09 +1100
@@ -62,7 +62,7 @@ generated would look like this:
Thank you for signing up!
In previous version of Rails you would call <tt>create_method_name</tt> and
-<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface, you
+<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface - you
simply call the method and optionally call +deliver+ on the return value.
Calling the method returns a Mail Message object:
@@ -76,7 +76,7 @@ Or you can just chain the methods together like:
== Setting defaults
-It is possible to set default values that will be used in every method in your Action Mailer class. To implement this functionality, you just call the public class method <tt>default</tt> which you get for free from ActionMailer::Base. This method accepts a Hash as the parameter. You can use any of the headers e-mail messages has, like <tt>:from</tt> as the key. You can also pass in a string as the key, like "Content-Type", but Action Mailer does this out of the box for you, so you won't need to worry about that. Finally it is also possible to pass in a Proc that will get evaluated when it is needed.
+It is possible to set default values that will be used in every method in your Action Mailer class. To implement this functionality, you just call the public class method <tt>default</tt> which you get for free from ActionMailer::Base. This method accepts a Hash as the parameter. You can use any of the headers e-mail messages has, like <tt>:from</tt> as the key. You can also pass in a string as the key, like "Content-Type", but Action Mailer does this out of the box for you, so you won't need to worry about that. Finally, it is also possible to pass in a Proc that will get evaluated when it is needed.
Note that every value you set with this method will get over written if you use the same key in your mailer method.
@@ -148,7 +148,9 @@ Source code can be downloaded as part of the Rails project on GitHub
== License
-Action Mailer is released under the MIT license.
+Action Mailer is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index e7d8ee299d..8f5aeb9603 100755
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -11,6 +11,7 @@ Rake::TestTask.new { |t|
t.libs << "test"
t.pattern = 'test/**/*_test.rb'
t.warning = true
+ t.verbose = true
}
namespace :test do
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index d7f450f751..c605f1ff04 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -6,16 +6,16 @@ Gem::Specification.new do |s|
s.version = version
s.summary = 'Email composition, delivery, and receiving framework (part of Rails).'
s.description = 'Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
+ s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
s.require_path = 'lib'
s.requirements << 'none'
s.add_dependency('actionpack', version)
- s.add_dependency('mail', '~> 2.3.0')
+ s.add_dependency('mail', '~> 2.4.1')
end
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 9bd73dd740..1045dd58ef 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2011 David Heinemeier Hansson
+# Copyright (c) 2004-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -31,7 +31,6 @@ require 'action_mailer/version'
# Common Active Support usage in Action Mailer
require 'active_support/core_ext/class'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/array/uniq_by'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections'
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 8f2c567e3e..1800ff5839 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -1,8 +1,6 @@
require 'mail'
require 'action_mailer/collector'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/proc'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/except'
require 'action_mailer/log_subscriber'
@@ -122,8 +120,8 @@ module ActionMailer #:nodoc:
#
# <%= users_url(:host => "example.com") %>
#
- # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
- # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
+ # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
+ # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
# have no concept of a current URL from which to determine a relative path.
#
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
@@ -132,7 +130,7 @@ module ActionMailer #:nodoc:
# config.action_mailer.default_url_options = { :host => "example.com" }
#
# When you decide to set a default <tt>:host</tt> for your mailers, then you need to make sure to use the
- # <tt>:only_path => false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
+ # <tt>:only_path => false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
# will generate relative URLs by default when a <tt>:host</tt> option isn't explicitly provided, passing
# <tt>:only_path => false</tt> will ensure that absolute URLs are generated.
#
@@ -149,8 +147,8 @@ module ActionMailer #:nodoc:
#
# = Multipart Emails
#
- # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
- # multipart templates, where each template is named after the name of the action, followed by the content
+ # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
+ # multipart templates, where each template is named after the name of the action, followed by the content
# type. Each such detected template will be added as a separate part to the message.
#
# For example, if the following templates exist:
@@ -333,7 +331,7 @@ module ActionMailer #:nodoc:
include AbstractController::Translation
include AbstractController::AssetPaths
- self.protected_instance_variables = %w(@_action_has_layout)
+ self.protected_instance_variables = [:@_action_has_layout]
helper ActionMailer::MailHelper
@@ -410,7 +408,7 @@ module ActionMailer #:nodoc:
# and passing a Mail::Message will do nothing except tell the logger you sent the email.
def deliver_mail(mail) #:nodoc:
ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
- self.set_payload_for_mail(payload, mail)
+ set_payload_for_mail(payload, mail)
yield # Let Mail do the delivery actions
end
end
@@ -603,9 +601,6 @@ module ActionMailer #:nodoc:
# end
#
def mail(headers={}, &block)
- # Guard flag to prevent both the old and the new API from firing
- # Should be removed when old API is removed
- @mail_was_called = true
m = @_message
# At the beginning, do not consider class default for parts order neither content_type
@@ -613,8 +608,9 @@ module ActionMailer #:nodoc:
parts_order = headers[:parts_order]
# Call all the procs (if any)
- default_values = self.class.default.merge(self.class.default) do |k,v|
- v.respond_to?(:call) ? v.bind(self).call : v
+ class_default = self.class.default
+ default_values = class_default.merge(class_default) do |k,v|
+ v.respond_to?(:to_proc) ? instance_eval(&v) : v
end
# Handle defaults
@@ -668,7 +664,7 @@ module ActionMailer #:nodoc:
end
end
- # Translates the +subject+ using Rails I18n class under <tt>[:actionmailer, mailer_scope, action_name]</tt> scope.
+ # Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
# If it does not find a translation for the +subject+ under the specified scope it will default to a
# humanized version of the <tt>action_name</tt>.
def default_i18n_subject #:nodoc:
@@ -707,8 +703,8 @@ module ActionMailer #:nodoc:
end
def each_template(paths, name, &block) #:nodoc:
- templates = lookup_context.find_all(name, Array.wrap(paths))
- templates.uniq_by { |t| t.formats }.each(&block)
+ templates = lookup_context.find_all(name, Array(paths))
+ templates.uniq { |t| t.formats }.each(&block)
end
def create_parts_from_responses(m, responses) #:nodoc:
diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb
index 7ba57b19e0..a6c163832e 100644
--- a/actionmailer/lib/action_mailer/log_subscriber.rb
+++ b/actionmailer/lib/action_mailer/log_subscriber.rb
@@ -1,9 +1,7 @@
-require 'active_support/core_ext/array/wrap'
-
module ActionMailer
class LogSubscriber < ActiveSupport::LogSubscriber
def deliver(event)
- recipients = Array.wrap(event.payload[:to]).join(', ')
+ recipients = Array(event.payload[:to]).join(', ')
info("\nSent mail to #{recipients} (%1.fms)" % event.duration)
debug(event.payload[:mail])
end
@@ -19,4 +17,4 @@ module ActionMailer
end
end
-ActionMailer::LogSubscriber.attach_to :action_mailer \ No newline at end of file
+ActionMailer::LogSubscriber.attach_to :action_mailer
diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb
index 6f22adc479..72314d81ad 100644
--- a/actionmailer/lib/action_mailer/mail_helper.rb
+++ b/actionmailer/lib/action_mailer/mail_helper.rb
@@ -3,9 +3,9 @@ module ActionMailer
# Uses Text::Format to take the text and format it, indented two spaces for
# each line, and wrapped at 72 columns.
def block_format(text)
- formatted = text.split(/\n\r\n/).collect { |paragraph|
+ formatted = text.split(/\n\r?\n/).collect { |paragraph|
format_paragraph(paragraph)
- }.join("\n")
+ }.join("\n\n")
# Make list points stand on their own line
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
@@ -41,7 +41,7 @@ module ActionMailer
sentences = [[]]
text.split.each do |word|
- if (sentences.last + [word]).join(' ').length > len
+ if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
sentences << [word]
else
sentences.last << word
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 444754d0e9..5c03a29f0f 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -19,8 +19,9 @@ module ActionMailer
options.stylesheets_dir ||= paths["public/stylesheets"].first
# make sure readers methods get compiled
- options.asset_path ||= app.config.asset_path
- options.asset_host ||= app.config.asset_host
+ options.asset_path ||= app.config.asset_path
+ options.asset_host ||= app.config.asset_host
+ options.relative_url_root ||= app.config.relative_url_root
ActiveSupport.on_load(:action_mailer) do
include AbstractController::UrlFor
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index c4de029694..529140dfad 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -1,3 +1,4 @@
+require 'active_support/test_case'
require 'active_support/core_ext/class/attribute'
module ActionMailer
@@ -15,6 +16,12 @@ module ActionMailer
include TestHelper
+ included do
+ class_attribute :_mailer_class
+ setup :initialize_test_deliveries
+ setup :set_expected_mail
+ end
+
module ClassMethods
def tests(mailer)
case mailer
@@ -42,8 +49,6 @@ module ActionMailer
end
end
- module InstanceMethods
-
protected
def initialize_test_deliveries
@@ -71,16 +76,8 @@ module ActionMailer
def read_fixture(action)
IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
end
- end
-
- included do
- class_attribute :_mailer_class
- setup :initialize_test_deliveries
- setup :set_expected_mail
- end
end
include Behavior
-
end
end
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 5beab87ad2..7204822395 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -1,7 +1,5 @@
module ActionMailer
module TestHelper
- extend ActiveSupport::Concern
-
# Asserts that the number of emails sent matches the given number.
#
# def test_emails
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index b74ba3cb0d..0cb5dc6d64 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -1,7 +1,7 @@
module ActionMailer
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/actionmailer/lib/rails/generators/mailer/USAGE b/actionmailer/lib/rails/generators/mailer/USAGE
index 448ddbd5df..9f1d6b182e 100644
--- a/actionmailer/lib/rails/generators/mailer/USAGE
+++ b/actionmailer/lib/rails/generators/mailer/USAGE
@@ -1,6 +1,6 @@
Description:
============
- Stubs out a new mailer and its views. Pass the mailer name, either
+ Stubs out a new mailer and its views. Passes the mailer name, either
CamelCased or under_scored, and an optional list of emails as arguments.
This generates a mailer class in app/mailers and invokes your template
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 0b076e1ff9..3a519253f9 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -13,20 +13,17 @@ end
require 'active_support/core_ext/kernel/reporting'
-require 'active_support/core_ext/string/encoding'
-if "ruby".encoding_aware?
- # These are the normal settings that will be set up by Railties
- # TODO: Have these tests support other combinations of these values
- silence_warnings do
- Encoding.default_internal = "UTF-8"
- Encoding.default_external = "UTF-8"
- end
+# These are the normal settings that will be set up by Railties
+# TODO: Have these tests support other combinations of these values
+silence_warnings do
+ Encoding.default_internal = "UTF-8"
+ Encoding.default_external = "UTF-8"
end
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
-require 'test/unit'
+require 'minitest/autorun'
require 'action_mailer'
require 'action_mailer/test_case'
diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb
index b24eca5fbb..696a9f1174 100644
--- a/actionmailer/test/asset_host_test.rb
+++ b/actionmailer/test/asset_host_test.rb
@@ -9,7 +9,7 @@ class AssetHostMailer < ActionMailer::Base
end
end
-class AssetHostTest < Test::Unit::TestCase
+class AssetHostTest < ActiveSupport::TestCase
def setup
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 9fdd0e1ced..65550ab505 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -107,7 +107,7 @@ class BaseTest < ActiveSupport::TestCase
assert_equal(1, email.attachments.length)
assert_equal('invoice.jpg', email.attachments[0].filename)
expected = "\312\213\254\232)b"
- expected.force_encoding(Encoding::BINARY) if '1.9'.respond_to?(:force_encoding)
+ expected.force_encoding(Encoding::BINARY)
assert_equal expected, email.attachments['invoice.jpg'].decoded
end
@@ -116,7 +116,7 @@ class BaseTest < ActiveSupport::TestCase
assert_equal(1, email.attachments.length)
assert_equal('invoice.jpg', email.attachments[0].filename)
expected = "\312\213\254\232)b"
- expected.force_encoding(Encoding::BINARY) if '1.9'.respond_to?(:force_encoding)
+ expected.force_encoding(Encoding::BINARY)
assert_equal expected, email.attachments['invoice.jpg'].decoded
end
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index 17e9c82045..d8a73e6c46 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -22,6 +22,14 @@ class HelperMailer < ActionMailer::Base
end
end
+ def use_format_paragraph_with_long_first_word
+ @text = "Antidisestablishmentarianism is very long."
+
+ mail_with_defaults do |format|
+ format.html { render(:inline => "<%= format_paragraph @text, 10, 1 %>") }
+ end
+ end
+
def use_mailer
mail_with_defaults do |format|
format.html { render(:inline => "<%= mailer.message.subject %>") }
@@ -34,6 +42,23 @@ class HelperMailer < ActionMailer::Base
end
end
+ def use_block_format
+ @text = <<-TEXT
+This is the
+first paragraph.
+
+The second
+ paragraph.
+
+* item1 * item2
+ * item3
+ TEXT
+
+ mail_with_defaults do |format|
+ format.html { render(:inline => "<%= block_format @text %>") }
+ end
+ end
+
protected
def mail_with_defaults(&block)
@@ -63,5 +88,24 @@ class MailerHelperTest < ActionMailer::TestCase
mail = HelperMailer.use_format_paragraph
assert_match " But soft! What\r\n light through\r\n yonder window\r\n breaks?", mail.body.encoded
end
+
+ def test_use_format_paragraph_with_long_first_word
+ mail = HelperMailer.use_format_paragraph_with_long_first_word
+ assert_equal " Antidisestablishmentarianism\r\n is very\r\n long.", mail.body.encoded
+ end
+
+ def test_use_block_format
+ mail = HelperMailer.use_block_format
+ expected = <<-TEXT
+ This is the first paragraph.
+
+ The second paragraph.
+
+ * item1
+ * item2
+ * item3
+ TEXT
+ assert_equal expected.gsub("\n", "\r\n"), mail.body.encoded
+ end
end
diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb
index def8da81b8..71e93c29f1 100644
--- a/actionmailer/test/mail_layout_test.rb
+++ b/actionmailer/test/mail_layout_test.rb
@@ -43,7 +43,7 @@ class ExplicitLayoutMailer < ActionMailer::Base
end
end
-class LayoutMailerTest < Test::Unit::TestCase
+class LayoutMailerTest < ActiveSupport::TestCase
def setup
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
deleted file mode 100644
index a7a47bf930..0000000000
--- a/actionpack/CHANGELOG
+++ /dev/null
@@ -1,5689 +0,0 @@
-*Rails 3.2.0 (unreleased)*
-
-* Responders now return 204 No Content for API requests without a response body (as in the new scaffold) [José Valim]
-
-* Added ActionDispatch::RequestId middleware that'll make a unique X-Request-Id header available to the response and enables the ActionDispatch::Request#uuid method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog [DHH]
-
-* Limit the number of options for select_year to 1000.
-
- Pass the :max_years_allowed option to set your own limit.
-
- [Libo Cannici]
-
-* Passing formats or handlers to render :template and friends is deprecated. For example: [Nick Sutterer & José Valim]
-
- render :template => "foo.html.erb"
-
- Instead, you can provide :handlers and :formats directly as option:
-
- render :template => "foo", :formats => [:html, :js], :handlers => :erb
-
-* Changed log level of warning for missing CSRF token from :debug to :warn. [Mike Dillon]
-
-* content_tag_for and div_for can now take the collection of records. It will also yield the record as the first argument if you set a receiving argument in your block [Prem Sichanugrist]
-
- So instead of having to do this:
-
- @items.each do |item|
- content_tag_for(:li, item) do
- Title: <%= item.title %>
- end
- end
-
- You can now do this:
-
- content_tag_for(:li, @items) do |item|
- Title: <%= item.title %>
- end
-
-* send_file now guess the mime type [Esad Hajdarevic]
-
-* Mime type entries for PDF, ZIP and other formats were added [Esad Hajdarevic]
-
-* Generate hidden input before select with :multiple option set to true.
- This is useful when you rely on the fact that when no options is set,
- the state of select will be sent to rails application. Without hidden field
- nothing is sent according to HTML spec [Bogdan Gusiev]
-
-* Refactor ActionController::TestCase cookies [Andrew White]
-
- Assigning cookies for test cases should now use cookies[], e.g:
-
- cookies[:email] = 'user@example.com'
- get :index
- assert_equal 'user@example.com', cookies[:email]
-
- To clear the cookies, use clear, e.g:
-
- cookies.clear
- get :index
- assert_nil cookies[:email]
-
- We now no longer write out HTTP_COOKIE and the cookie jar is
- persistent between requests so if you need to manipulate the environment
- for your test you need to do it before the cookie jar is created.
-
-
-*Rails 3.1.1 (unreleased)*
-
-* javascript_path and stylesheet_path now refer to /assets if asset pipelining
-is on. [Santiago Pastorino]
-
-* button_to support form option. Now you're able to pass for example
-'data-type' => 'json'. [ihower]
-
-* image_path and image_tag should use /assets if asset pipelining is turned
-on. Closes #3126 [Santiago Pastorino and christos]
-
-* Avoid use of existing precompiled assets during rake assets:precompile run.
-Closes #3119 [Guillermo Iguaran]
-
-* Copy assets to nondigested filenames too [Santiago Pastorino]
-
-* Give precedence to `config.digest = false` over the existence of
-manifest.yml asset digests [christos]
-
-* escape options for the stylesheet_link_tag method [Alexey Vakhov]
-
-* Re-launch assets:precompile task using (Rake.)ruby instead of Kernel.exec so
-it works on Windows [cablegram]
-
-* env var passed to process shouldn't be modified in process method. [Santiago
-Pastorino]
-
-* `rake assets:precompile` loads the application but does not initialize
-it.
-
- To the app developer, this means configuration add in
- config/initializers/* will not be executed.
-
- Plugins developers need to special case their initializers that are
- meant to be run in the assets group by adding :group => :assets. [José Valim]
-
-* Sprockets uses config.assets.prefix for asset_path [asee]
-
-* FileStore key_file_path properly limit filenames to 255 characters. [phuibonhoa]
-
-* Fix Hash#to_query edge case with html_safe strings. [brainopia]
-
-* Allow asset tag helper methods to accept :digest => false option in order to completely avoid the digest generation.
-Useful for linking assets from static html files or from emails when the user
-could probably look at an older html email with an older asset. [Santiago Pastorino]
-
-* Don't mount Sprockets server at config.assets.prefix if config.assets.compile is false. [Mark J. Titorenko]
-
-* Set relative url root in assets when controller isn't available for Sprockets (eg. Sass files using asset_path). Fixes #2435 [Guillermo Iguaran]
-
-* Fix basic auth credential generation to not make newlines. GH #2882
-
-* Fixed the behavior of asset pipeline when config.assets.digest and config.assets.compile are false and requested asset isn't precompiled.
- Before the requested asset were compiled anyway ignoring that the config.assets.compile flag is false. [Guillermo Iguaran]
-
-* CookieJar is now Enumerable. Fixes #2795
-
-* Fixed AssetNotPrecompiled error raised when rake assets:precompile is compiling certain .erb files. See GH #2763 #2765 #2805 [Guillermo Iguaran]
-
-* Manifest is correctly placed in assets path when default assets prefix is changed. Fixes #2776 [Guillermo Iguaran]
-
-* Fixed stylesheet_link_tag and javascript_include_tag to respect additional options passed by the users when debug is on. [Guillermo Iguaran]
-
-
-*Rails 3.1.0 (August 30, 2011)*
-
-* Param values are `paramified` in controller tests. [David Chelimsky]
-
-* x_sendfile_header now defaults to nil and config/environments/production.rb doesn't set a particular value for it. This allows servers to set it through X-Sendfile-Type. [Santiago Pastorino]
-
-* The submit form helper does not generate an id "object_name_id" anymore. [fbrusatti]
-
-* Make sure respond_with with :js tries to render a template in all cases [José Valim]
-
-* json_escape will now return a SafeBuffer string if it receives SafeBuffer string [tenderlove]
-
-* Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string [Prem Sichanugrist]
-
-* Fix escape_js to work correctly with the new SafeBuffer restriction [Paul Gallagher]
-
-* Brought back alternative convention for namespaced models in i18n [thoefer]
-
- Now the key can be either "namespace.model" or "namespace/model" until further deprecation.
-
-* It is prohibited to perform a in-place SafeBuffer mutation [tenderlove]
-
- The old behavior of SafeBuffer allowed you to mutate string in place via
- method like `sub!`. These methods can add unsafe strings to a safe buffer,
- and the safe buffer will continue to be marked as safe.
-
- An example problem would be something like this:
-
- <%= link_to('hello world', @user).sub!(/hello/, params[:xss]) %>
-
- In the above example, an untrusted string (`params[:xss]`) is added to the
- safe buffer returned by `link_to`, and the untrusted content is successfully
- sent to the client without being escaped. To prevent this from happening
- `sub!` and other similar methods will now raise an exception when they are called on a safe buffer.
-
- In addition to the in-place versions, some of the versions of these methods which return a copy of the string will incorrectly mark strings as safe. For example:
-
- <%= link_to('hello world', @user).sub(/hello/, params[:xss]) %>
-
- The new versions will now ensure that *all* strings returned by these methods on safe buffers are marked unsafe.
-
- You can read more about this change in http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2e516e7acc96c4fb
-
-* Warn if we cannot verify CSRF token authenticity [José Valim]
-
-* Allow AM/PM format in datetime selectors [Aditya Sanghi]
-
-* Only show dump of regular env methods on exception screen (not all the rack crap) [DHH]
-
-* auto_link has been removed with no replacement. If you still use auto_link
- please install the rails_autolink gem:
- http://github.com/tenderlove/rails_autolink
-
- [tenderlove]
-
-* Added streaming support, you can enable it with: [José Valim]
-
- class PostsController < ActionController::Base
- stream :only => :index
- end
-
- Please read the docs at `ActionController::Streaming` for more information.
-
-* Added `ActionDispatch::Request.ignore_accept_header` to ignore accept headers and only consider the format given as parameter [José Valim]
-
-* Created `ActionView::Renderer` and specified an API for `ActionView::Context`, check those objects for more information [José Valim]
-
-* Added `ActionController::ParamsWrapper` to wrap parameters into a nested hash, and will be turned on for JSON request in new applications by default [Prem Sichanugrist]
-
- This can be customized by setting `ActionController::Base.wrap_parameters` in `config/initializer/wrap_parameters.rb`
-
-* RJS has been extracted out to a gem. [fxn]
-
-* Implicit actions named not_implemented can be rendered. [Santiago Pastorino]
-
-* Wildcard route will always match the optional format segment by default. [Prem Sichanugrist]
-
- For example if you have this route:
-
- match '*pages' => 'pages#show'
-
- by requesting '/foo/bar.json', your `params[:pages]` will be equals to "foo/bar" with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `:format => false` like this:
-
- match '*pages' => 'pages#show', :format => false
-
-* Added Base.http_basic_authenticate_with to do simple http basic authentication with a single class method call [DHH]
-
- class PostsController < ApplicationController
- USER_NAME, PASSWORD = "dhh", "secret"
-
- before_filter :authenticate, :except => [ :index ]
-
- def index
- render :text => "Everyone can see me!"
- end
-
- def edit
- render :text => "I'm only accessible if you know the password"
- end
-
- private
- def authenticate
- authenticate_or_request_with_http_basic do |user_name, password|
- user_name == USER_NAME && password == PASSWORD
- end
- end
- end
-
- ..can now be written as
-
- class PostsController < ApplicationController
- http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
-
- def index
- render :text => "Everyone can see me!"
- end
-
- def edit
- render :text => "I'm only accessible if you know the password"
- end
- end
-
-* Allow you to add `force_ssl` into controller to force browser to transfer data via HTTPS protocol on that particular controller. You can also specify `:only` or `:except` to specific it to particular action. [DHH and Prem Sichanugrist]
-
-* Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash [DHH]
-
- form_for(@post, remote: true, method: :delete) instead of form_for(@post, remote: true, html: { method: :delete })
-
-* Make JavaScriptHelper#j() an alias for JavaScriptHelper#escape_javascript() -- note this then supersedes the Object#j() method that the JSON gem adds within templates using the JavaScriptHelper [DHH]
-
-* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. [Prem Sichanugrist, fxn]
-
-* URL parameters which return false for to_param now appear in the query string (previously they were removed) [Andrew White]
-
-* URL parameters which return nil for to_param are now removed from the query string [Andrew White]
-
-* ActionDispatch::MiddlewareStack now uses composition over inheritance. It is
-no longer an array which means there may be methods missing that were not
-tested.
-
-* Add an :authenticity_token option to form_tag for custom handling or to omit the token (pass :authenticity_token => false). [Jakub Kuźma, Igor Wiedler]
-
-* HTML5 button_tag helper. [Rizwan Reza]
-
-* Template lookup now searches further up in the inheritance chain. [Artemave]
-
-* Brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not. [Piotr Sarnacki]
-
-* url_for and named url helpers now accept :subdomain and :domain as options, [Josh Kalderimis]
-
-* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). [Josh Kalderimis]
-
-* Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). [Piotr Sarnacki]
-
-* Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options:
-
- tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)})
- # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
-
- Keys are dasherized. Values are JSON-encoded, except for strings and symbols. [Stephen Celis]
-
-* Deprecate old template handler API. The new API simply requires a template handler to respond to call. [José Valim]
-
-* :rhtml and :rxml were finally removed as template handlers. [José Valim]
-
-* Moved etag responsibility from ActionDispatch::Response to the middleware stack. [José Valim]
-
-* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. [José Valim]
-
-* file_field automatically adds :multipart => true to the enclosing form. [Santiago Pastorino]
-
-* Renames csrf_meta_tag -> csrf_meta_tags, and aliases csrf_meta_tag for backwards compatibility. [fxn]
-
-* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. [Yehuda Katz, Carl Lerche]
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-*No changes.
-
-
-*Rails 3.0.6 (April 5, 2011)
-
-* Fixed XSS vulnerability in `auto_link`. `auto_link` no longer marks input as
- html safe. Please make sure that calls to auto_link() are wrapped in a
- sanitize(), or a raw() depending on the type of input passed to auto_link().
- For example:
-
- <%= sanitize(auto_link(some_user_input)) %>
-
- Thanks to Torben Schulz for reporting this. The fix can be found here:
- 61ee3449674c591747db95f9b3472c5c3bd9e84d
-
-* Fixes the output of `rake routes` to be correctly match to the behavior of the application, as the regular expression used to match the path is greedy and won't capture the format part by default [Prem Sichanugrist]
-
-* Fixes an issue with number_to_human when converting values which are less than 1 but greater than -1 [Josh Kalderimis]
-
-* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. [Prem Sichanugrist, fxn]
-
-* URL parameters which return nil for to_param are now removed from the query string [Andrew White]
-
-* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 [Santiago Pastorino]
-
-* Make TranslationHelper#translate use the :rescue_format option in I18n 0.5.0 [Sven Fuchs]
-
-* Fix regression: javascript_include_tag shouldn't raise if you register an expansion key with nil or [] value [Santiago Pastorino]
-
-* Fix Action caching bug where an action that has a non-cacheable response always renders a nil response body. It now correctly renders the response body. [Cheah Chu Yeow]
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* When ActiveRecord::Base objects are sent to predicate methods, the id of the object should be sent to ARel, not the ActiveRecord::Base object.
-
-* :constraints routing should only do sanity checks against regular expressions. String arguments are OK.
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* The helper number_to_currency accepts a new :negative_format option to be able to configure how to render negative amounts. [Don Wilson]
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* No Changes, just a version bump.
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* password_field renders with nil value by default making the use of passwords secure by default, if you want to render you should do for instance f.password_field(:password, :value => @user.password) [Santiago Pastorino]
-
-* Symbols and strings in routes should yield the same behavior. Note this may break existing apps that were using symbols with the new routes API. [José Valim]
-
-* Add clear_helpers as a way to clean up all helpers added to this controller, maintaining just the helper with the same name as the controller. [José Valim]
-
-* Support routing constraints in functional tests. [Andrew White]
-
-* Add a header that tells Internet Explorer (all versions) to use the best available standards support. [Yehuda Katz]
-
-* Allow stylesheet/javascript extensions to be changed through railties. [Josh Kalderimis]
-
-* link_to, button_to, and tag/tag_options now rely on html_escape instead of escape_once. [fxn]
-
-* url_for returns always unescaped strings, and the :escape option is gone. [fxn]
-
-* Added accept-charset parameter and _snowman hidden field to force the contents
- of Rails POSTed forms to be in UTF-8 [Yehuda Katz]
-
-* Upgrade to Rack 1.2.1 [Jeremy Kemper]
-
-* Allow :path to be given to match/get/post/put/delete instead of :path_names in the new router [Carlos Antônio da Silva]
-
-* Added resources_path_names to the new router DSL [José Valim]
-
-* Allow options to be given to the namespace method in the new router [Carlos Antônio da Silva]
-
-* Deprecate :name_prefix in the new router DSL [José Valim]
-
-* Add shallow routes back to the new router [Diego Carrion, Andrew White]
-
- resources :posts do
- shallow do
- resources :comments
- end
- end
-
- You can now use comment_path for /comments/1 instead of post_comment_path for /posts/1/comments/1.
-
-* Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 [Guillermo Álvarez]
-
-* Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino]
-
-* Remove middleware laziness [José Valim]
-
-* Make session stores rely on request.cookie_jar and change set_session semantics to return the cookie value instead of a boolean. [José Valim]
-
-* OAuth 2: HTTP Token Authorization support to complement Basic and Digest Authorization. [Rick Olson]
-
-* Fixed inconsistencies in form builder and view helpers #4432 [Neeraj Singh]
-
-* Both :xml and :json renderers now forwards the given options to the model, allowing you to invoke them as render :xml => @projects, :include => :tasks [José Valim, Yehuda Katz]
-
-* Renamed the field error CSS class from fieldWithErrors to field_with_errors for consistency. [Jeremy Kemper]
-
-* Add support for shorthand routes like /projects/status(.:format) #4423 [Diego Carrion]
-
-* Changed translate helper so that it doesn’t mark every translation as safe HTML. Only keys with a "_html" suffix and keys named "html" are considered to be safe HTML. All other translations are left untouched. [Craig Davey]
-
-* New option :as added to form_for allows to change the object name. The old <% form_for :client, @post %> becomes <% form_for @post, :as => :client %> [spastorino]
-
-* Removed verify method in controllers. [JV]
- It's now available as a plugin at http://github.com/rails/verification
-
-* Removed input, form, error_messages_for and error_message_on from views. [JV]
- It's now available as a plugin at http://github.com/rails/dynamic_form
-
-* Routes can be scoped by controller module. [Jeremy Kemper]
-
- # /session => Auth::SessionsController
- scope :module => 'auth' do
- resource :session
- end
-
-* Added #favicon_link_tag, it uses #image_path so in particular the favicon gets an asset ID [fxn]
-
-* Fixed that default locale templates should be used if the current locale template is missing [DHH]
-
-* Added all the new HTML5 form types as individual form tag methods (search, url, number, etc) #3646 [Stephen Celis]
-
-* Changed the object used in routing constraints to be an instance of
- ActionDispatch::Request rather than Rack::Request [YK]
-
-* Changed ActionDispatch::Request#method to return a String, to be compatible
- with Rack::Request. Added ActionDispatch::Request#method_symbol to
- return a symbol form of the request method. [YK]
-
-* Changed ActionDispatch::Request#method to return the original
- method and #request_method to return the overridden method in the
- case of methodoverride being used (this means that #method returns
- "HEAD" and #request_method returns "GET" in HEAD requests). This
- is for compatibility with Rack::Request [YK]
-
-* #concat is now deprecated in favor of using <%= %> helpers [YK]
-
-* Block helpers now return Strings, so you can use <%= form_for @foo do |f| %>.
- <% form_for do |f| %> still works with deprecation notices [YK]
-
-* Add a new #mount method on the router that does not anchor the PATH_INFO
- at the end [YK & CL]
-
-* Create a new LookupContext object that is responsible for performantly
- finding a template for a given pattern [JV]
-
-* Removed relative_url_for in favor of respecting SCRIPT_NAME [YK & CL]
-
-* Changed file streaming to use Rack::Sendfile middleware [YK]
-
-* ActionDispatch::Request#content_type returns a String to be compatible with
- Rack::Request. Use #content_mime_type for the Mime::Type instance [YK]
-
-* Updated Prototype to 1.6.1 and Scriptaculous to 1.8.3 [ML]
-
-* Change the preferred way that URL helpers are included into a class[YK & CL]
-
- # for all helpers including named routes
- include Rails.application.router.url_helpers
-
- # for just url_for
- include Rails.application.router.url_for
-
-* Fixed that PrototypeHelper#update_page should return html_safe [DHH]
-
-* Fixed that much of DateHelper wouldn't return html_safe? strings [DHH]
-
-* Fixed that fragment caching should return a cache hit as html_safe (or it would all just get escaped) [DHH]
-
-* Added that ActionController::Base now does helper :all instead of relying on the default ApplicationController in Rails to do it [DHH]
-
-* Added ActionDispatch::Request#authorization to access the http authentication header regardless of its proxy hiding [DHH]
-
-* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples:
-
- flash[:notice] = 'Post was created'
- redirect_to(@post)
-
- ...becomes:
-
- redirect_to(@post, :notice => 'Post was created')
-
-* Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= [DHH]
-
-
-* Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom]
-
-* Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 [Matthew Rudy Jacobs]
-
-* Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov]
-
-* 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]
-
-
-*2.3.2 [Final] (March 15, 2009)*
-
-* Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") [DHH]
-
-* Don't check authenticity tokens for any AJAX requests [Ross Kaffenberger/Bryan Helmkamp]
-
-* Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 [Gregg Pollack]
-
-* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]
-
-* Form option helpers now support disabled option tags and the use of lambdas for selecting/disabling option tags from collections #837 [Tekin]
-
-* Added partial scoping to TranslationHelper#translate, so if you call translate(".foo") from the people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo") [DHH]
-
-* Fix a syntax error in current_page?() that was prevent matches against URL's with multiple query parameters #1385, #1868 [chris finne/Andrew White]
-
-* Added localized rescue template when I18n.locale is set (ex: public/404.da.html) #1835 [José Valim]
-
-* Make the form_for and fields_for helpers support the new Active Record nested update options. #1202 [Eloy Duran]
-
- <% form_for @person do |person_form| %>
- ...
- <% person_form.fields_for :projects do |project_fields| %>
- <% if project_fields.object.active? %>
- Name: <%= project_fields.text_field :name %>
- <% end %>
- <% end %>
- <% end %>
-
-
-* Added grouped_options_for_select helper method for wrapping option tags in optgroups. #977 [Jon Crawford]
-
-* Implement HTTP Digest authentication. #1230 [Gregg Kellogg, Pratik Naik] Example :
-
- class DummyDigestController < ActionController::Base
- USERS = { "lifo" => 'world' }
-
- before_filter :authenticate
-
- def index
- render :text => "Hello Secret"
- end
-
- private
-
- def authenticate
- authenticate_or_request_with_http_digest("Super Secret") do |username|
- # Return the user's password
- USERS[username]
- end
- end
- end
-
-* Improved i18n support for the number_to_human_size helper. Changes the storage_units translation data; update your translations accordingly. #1634 [Yaroslav Markin]
- storage_units:
- # %u is the storage unit, %n is the number (default: 2 MB)
- format: "%n %u"
- units:
- byte:
- one: "Byte"
- other: "Bytes"
- kb: "KB"
- mb: "MB"
- gb: "GB"
- tb: "TB"
-
-* Added :silence option to BenchmarkHelper#benchmark and turned log_level into a hash parameter and deprecated the old use [DHH]
-
-* Fixed the AssetTagHelper cache to use the computed asset host as part of the cache key instead of just assuming the its a string #1299 [DHH]
-
-* Make ActionController#render(string) work as a shortcut for render :file/:template/:action => string. [#1435] [Pratik Naik] Examples:
-
- # Instead of render(:action => 'other_action')
- render('other_action') # argument has no '/'
- render(:other_action)
-
- # Instead of render(:template => 'controller/action')
- render('controller/action') # argument must not begin with a '/', but contain a '/'
-
- # Instead of render(:file => '/Users/lifo/home.html.erb')
- render('/Users/lifo/home.html.erb') # argument must begin with a '/'
-
-* Add :prompt option to date/time select helpers. #561 [Sam Oliver]
-
-* Fixed that send_file shouldn't set an etag #1578 [Hongli Lai]
-
-* Allow users to opt out of the spoofing checks in Request#remote_ip. Useful for sites whose traffic regularly triggers false positives. [Darren Boyd]
-
-* Deprecated formatted_polymorphic_url. [Jeremy Kemper]
-
-* Added the option to declare an asset_host as an object that responds to call (see http://github.com/dhh/asset-hosting-with-minimum-ssl for an example) [David Heinemeier Hansson]
-
-* Added support for multiple routes.rb files (useful for plugin engines). This also means that draw will no longer clear the route set, you have to do that by hand (shouldn't make a difference to you unless you're doing some funky stuff) [David Heinemeier Hansson]
-
-* Dropped formatted_* routes in favor of just passing in :format as an option. This cuts resource routes generation in half #1359 [aaronbatalion]
-
-* Remove support for old double-encoded cookies from the cookie store. These values haven't been generated since before 2.1.0, and any users who have visited the app in the intervening 6 months will have had their cookie upgraded. [Michael Koziarski]
-
-* Allow helpers directory to be overridden via ActionController::Base.helpers_dir #1424 [Sam Pohlenz]
-
-* Remove deprecated ActionController::Base#assign_default_content_type_and_charset
-
-* Changed the default of ActionView#render to assume partials instead of files when not given an options hash [David Heinemeier Hansson]. Examples:
-
- # Instead of <%= render :partial => "account" %>
- <%= render "account" %>
-
- # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
- <%= render "account", :account => @buyer %>
-
- # @account is an Account instance, so it uses the RecordIdentifier to replace
- # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
- <%= render(@account) %>
-
- # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
- # <%= render :partial => "posts/post", :collection => @posts %>
- <%= render(@posts) %>
-
-* Remove deprecated render_component. Please use the plugin from http://github.com/rails/render_component/tree/master [Pratik Naik]
-
-* Fixed RedCloth and BlueCloth shouldn't preload. Instead just assume that they're available if you want to use textilize and markdown and let autoload require them [David Heinemeier Hansson]
-
-
-*2.2.2 (November 21st, 2008)*
-
-* I18n: translate number_to_human_size. Add storage_units: [Bytes, KB, MB, GB, TB] to your translations. #1448 [Yaroslav Markin]
-
-* Restore backwards compatible functionality for setting relative_url_root. Include deprecation
-
-* Switched the CSRF module to use the request content type to decide if the request is forgeable. #1145 [Jeff Cohen]
-
-* Added :only and :except to map.resources to let people cut down on the number of redundant routes in an application. Typically only useful for huge routesets. #1215 [Tom Stuart]
-
- map.resources :products, :only => :show do |product|
- product.resources :images, :except => :destroy
- end
-
-* Added render :js for people who want to render inline JavaScript replies without using RJS [David Heinemeier Hansson]
-
-* Fixed that polymorphic_url should compact given array #1317 [hiroshi]
-
-* Fixed the sanitize helper to avoid double escaping already properly escaped entities #683 [antonmos/Ryan McGeary]
-
-* Fixed that FormTagHelper generated illegal html if name contained square brackets #1238 [Vladimir Dobriakov]
-
-* Fix regression bug that made date_select and datetime_select raise a Null Pointer Exception when a nil date/datetime was passed and only month and year were displayed #1289 [Bernardo Padua/Tor Erik]
-
-* Simplified the logging format for parameters (don't include controller, action, and format as duplicates) [David Heinemeier Hansson]
-
-* Remove the logging of the Session ID when the session store is CookieStore [David Heinemeier Hansson]
-
-* Fixed regex in redirect_to to fully support URI schemes #1247 [Seth Fitzsimmons]
-
-* Fixed bug with asset timestamping when using relative_url_root #1265 [Joe Goldwasser]
-
-
-*2.2.0 [RC1] (October 24th, 2008)*
-
-* Fix incorrect closing CDATA delimiter and that HTML::Node.parse would blow up on unclosed CDATA sections [packagethief]
-
-* Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends [David Heinemeier Hansson]. Example:
-
- class ArticlesController < ApplicationController
- def show_with_respond_to_block
- @article = Article.find(params[:id])
-
-
- # If the request sends headers that differs from the options provided to stale?, then
- # the request is indeed stale and the respond_to block is triggered (and the options
- # to the stale? call is set on the response).
- #
- # If the request headers match, then the request is fresh and the respond_to block is
- # not triggered. Instead the default render will occur, which will check the last-modified
- # and etag headers and conclude that it only needs to send a "304 Not Modified" instead
- # of rendering the template.
- if stale?(:last_modified => @article.published_at.utc, :etag => @article)
- respond_to do |wants|
- # normal response processing
- end
- end
- end
-
- def show_with_implied_render
- @article = Article.find(params[:id])
-
- # Sets the response headers and checks them against the request, if the request is stale
- # (i.e. no match of either etag or last-modified), then the default render of the template happens.
- # If the request is fresh, then the default render will return a "304 Not Modified"
- # instead of rendering the template.
- fresh_when(:last_modified => @article.published_at.utc, :etag => @article)
- end
- end
-
-
-* Added inline builder yield to atom_feed_helper tags where appropriate [Sam Ruby]. Example:
-
- entry.summary :type => 'xhtml' do |xhtml|
- xhtml.p pluralize(order.line_items.count, "line item")
- xhtml.p "Shipped to #{order.address}"
- xhtml.p "Paid by #{order.pay_type}"
- end
-
-* Make PrototypeHelper#submit_to_remote a wrapper around PrototypeHelper#button_to_remote. [Tarmo Tänav]
-
-* Set HttpOnly for the cookie session store's cookie. #1046
-
-* Added FormTagHelper#image_submit_tag confirm option #784 [Alastair Brunton]
-
-* Fixed FormTagHelper#submit_tag with :disable_with option wouldn't submit the button's value when was clicked #633 [Jose Fernandez]
-
-* Stopped logging template compiles as it only clogs up the log [David Heinemeier Hansson]
-
-* Changed the X-Runtime header to report in milliseconds [David Heinemeier Hansson]
-
-* Changed BenchmarkHelper#benchmark to report in milliseconds [David Heinemeier Hansson]
-
-* Changed logging format to be millisecond based and skip misleading stats [David Heinemeier Hansson]. Went from:
-
- Completed in 0.10000 (4 reqs/sec) | Rendering: 0.04000 (40%) | DB: 0.00400 (4%) | 200 OK [http://example.com]
-
- ...to:
-
- Completed in 100ms (View: 40, DB: 4) | 200 OK [http://example.com]
-
-* Add support for shallow nesting of routes. #838 [S. Brent Faulkner]
-
- Example :
-
- map.resources :users, :shallow => true do |user|
- user.resources :posts
- end
-
- - GET /users/1/posts (maps to PostsController#index action as usual)
- named route "user_posts" is added as usual.
-
- - GET /posts/2 (maps to PostsController#show action as if it were not nested)
- Additionally, named route "post" is added too.
-
-* Added button_to_remote helper. #3641 [Donald Piret, Tarmo Tänav]
-
-* Deprecate render_component. Please use render_component plugin from http://github.com/rails/render_component/tree/master [Pratik Naik]
-
-* Routes may be restricted to lists of HTTP methods instead of a single method or :any. #407 [Brennan Dunn, Gaius Centus Novus]
- map.resource :posts, :collection => { :search => [:get, :post] }
- map.session 'session', :requirements => { :method => [:get, :post, :delete] }
-
-* Deprecated implicit local assignments when rendering partials [Josh Peek]
-
-* Introduce current_cycle helper method to return the current value without bumping the cycle. #417 [Ken Collins]
-
-* Allow polymorphic_url helper to take url options. #880 [Tarmo Tänav]
-
-* Switched integration test runner to use Rack processor instead of CGI [Josh Peek]
-
-* Made AbstractRequest.if_modified_sense return nil if the header could not be parsed [Jamis Buck]
-
-* Added back ActionController::Base.allow_concurrency flag [Josh Peek]
-
-* AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root [Josh Peek]
-
-* Update Prototype to 1.6.0.2 #599 [Patrick Joyce]
-
-* Conditional GET utility methods. [Jeremy Kemper]
- response.last_modified = @post.updated_at
- response.etag = [:admin, @post, current_user]
-
- if request.fresh?(response)
- head :not_modified
- else
- # render ...
- end
-
-* All 2xx requests are considered successful [Josh Peek]
-
-* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [David Heinemeier Hansson]
-
-* Removed config.action_view.cache_template_loading, use config.cache_classes instead [Josh Peek]
-
-* Get buffer for fragment cache from template's @output_buffer [Josh Peek]
-
-* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached [Josh Peek]
-
-* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class [Josh Peek]
-
-* Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {}) [Josh Peek]
-
-* Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent with link_to_remote (submit_to_remote still works as an alias) #8994 [clemens]
-
-* Add :recursive option to javascript_include_tag and stylesheet_link_tag to be used along with :all. #480 [Damian Janowski]
-
-* Allow users to disable the use of the Accept header [Michael Koziarski]
-
- The accept header is poorly implemented by browsers and causes strange
- errors when used on public sites where crawlers make requests too. You
- can use formatted urls (e.g. /people/1.xml) to support API clients in a
- much simpler way.
-
- To disable the header you need to set:
-
- config.action_controller.use_accept_header = false
-
-* Do not stat template files in production mode before rendering. You will no longer be able to modify templates in production mode without restarting the server [Josh Peek]
-
-* Deprecated TemplateHandler line offset [Josh Peek]
-
-* Allow caches_action to accept cache store options. #416. [José Valim]. Example:
-
- caches_action :index, :redirected, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
-
-* Remove define_javascript_functions, javascript_include_tag and friends are far superior. [Michael Koziarski]
-
-* Deprecate :use_full_path render option. The supplying the option no longer has an effect [Josh Peek]
-
-* Add :as option to render a collection of partials with a custom local variable name. #509 [Simon Jefford, Pratik Naik]
-
- render :partial => 'other_people', :collection => @people, :as => :person
-
- This will let you access objects of @people as 'person' local variable inside 'other_people' partial template.
-
-* time_zone_select: support for regexp matching of priority zones. Resolves #195 [Ernie Miller]
-
-* Made ActionView::Base#render_file private [Josh Peek]
-
-* Refactor and simplify the implementation of assert_redirected_to. Arguments are now normalised relative to the controller being tested, not the root of the application. [Michael Koziarski]
-
- This could cause some erroneous test failures if you were redirecting between controllers
- in different namespaces and wrote your assertions relative to the root of the application.
-
-* Remove follow_redirect from controller functional tests.
-
- If you want to follow redirects you can use integration tests. The functional test
- version was only useful if you were using redirect_to :id=>...
-
-* Fix polymorphic_url with singleton resources. #461 [Tammer Saleh]
-
-* Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek]
-
-* Added block-call style to link_to [Sam Stephenson/David Heinemeier Hansson]. Example:
-
- <% link_to(@profile) do %>
- <strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
- <% end %>
-
-* Performance: integration test benchmarking and profiling. [Jeremy Kemper]
-
-* Make caching more aware of mime types. Ensure request format is not considered while expiring cache. [Jonathan del Strother]
-
-* Drop ActionController::Base.allow_concurrency flag [Josh Peek]
-
-* More efficient concat and capture helpers. Remove ActionView::Base.erb_variable. [Jeremy Kemper]
-
-* Added page.reload functionality. Resolves #277. [Sean Huber]
-
-* Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR doesn't match (not just if they're both present) [Mark Imbriaco, Bradford Folkens]
-
-* Allow caches_action to accept a layout option [José Valim]
-
-* Added Rack processor [Ezra Zygmuntowicz, Josh Peek]
-
-
-*2.1.0 (May 31st, 2008)*
-
-* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing]
-
-* Fixed that forgery protection can be used without session tracking (Peter Jones) [#139]
-
-* Added session(:on) to turn session management back on in a controller subclass if the superclass turned it off (Peter Jones) [#136]
-
-* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" [Rick Olson]
-* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap [Geoff Buesing]
-
-* select_date defaults to Time.zone.today when config.time_zone is set [Geoff Buesing]
-
-* Fixed that TextHelper#text_field would corrypt when raw HTML was used as the value (mchenryc, Kevin Glowacz) [#80]
-
-* Added ActionController::TestCase#rescue_action_in_public! to control whether the action under test should use the regular rescue_action path instead of simply raising the exception inline (great for error testing) [David Heinemeier Hansson]
-
-* Reduce number of instance variables being copied from controller to view. [Pratik Naik]
-
-* select_datetime and select_time default to Time.zone.now when config.time_zone is set [Geoff Buesing]
-
-* datetime_select defaults to Time.zone.now when config.time_zone is set [Geoff Buesing]
-
-* Remove ActionController::Base#view_controller_internals flag. [Pratik Naik]
-
-* Add conditional options to caches_page method. [Paul Horsfall]
-
-* Move missing template logic to ActionView. [Pratik Naik]
-
-* Introduce ActionView::InlineTemplate class. [Pratik Naik]
-
-* Automatically parse posted JSON content for Mime::JSON requests. [Rick Olson]
-
- POST /posts
- {"post": {"title": "Breaking News"}}
-
- def create
- @post = Post.create params[:post]
- # ...
- end
-
-* add json_escape ERB util to escape html entities in json strings that are output in HTML pages. [Rick Olson]
-
-* Provide a helper proxy to access helper methods from outside views. Closes #10839 [Josh Peek]
- e.g. ApplicationController.helpers.simple_format(text)
-
-* Improve documentation. [Xavier Noria, leethal, jerome]
-
-* Ensure RJS redirect_to doesn't html-escapes string argument. Closes #8546 [Josh Peek, eventualbuddha, Pratik Naik]
-
-* Support render :partial => collection of heterogeneous elements. #11491 [Zach Dennis]
-
-* Avoid remote_ip spoofing. [Brian Candler]
-
-* Added support for regexp flags like ignoring case in the :requirements part of routes declarations #11421 [NeilW]
-
-* Fixed that ActionController::Base#read_multipart would fail if boundary was exactly 10240 bytes #10886 [ariejan]
-
-* Fixed HTML::Tokenizer (used in sanitize helper) didn't handle unclosed CDATA tags #10071 [esad, packagethief]
-
-* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-
-* Fixed that FormHelper#radio_button would produce invalid ids #11298 [harlancrystal]
-
-* Added :confirm option to submit_tag #11415 [Emilio Tagua]
-
-* Fixed NumberHelper#number_with_precision to properly round in a way that works equally on Mac, Windows, Linux (closes #11409, #8275, #10090, #8027) [zhangyuanyi]
-
-* Allow the #simple_format text_helper to take an html_options hash for each paragraph. #2448 [François Beausoleil, Chris O'Sullivan]
-
-* Fix regression from filter refactoring where re-adding a skipped filter resulted in it being called twice. [Rick Olson]
-
-* Refactor filters to use Active Support callbacks. #11235 [Josh Peek]
-
-* Fixed that polymorphic routes would modify the input array #11363 [thomas.lee]
-
-* Added :format option to NumberHelper#number_to_currency to enable better localization support #11149 [lylo]
-
-* Fixed that TextHelper#excerpt would include one character too many #11268 [Irfy]
-
-* Fix more obscure nested parameter hash parsing bug. #10797 [thomas.lee]
-
-* Added ActionView::Helpers::register_javascript/stylesheet_expansion to make it easier for plugin developers to inject multiple assets. #10350 [lotswholetime]
-
-* Fix nested parameter hash parsing bug. #10797 [thomas.lee]
-
-* Allow using named routes in ActionController::TestCase before any request has been made. Closes #11273 [Eloy Duran]
-
-* Fixed that sweepers defined by cache_sweeper will be added regardless of the perform_caching setting. Instead, control whether the sweeper should be run with the perform_caching setting. This makes testing easier when you want to turn perform_caching on/off [David Heinemeier Hansson]
-
-* Make MimeResponds::Responder#any work without explicit types. Closes #11140 [jaw6]
-
-* Better error message for type conflicts when parsing params. Closes #7962 [spicycode, matt]
-
-* Remove unused ActionController::Base.template_class. Closes #10787 [Pratik Naik]
-
-* Moved template handlers related code from ActionView::Base to ActionView::Template. [Pratik Naik]
-
-* Tests for div_for and content_tag_for helpers. Closes #11223 [Chris O'Sullivan]
-
-* Allow file uploads in Integration Tests. Closes #11091 [RubyRedRick]
-
-* Refactor partial rendering into a PartialTemplate class. [Pratik Naik]
-
-* Added that requests with JavaScript as the priority mime type in the accept header and no format extension in the parameters will be treated as though their format was :js when it comes to determining which template to render. This makes it possible for JS requests to automatically render action.js.rjs files without an explicit respond_to block [David Heinemeier Hansson]
-
-* Tests for distance_of_time_in_words with TimeWithZone instances. Closes #10914 [Ernesto Jimenez]
-
-* Remove support for multivalued (e.g., '&'-delimited) cookies. [Jamis Buck]
-
-* Fix problem with render :partial collections, records, and locals. #11057 [lotswholetime]
-
-* Added support for naming concrete classes in sweeper declarations [David Heinemeier Hansson]
-
-* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application. #10098 [Tim Pope, Chris Kampmeier]
-
-* Fix typo in form_helper documentation. #10650 [Xavier Shay, Chris Kampmeier]
-
-* Fix bug with setting Request#format= after the getter has cached the value. #10889 [cch1]
-
-* Correct inconsistencies in RequestForgeryProtection docs. #11032 [Mislav Marohnić]
-
-* Introduce a Template class to ActionView. #11024 [Pratik Naik]
-
-* Introduce the :index option for form_for and fields_for to simplify multi-model forms (see http://railscasts.com/episodes/75). #9883 [rmm5t]
-
-* Introduce map.resources :cards, :as => 'tarjetas' to use a custom resource name in the URL: cards_path == '/tarjetas'. #10578 [blj]
-
-* TestSession supports indifferent access. #7372 [tamc, Arsen7, mhackett, julik, jean.helou]
-
-* Make assert_routing aware of the HTTP method used. #8039 [mpalmer]
- e.g. assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
-
-* Make map.root accept a single symbol as an argument to declare an alias. #10818 [bscofield]
-
- e.g. map.dashboard '/dashboard', :controller=>'dashboard'
- map.root :dashboard
-
-* Handle corner case with image_tag when passed 'messed up' image names. #9018 [Duncan Beevers, mpalmer]
-
-* Add label_tag helper for generating elements. #10802 [DefV]
-
-* Introduce TemplateFinder to handle view paths and lookups. #10800 [Pratik Naik]
-
-* Performance: optimize route recognition. Large speedup for apps with many resource routes. #10835 [oleganza]
-
-* Make render :partial recognise form builders and use the _form partial. #10814 [Damian Janowski]
-
-* Allow users to declare other namespaces when using the atom feed helpers. #10304 [david.calavera]
-
-* Introduce send_file :x_sendfile => true to send an X-Sendfile response header. [Jeremy Kemper]
-
-* Fixed ActionView::Helpers::ActiveRecordHelper::form for when protect_from_forgery is used #10739 [Jeremy Evans]
-
-* Provide nicer access to HTTP Headers. Instead of request.env["HTTP_REFERRER"] you can now use request.headers["Referrer"]. [Michael Koziarski]
-
-* UrlWriter respects relative_url_root. #10748 [Cheah Chu Yeow]
-
-* The asset_host block takes the controller request as an optional second argument. Example: use a single asset host for SSL requests. #10549 [Cheah Chu Yeow, Peter B, Tom Taylor]
-
-* Support render :text => nil. #6684 [tjennings, PotatoSalad, Cheah Chu Yeow]
-
-* assert_response failures include the exception message. #10688 [Seth Rasmussen]
-
-* All fragment cache keys are now by default prefixed with the "views/" namespace [David Heinemeier Hansson]
-
-* Moved the caching stores from ActionController::Caching::Fragments::* to ActiveSupport::Cache::*. If you're explicitly referring to a store, like ActionController::Caching::Fragments::MemoryStore, you need to update that reference with ActiveSupport::Cache::MemoryStore [David Heinemeier Hansson]
-
-* Deprecated ActionController::Base.fragment_cache_store for ActionController::Base.cache_store [David Heinemeier Hansson]
-
-* Made fragment caching in views work for rjs and builder as well #6642 [Dee Zsombor]
-
-* Fixed rendering of partials with layout when done from site layout #9209 [antramm]
-
-* Fix atom_feed_helper to comply with the atom spec. Closes #10672 [Xavier Shay]
-
- * The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
- * IDs are not guaranteed unique
- * A default self link was not provided, contrary to the documentation
- * NOTE: This changes tags for existing atom entries, but at least they validate now.
-
-* Correct indentation in tests. Closes #10671 [Luca Guidi]
-
-* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 [Brad Greenlee]
-
-* Ensure that test case setup is run even if overridden. #10382 [Josh Peek]
-
-* Fix HTML Sanitizer to allow trailing spaces in CSS style attributes. Closes #10566 [wesley.moxam]
-
-* Add :default option to time_zone_select. #10590 [Matt Aimonetti]
-
-
-*2.0.2* (December 16th, 2007)
-
-* Added delete_via_redirect and put_via_redirect to integration testing #10497 [philodespotos]
-
-* Allow headers['Accept'] to be set by hand when calling xml_http_request #10461 [BMorearty]
-
-* Added OPTIONS to list of default accepted HTTP methods #10449 [holoway]
-
-* Added option to pass proc to ActionController::Base.asset_host for maximum configurability #10521 [Cheah Chu Yeow]. Example:
-
- ActionController::Base.asset_host = Proc.new { |source|
- if source.starts_with?('/images')
- "http://images.example.com"
- else
- "http://assets.example.com"
- end
- }
-
-* Fixed that ActionView#file_exists? would be incorrect if @first_render is set #10569 [dbussink]
-
-* Added that Array#to_param calls to_param on all it's elements #10473 [brandon]
-
-* Ensure asset cache directories are automatically created. #10337 [Josh Peek, Cheah Chu Yeow]
-
-* render :xml and :json preserve custom content types. #10388 [jmettraux, Cheah Chu Yeow]
-
-* Refactor Action View template handlers. #10437, #10455 [Josh Peek]
-
-* Fix DoubleRenderError message and leave out mention of returning false from filters. Closes #10380 [Frederick Cheung]
-
-* Clean up some cruft around ActionController::Base#head. Closes #10417 [ssoroka]
-
-
-*2.0.1* (December 7th, 2007)
-
-* Fixed send_file/binary_content for testing #8044 [tolsen]
-
-* When a NonInferrableControllerError is raised, make the proposed fix clearer in the error message. Closes #10199 [Jack Danger Canty]
-
-* Update Prototype to 1.6.0.1. [sam]
-
-* Update script.aculo.us to 1.8.0.1. [madrobby]
-
-* Add 'disabled' attribute to <OPTION> separators used in time zone and country selects. Closes #10354 [Josh Susser]
-
-* Added the same record identification guessing rules to fields_for as form_for has [David Heinemeier Hansson]
-
-* Fixed that verification violations with no specified action didn't halt the chain (now they do with a 400 Bad Request) [David Heinemeier Hansson]
-
-* Raise UnknownHttpMethod exception for unknown HTTP methods. Closes #10303 [Tarmo Tänav]
-
-* Update to Prototype -r8232. [sam]
-
-* Make sure the optimisation code for routes doesn't get used if :host, :anchor or :port are provided in the hash arguments. [pager, Michael Koziarski] #10292
-
-* Added protection from trailing slashes on page caching #10229 [devrieda]
-
-* Asset timestamps are appended, not prepended. Closes #10276 [Mike Naberezny]
-
-* Minor inconsistency in description of render example. Closes #10029 [ScottSchram]
-
-* Add #prepend_view_path and #append_view_path instance methods on ActionController::Base for consistency with the class methods. [Rick Olson]
-
-* Refactor sanitizer helpers into HTML classes and make it easy to swap them out with custom implementations. Closes #10129. [Rick Olson]
-
-* Add deprecation for old subtemplate syntax for ActionMailer templates, use render :partial [Rick Olson]
-
-* Fix TemplateError so it doesn't bomb on exceptions while running tests [Rick Olson]
-
-* Fixed that named routes living under resources shouldn't have double slashes #10198 [Isaac Feliu]
-
-* Make sure that cookie sessions use a secret that is at least 30 chars in length. [Michael Koziarski]
-
-* Fixed that partial rendering should look at the type of the first render to determine its own type if no other clues are available (like when using text.plain.erb as the extension in AM) #10130 [java]
-
-* Fixed that has_many :through associations should render as collections too #9051 [mathie/Jack Danger Canty]
-
-* Added :mouseover short-cut to AssetTagHelper#image_tag for doing easy image swaps #6893 [joost]
-
-* Fixed handling of non-domain hosts #9479 [purp]
-
-* Fix syntax error in documentation example for cycle method. Closes #8735 [foca]
-
-* Document :with option for link_to_remote. Closes #8765 [Ryan Bates]
-
-* Document :minute_step option for time_select. Closes #8814 [brupm]
-
-* Explain how to use the :href option for link_to_remote to degrade gracefully in the absence of JavaScript. Closes #8911 [vlad]
-
-* Disambiguate :size option for text area tag. Closes #8955 [redbeard]
-
-* Fix broken tag in assert_tag documentation. Closes #9037 [mfazekas]
-
-* Add documentation for route conditions. Closes #9041 [innu, Manfred Stienstra]
-
-* Fix typo left over from previous typo fix in url helper. Closes #9414 [Henrik N]
-
-* Fixed that ActionController::CgiRequest#host_with_port() should handle standard port #10082 [moro]
-
-* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. [sam, madrobby]
-
-* Expose the cookie jar as a helper method (before the view would just get the raw cookie hash) [David Heinemeier Hansson]
-
-* Integration tests: get_ and post_via_redirect take a headers hash. #9130 [simonjefford]
-
-* Simplfy #view_paths implementation. ActionView templates get the exact object, not a dup. [Rick Olson]
-
-* Update tests for ActiveSupport's JSON escaping change. [Rick Olson]
-
-* FormHelper's auto_index should use #to_param instead of #id_before_type_cast. Closes #9994 [mattly]
-
-* Doc typo fixes for ActiveRecordHelper. Closes #9973 [mikong]
-
-* Make example parameters in restful routing docs idiomatic. Closes #9993 [Jack Danger Canty]
-
-* Make documentation comment for mime responders match documentation example. Closes #9357 [yon]
-
-* Introduce a new test case class for functional tests. ActionController::TestCase. [Michael Koziarski]
-
-* Fix incorrect path in helper rdoc. Closes #9926 [viktor tron]
-
-* Partials also set 'object' to the default partial variable. #8823 [Nick Retallack, Jeremy Kemper]
-
-* Request profiler. [Jeremy Kemper]
- $ cat login_session.rb
- get_with_redirect '/'
- say "GET / => #{path}"
- post_with_redirect '/sessions', :username => 'john', :password => 'doe'
- say "POST /sessions => #{path}"
- $ ./script/performance/request -n 10 login_session.rb
-
-* Disabled checkboxes don't submit a form value. #9301 [vladr, robinjfisher]
-
-* Added tests for options to ActiveRecordHelper#form. Closes #7213 [richcollins, mikong, Mislav Marohnić]
-
-* Changed before_filter halting to happen automatically on render or redirect but no longer on simply returning false [David Heinemeier Hansson]
-
-* Ensure that cookies handle array values correctly. Closes #9937 [queso]
-
-* Make sure resource routes don't clash with internal helpers like javascript_path, image_path etc. #9928 [Geoff Buesing]
-
-* caches_page uses a single after_filter instead of one per action. #9891 [Pratik Naik]
-
-* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0. [sam, madrobby]
-
-* Dispatcher: fix that to_prepare should only run once in production. #9889 [Nathaniel Talbott]
-
-* Memcached sessions: add session data on initialization; don't silently discard exceptions; add unit tests. #9823 [kamk]
-
-* error_messages_for also takes :message and :header_message options which defaults to the old "There were problems with the following fields:" and "<count> errors prohibited this <object_name> from being saved". #8270 [rmm5t, zach-inglis-lt3]
-
-* Make sure that custom inflections are picked up by map.resources. #9815 [Mislav Marohnić]
-
-* Changed SanitizeHelper#sanitize to only allow the custom attributes and tags when specified in the call [David Heinemeier Hansson]
-
-* Extracted sanitization methods from TextHelper to SanitizeHelper [David Heinemeier Hansson]
-
-* rescue_from accepts :with => lambda { |exception| ... } or a normal block. #9827 [Pratik Naik]
-
-* Add :status to redirect_to allowing users to choose their own response code without manually setting headers. #8297 [Coda Hale, chasgrundy]
-
-* Add link_to :back which uses your referrer with a fallback to a javascript link. #7366 [eventualbuddha, Tarmo Tänav]
-
-* error_messages_for and friends also work with local variables. #9699 [Frederick Cheung]
-
-* Fix url_for, redirect_to, etc. with :controller => :symbol instead of 'string'. #8562, #9525 [Justin Lynn, Tarmo Tänav, shoe]
-
-* Use #require_library_or_gem to load the memcache library for the MemCache session and fragment cache stores. Closes #8662. [Rick Olson]
-
-* Move ActionController::Routing.optimise_named_routes to ActionController::Base.optimise_named_routes. Now you can set it in the config. [Rick Olson]
-
- config.action_controller.optimise_named_routes = false
-
-* ActionController::Routing::DynamicSegment#interpolation_chunk should call #to_s on all values before calling URI.escape. [Rick Olson]
-
-* Only accept session ids from cookies, prevents session fixation attacks. [bradediger]
-
-
-*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.12.2 - 1.13.3]
-
-* Fixed that render template did not honor exempt_from_layout #9698 [pezra]
-
-* Better error messages if you leave out the :secret option for request forgery protection. Closes #9670 [Rick Olson]
-
-* Allow ability to disable request forgery protection, disable it in test mode by default. Closes #9693 [Pratik Naik]
-
-* Avoid calling is_missing on LoadErrors. Closes #7460. [ntalbott]
-
-* Move Railties' Dispatcher to ActionController::Dispatcher, introduce before_ and after_dispatch callbacks, and warm up to non-CGI requests. [Jeremy Kemper]
-
-* The tag helper may bypass escaping. [Jeremy Kemper]
-
-* Cache asset ids. [Jeremy Kemper]
-
-* Optimized named routes respect AbstractRequest.relative_url_root. #9612 [Daniel Morrison, Jeremy Kemper]
-
-* Introduce ActionController::Base.rescue_from to declare exception-handling methods. Cleaner style than the case-heavy rescue_action_in_public. #9449 [Norbert Crombach]
-
-* Rename some RequestForgeryProtection methods. The class method is now #protect_from_forgery, and the default parameter is now 'authenticity_token'. [Rick Olson]
-
-* Merge csrf_killer plugin into rails. Adds RequestForgeryProtection model that verifies session-specific _tokens for non-GET requests. [Rick Olson]
-
-* Secure #sanitize, #strip_tags, and #strip_links helpers against xss attacks. Closes #8877. [Rick Olson, Pratik Naik, Jacques Distler]
-
- This merges and renames the popular white_list helper (along with some css sanitizing from Jacques Distler version of the same plugin).
- Also applied updated versions of #strip_tags and #strip_links from #8877.
-
-* Remove use of & logic operator. Closes #8114. [watson]
-
-* Fixed JavaScriptHelper#escape_javascript to also escape closing tags #8023 [Ruy Asan]
-
-* Fixed TextHelper#word_wrap for multiline strings with extra carrier returns #8663 [seth]
-
-* Fixed that setting the :host option in url_for would automatically turn off :only_path (since :host would otherwise not be shown) #9586 [Bounga]
-
-* Added FormHelper#label. #8641, #9850 [jcoglan, Jarkko Laine]
-
-* Added AtomFeedHelper (slightly improved from the atom_feed_helper plugin) [David Heinemeier Hansson]
-
-* Prevent errors when generating routes for uncountable resources, (i.e. sheep where plural == singluar). map.resources :sheep now creates sheep_index_url for the collection and sheep_url for the specific item. [Michael Koziarski]
-
-* Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks #8895 [Pratik Naik, Mark Somerville]
-
-* Don't warn when a path segment precedes a required segment. Closes #9615. [Nicholas Seckar]
-
-* Fixed CaptureHelper#content_for to work with the optional content parameter instead of just the block #9434 [sandofsky/wildchild].
-
-* Added Mime::Type.register_alias for dealing with different formats using the same mime type [David Heinemeier Hansson]. Example:
-
- class PostsController < ApplicationController
- before_filter :adjust_format_for_iphone
-
- def index
- @posts = Post.find(:all)
-
- respond_to do |format|
- format.html # => renders index.html.erb and uses "text/html" as the content type
- format.iphone # => renders index.iphone.erb and uses "text/html" as the content type
- end
- end
-
-
- private
- def adjust_format_for_iphone
- if request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/iPhone/]
- request.format = :iphone
- end
- end
- end
-
-* Added that render :json will automatically call .to_json unless it's being passed a string [David Heinemeier Hansson].
-
-* Autolink behaves well with emails embedded in URLs. #7313 [Jeremy McAnally, Tarmo Tänav]
-
-* Fixed that default layouts did not take the format into account #9564 [Pratik Naik]
-
-* Fixed optimized route segment escaping. #9562 [wildchild, Jeremy Kemper]
-
-* Added block acceptance to JavaScriptHelper#javascript_tag. #7527 [Bob Silva, Tarmo Tänav, rmm5t]
-
-* root_path returns '/' not ''. #9563 [Pratik Naik]
-
-* Fixed that setting request.format should also affect respond_to blocks [David Heinemeier Hansson]
-
-* Add option to force binary mode on tempfile used for fixture_file_upload. #6380 [Jonathan Viney]
-
-* Fixed that resource namespaces wouldn't stick to all nested resources #9399 [pixeltrix]
-
-* Moved ActionController::Macros::AutoComplete into the auto_complete plugin on the official Rails svn. #9512 [Pratik Naik]
-
-* Moved ActionController::Macros::InPlaceEditing into the in_place_editor plugin on the official Rails svn. #9513 [Pratik Naik]
-
-* Removed deprecated form of calling xml_http_request/xhr without the first argument being the http verb [David Heinemeier Hansson]
-
-* Removed deprecated methods [David Heinemeier Hansson]:
-
- - ActionController::Base#keep_flash (use flash.keep instead)
- - ActionController::Base#expire_matched_fragments (just call expire_fragment with a regular expression)
- - ActionController::Base.template_root/= methods (use ActionController#Base.view_paths/= instead)
- - ActionController::Base.cookie (use ActionController#Base.cookies[]= instead)
-
-* Removed the deprecated behavior of appending ".png" to image_tag/image_path calls without an existing extension [David Heinemeier Hansson]
-
-* Removed ActionController::Base.scaffold -- it went through the whole idea of scaffolding (card board walls you remove and tweak one by one). Use the scaffold generator instead (it does resources too now!) [David Heinemeier Hansson]
-
-* Optimise named route generation when using positional arguments. [Michael Koziarski]
-
- This change delivers significant performance benefits for the most
- common usage scenarios for modern rails applications by avoiding the
- costly trip through url_for. Initial benchmarks indicate this is
- between 6 and 20 times as fast.
-
-* Explicitly require active_record/query_cache before using it. [Jeremy Kemper]
-
-* Fix layout overriding response status. #9476 [lotswholetime]
-
-* Add field_set_tag for generating field_sets, closes #9477. [Damian Janowski]
-
-* Allow additional parameters to be passed to named route helpers when using positional arguments. Closes #8930 [Ian White]
-
-* Make render :partial work with a :collection of Hashes, previously this wasn't possible due to backwards compatibility restrictions. [Pratik Naik]
-
-* request.host works with IPv6 addresses. #9458 [yuya]
-
-* Fix bug where action caching sets the content type to the ActionCachePath object. Closes #9282 [mindforge]
-
-* Find layouts even if they're not in the first view_paths directory. Closes #9258 [caio]
-
-* Major improvement to the documentation for the options / select form helpers. Closes #9038 [Chris Kampmeier, jardeon, wesg]
-
-* Fix number_to_human_size when using different precisions. Closes #7536. [RichardStrand, mpalmer]
-
-* Added partial layouts (see example in action_view/lib/partials.rb) [David Heinemeier Hansson]
-
-* Allow you to set custom :conditions on resource routes. [Rick Olson]
-
-* Fixed that file.content_type for uploaded files would include a trailing \r #9053 [Brad Greenlee]
-
-* url_for now accepts a series of symbols representing the namespace of the record [Josh Knowles]
-
-* Make :trailing_slash work with query parameters for url_for. Closes #4004 [nov]
-
-* Make sure missing template exceptions actually say which template they were looking for. Closes #8683 [dasil003]
-
-* Fix errors with around_filters which do not yield, restore 1.1 behaviour with after filters. Closes #8891 [Stefan Kaes]
-
- After filters will *no longer* be run if an around_filter fails to yield, users relying on
- this behaviour are advised to put the code in question after a yield statement in an around filter.
-
-
-* Allow you to delete cookies with options. Closes #3685 [Josh Peek, Chris Wanstrath]
-
-* Allow you to render views with periods in the name. Closes #8076 [Norbert Crombach]
-
- render :partial => 'show.html.erb'
-
-* Improve capture helper documentation. #8796 [Chris Kampmeier]
-
-* Prefix nested resource named routes with their action name, e.g. new_group_user_path(@group) instead of group_new_user_path(@group). The old nested action named route is deprecated in Rails 1.2.4. #8558 [David Chelimsky]
-
-* Allow sweepers to be created solely for expiring after controller actions, not model changes [David Heinemeier Hansson]
-
-* Added assigns method to ActionController::Caching::Sweeper to easily access instance variables on the controller [David Heinemeier Hansson]
-
-* Give the legacy X-POST_DATA_FORMAT header greater precedence during params parsing for backward compatibility. [Jeremy Kemper]
-
-* Fixed that link_to with an href of # when using :method will not allow for click-through without JavaScript #7037 [Steven Bristol, Josh Peek]
-
-* Fixed that radio_button_tag should generate unique ids #3353 [Bob Silva, Rebecca, Josh Peek]
-
-* Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well #6754 [Mislav Marohnić]
-
-* Don't mistakenly interpret the request uri as the query string. #8731 [Pratik Naik, Jeremy Kemper]
-
-* Make ActionView#view_paths an attr_accessor for real this time. Also, don't perform an unnecessary #compact on the @view_paths array in #initialize. Closes #8582 [dasil003, julik, Rick Olson]
-
-* Tolerate missing content type on multipart file uploads. Fix for Safari 3. [Jeremy Kemper]
-
-* Deprecation: remove pagination. Install the classic_pagination plugin for forward compatibility, or move to the superior will_paginate plugin. #8157 [Josh Peek]
-
-* Action caching is limited to GET requests returning 200 OK status. #3335 [tom@craz8.com, halfbyte, Dan Kubb, Josh Peek]
-
-* Improve Text Helper test coverage. #7274 [Rob Sanheim, Josh Peek]
-
-* Improve helper test coverage. #7208, #7212, #7215, #7233, #7234, #7235, #7236, #7237, #7238, #7241, #7243, #7244 [Rich Collins, Josh Peek]
-
-* Improve UrlRewriter tests. #7207 [Rich Collins]
-
-* Resources: url_for([parent, child]) generates /parents/1/children/2 for the nested resource. Likewise with the other simply helpful methods like form_for and link_to. #6432 [mhw, Jonathan Vaught, lotswholetime]
-
-* Assume html format when rendering partials in RJS. #8076 [Rick Olson]
-
-* Don't double-escape url_for in views. #8144 [Rich Collins, Josh Peek]
-
-* Allow JSON-style values for the :with option of observe_field. Closes #8557 [kommen]
-
-* Remove RAILS_ROOT from backtrace paths. #8540 [Tim Pope]
-
-* Routing: map.resource :logo routes to LogosController so the controller may be reused for multiple nestings or namespaces. [Jeremy Kemper]
-
-* render :partial recognizes Active Record associations as Arrays. #8538 [Kamal Fariz Mahyuddin]
-
-* Routing: drop semicolon and comma as route separators. [Jeremy Kemper]
-
-* request.remote_ip understands X-Forwarded-For addresses with nonstandard whitespace. #7386 [moses]
-
-* Don't prepare response when rendering a component. #8493 [jsierles]
-
-* Reduce file stat calls when checking for template changes. #7736 [alex]
-
-* Added custom path cache_page/expire_page parameters in addition to the options hashes [David Heinemeier Hansson]. Example:
-
- def index
- caches_page(response.body, "/index.html")
- end
-
-* Action Caching speedup. #8231 [Stefan Kaes]
-
-* Wordsmith resources documentation. #8484 [marclove]
-
-* Fix syntax error in code example for routing documentation. #8377. [Norbert Crombach]
-
-* Routing: respond with 405 Method Not Allowed status when the route path matches but the HTTP method does not. #6953 [Josh Peek, defeated, Dan Kubb, Coda Hale]
-
-* Add support for assert_select_rjs with :show and :hide. #7780 [dchelimsky]
-
-* Make assert_select's failure messages clearer about what failed. #7779 [dchelimsky]
-
-* Introduce a default respond_to block for custom types. #8174 [Josh Peek]
-
-* auto_complete_field takes a :method option so you can GET or POST. #8120 [zapnap]
-
-* Added option to suppress :size when using :maxlength for FormTagHelper#text_field #3112 [Tim Pope]
-
-* catch possible WSOD when trying to render a missing partial. Closes #8454 [Jonathan del Strother]
-
-* Rewind request body after reading it, if possible. #8438 [s450r1]
-
-* Resource namespaces are inherited by their has_many subresources. #8280 [marclove, Geoff Garside]
-
-* Fix filtered parameter logging with nil parameter values. #8422 [choonkeat]
-
-* Integration tests: alias xhr to xml_http_request and add a request_method argument instead of always using POST. #7124 [Nik Wakelin, François Beausoleil, Wizard]
-
-* Document caches_action. #5419 [Jarkko Laine]
-
-* Update to Prototype 1.5.1. [Sam Stephenson]
-
-* Allow routes to be decalred under namespaces [Tobias Lütke]:
-
- map.namespace :admin do |admin|
- admin.root :controller => "products"
- admin.feed 'feed.xml', :controller => 'products', :action => 'feed', :format => 'xml'
- end
-
-* Update to script.aculo.us 1.7.1_beta3. [Thomas Fuchs]
-
-* observe_form always sends the serialized form. #5271 [Manfred Stienstra, normelton@gmail.com]
-
-* Parse url-encoded and multipart requests ourselves instead of delegating to CGI. [Jeremy Kemper]
-
-* select :include_blank option can be set to a string instead of true, which just uses an empty string. #7664 [Wizard]
-
-* Added url_for usage on render :location, which allows for record identification [David Heinemeier Hansson]. Example:
-
- render :xml => person, :status => :created, :location => person
-
- ...expands the location to person_url(person).
-
-* Introduce the request.body stream. Lazy-read to parse parameters rather than always setting RAW_POST_DATA. Reduces the memory footprint of large binary PUT requests. [Jeremy Kemper]
-
-* Add some performance enhancements to ActionView.
-
- * Cache base_paths in @@cached_base_paths
- * Cache template extensions in @@cached_template_extension
- * Remove unnecessary rescues
-
-* Assume that rendered partials go by the HTML format by default
-
- def my_partial
- render :update do |page|
- # in this order
- # _foo.html.erb
- # _foo.erb
- # _foo.rhtml
- page.replace :foo, :partial => 'foo'
- end
- end
-
-* Added record identifications to FormHelper#form_for and PrototypeHelper#remote_form_for [David Heinemeier Hansson]. Examples:
-
- <% form_for(@post) do |f| %>
- ...
- <% end %>
-
- This will expand to be the same as:
-
- <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
- ...
- <% end %>
-
- And for new records:
-
- <% form_for(Post.new) do |f| %>
- ...
- <% end %>
-
- This will expand to be the same as:
-
- <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
- ...
- <% end %>
-
-* Rationalize route path escaping according to RFC 2396 section 3.3. #7544, #8307. [Jeremy Kemper, Chris Roos, begemot, jugend]
-
-* Added record identification with polymorphic routes for ActionController::Base#url_for and ActionView::Base#url_for [David Heinemeier Hansson]. Examples:
-
- redirect_to(post) # => redirect_to(posts_url(post)) => Location: http://example.com/posts/1
- link_to(post.title, post) # => link_to(post.title, posts_url(post)) => <a href="/posts/1">Hello world</a>
-
- Any method that calls url_for on its parameters will automatically benefit from this.
-
-* Removed deprecated parameters_for_method_reference concept (legacy from before named routes) [David Heinemeier Hansson]
-
-* Add ActionController::Routing::Helpers, a module to contain common URL helpers such as polymorphic_url. [Nicholas Seckar]
-
-* Included the HttpAuthentication plugin as part of core (ActionController::HttpAuthentication::Basic) [David Heinemeier Hansson]
-
-* Modernize documentation for form helpers. [Jeremy McAnally]
-
-* Add brief introduction to REST to the resources documentation. [fearoffish]
-
-* Fix various documentation typos throughout ActionPack. [Henrik N]
-
-* Enhance documentation and add examples for url_for. [Jeremy McAnally]
-
-* Fix documentation typo in routes. [Norbert Crombach, pam]
-
-* Sweep flash when filter chain is halted. [Caio Chassot <lists@v2studio.com>]
-
-* Fixed that content_tag with a block will just return the result instead of concate it if not used in a ERb view #7857, #7432 [michael.niessner]
-
-* Replace the current block/continuation filter chain handling by an implementation based on a simple loop. #8226 [Stefan Kaes]
-
-* Update UrlWriter to accept :anchor parameter. Closes #6771. [Chris McGrath]
-
-* Added RecordTagHelper for using RecordIdentifier conventions on divs and other container elements [David Heinemeier Hansson]. Example:
-
- <% div_for(post) do %> <div id="post_45" class="post">
- <%= post.body %> What a wonderful world!
- <% end %> </div>
-
-* Added page[record] accessor to JavaScriptGenerator that relies on RecordIdentifier to find the right dom id [David Heinemeier Hansson]. Example:
-
- format.js do
- # Calls: new Effect.fade('post_45');
- render(:update) { |page| page[post].visual_effect(:fade) }
- end
-
-* Added RecordIdentifier to enforce view conventions on records for dom ids, classes, and partial paths [David Heinemeier Hansson]
-
-* Added map.namespace to deal with the common situation of admin sections and the like [David Heinemeier Hansson]
-
- Before:
-
- map.resources :products, :path_prefix => "admin", :controller => "admin/products", :collection => { :inventory => :get }, :member => { :duplicate => :post }
- map.resources :tags, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_tags"
- map.resources :images, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_images"
- map.resources :variants, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_variants"
-
- After:
-
- map.namespace(:admin) do |admin|
- admin.resources :products,
- :collection => { :inventory => :get },
- :member => { :duplicate => :post },
- :has_many => [ :tags, :images, :variants ]
- end
-
-* Added :name_prefix as standard for nested resources [David Heinemeier Hansson]. WARNING: May be backwards incompatible with your app
-
- Before:
-
- map.resources :emails do |emails|
- emails.resources :comments, :name_prefix => "email_"
- emails.resources :attachments, :name_prefix => "email_"
- end
-
- After:
-
- map.resources :emails do |emails|
- emails.resources :comments
- emails.resources :attachments
- end
-
- This does mean that if you intended to have comments_url go to /emails/5/comments, then you'll have to set :name_prefix to nil explicitly.
-
-* Added :has_many and :has_one for declaring plural and singular resources beneath the current [David Heinemeier Hansson]
-
- Before:
-
- map.resources :notes do |notes|
- notes.resources :comments
- notes.resources :attachments
- notes.resource :author
- end
-
- After:
-
- map.resources :notes, :has_many => [ :comments, :attachments ], :has_one => :author
-
-* Added that render :xml will try to call to_xml if it can [David Heinemeier Hansson]. Makes these work:
-
- render :xml => post
- render :xml => comments
-
-* Added :location option to render so that the common pattern of rendering a response after creating a new resource is now a 1-liner [David Heinemeier Hansson]
-
- render :xml => post.to_xml, :status => :created, :location => post_url(post)
-
-* Ensure that render_text only adds string content to the body of the response [David Heinemeier Hansson]
-
-* Return the string representation from an Xml Builder when rendering a partial. Closes #5044 [Tim Pope]
-
-* Fixed that parameters from XML should also be presented in a hash with indifferent access [David Heinemeier Hansson]
-
-* Tweak template format rules so that the ACCEPT header is only used if it's text/javascript. This is so ajax actions without a :format param get recognized as Mime::JS. [Rick Olson]
-
-* The default respond_to blocks don't set a specific extension anymore, so that both 'show.rjs' and 'show.js.rjs' will work. [Rick Olson]
-
-* Allow layouts with extension of .html.erb. Closes #8032 [Josh Knowles]
-
-* Change default respond_to templates for xml and rjs formats. [Rick Olson]
-
- * Default xml template goes from #{action_name}.rxml => #{action_name}.xml.builder.
- * Default rjs template goes from #{action_name}.rjs => #{action_name}.js.rjs.
-
- You can still specify your old templates:
-
- respond_to do |format|
- format.xml do
- render :action => "#{action_name}.rxml"
- end
- end
-
-* Fix WSOD due to modification of a formatted template extension so that requests to templates like 'foo.html.erb' fail on the second hit. [Rick Olson]
-
-* Fix WSOD when template compilation fails [Rick Olson]
-
-* Change ActionView template defaults. Look for templates using the request format first, such as "show.html.erb" or "show.xml.builder", before looking for the old defaults like "show.erb" or "show.builder" [Rick Olson]
-
-* Highlight helper highlights one or many terms in a single pass. [Jeremy Kemper]
-
-* Dropped the use of ; as a separator of non-crud actions on resources and went back to the vanilla slash. It was a neat idea, but lots of the non-crud actions turned out not to be RPC (as the ; was primarily intended to discourage), but legitimate sub-resources, like /parties/recent, which didn't deserve the uglification of /parties;recent. Further more, the semicolon caused issues with caching and HTTP authentication in Safari. Just Not Worth It [David Heinemeier Hansson]
-
-* Added that FormTagHelper#submit_tag will return to its original state if the submit fails and you're using :disable_with [David Heinemeier Hansson]
-
-* Cleaned up, corrected, and mildly expanded ActionPack documentation. Closes #7190 [Jeremy McAnally]
-
-* Small collection of ActionController documentation cleanups. Closes #7319 [Jeremy McAnally]
-
-* Make sure the route expiry hash is constructed by comparing the to_param-ized values of each hash. [Jamis Buck]
-
-* Allow configuration of the default action cache path for #caches_action calls. [Rick Olson]
-
- class ListsController < ApplicationController
- caches_action :index, :cache_path => Proc.new { |controller|
- controller.params[:user_id] ?
- controller.send(:user_lists_url, c.params[:user_id]) :
- controller.send(:lists_url) }
- end
-
-* Performance: patch cgi/session/pstore to require digest/md5 once rather than per #initialize. #7583 [Stefan Kaes]
-
-* Cookie session store: ensure that new sessions doesn't reuse data from a deleted session in the same request. [Jeremy Kemper]
-
-* Deprecation: verification with :redirect_to => :named_route shouldn't be deprecated. #7525 [Justin French]
-
-* Cookie session store: raise ArgumentError when :session_key is blank. [Jeremy Kemper]
-
-* Deprecation: remove deprecated request, redirect, and dependency methods. Remove deprecated instance variables. Remove deprecated url_for(:symbol, *args) and redirect_to(:symbol, *args) in favor of named routes. Remove uses_component_template_root for toplevel components directory. Privatize deprecated render_partial and render_partial_collection view methods. Remove deprecated link_to_image, link_image_to, update_element_function, start_form_tag, and end_form_tag helper methods. Remove deprecated human_size helper alias. [Jeremy Kemper]
-
-* Consistent public/protected/private visibility for chained methods. #7813 [Dan Manges]
-
-* Prefer MIME constants to strings. #7707 [Dan Kubb]
-
-* Allow array and hash query parameters. Array route parameters are converted/to/a/path as before. #6765, #7047, #7462 [bgipsy, Jeremy McAnally, Dan Kubb, brendan]
-
-# Add a #dbman attr_reader for CGI::Session and make CGI::Session::CookieStore#generate_digest public so it's easy to generate digests
-using the cookie store's secret. [Rick Olson]
-
-* Added Request#url that returns the complete URL used for the request [David Heinemeier Hansson]
-
-* Extract dynamic scaffolding into a plugin. #7700 [Josh Peek]
-
-* Added user/password options for url_for to add http authentication in a URL [David Heinemeier Hansson]
-
-* Fixed that FormTagHelper#text_area_tag should disregard :size option if it's not a string [Brendon Davidson]
-
-* Set the original button value in an attribute of the button when using the :disable_with key with submit_tag, so that the original can be restored later. [Jamis Buck]
-
-* session_enabled? works with session :off. #6680 [Jonathan del Strother]
-
-* Added :port and :host handling to UrlRewriter (which unified url_for usage, regardless of whether it's called in view or controller) #7616 [alancfrancis]
-
-* Allow send_file/send_data to use a registered mime type as the :type parameter #7620 [jonathan]
-
-* Allow routing requirements on map.resource(s) #7633 [quixoten]. Example:
-
- map.resources :network_interfaces, :requirements => { :id => /^\d+\.\d+\.\d+\.\d+$/ }
-
-* Cookie session store: empty and unchanged sessions don't write a cookie. [Jeremy Kemper]
-
-* Added helper(:all) as a way to include all helpers from app/helpers/**/*.rb in ApplicationController [David Heinemeier Hansson]
-
-* Integration tests: introduce methods for other HTTP methods. #6353 [caboose]
-
-* Routing: better support for escaped values in route segments. #7544 [Chris
-Roos]
-
-* Introduce a cookie-based session store as the Rails default. Sessions typically contain at most a user_id and flash message; both fit within the 4K cookie size limit. A secure message digest is included with the cookie to ensure data integrity (a user cannot alter his user_id without knowing the secret key included in the digest). If you have more than 4K of session data or don't want your data to be visible to the user, pick another session store. Cookie-based sessions are dramatically faster than the alternatives. [Jeremy Kemper]
-
- Example config/environment.rb:
- # Use an application-wide secret key and the default SHA1 message digest.
- config.action_controller.session = { :secret => "can't touch this" }
-
- # Store a secret key per user and employ a stronger message digest.
- config.action_controller.session = {
- :digest => 'SHA512',
- :secret => Proc.new { User.current.secret_key }
- }
-
-* Added .erb and .builder as preferred aliases to the now deprecated .rhtml and .rxml extensions [Chad Fowler]. This is done to separate the renderer from the mime type. .erb templates are often used to render emails, atom, csv, whatever. So labeling them .rhtml doesn't make too much sense. The same goes for .rxml, which can be used to build everything from HTML to Atom to whatever. .rhtml and .rxml will continue to work until Rails 3.0, though. So this is a slow phasing out. All generators and examples will start using the new aliases, though.
-
-* Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag [David Heinemeier Hansson]. Examples:
-
- stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
- <link href="/stylesheets/style1.css" media="screen" rel="Stylesheet" type="text/css" />
- <link href="/stylesheets/styleB.css" media="screen" rel="Stylesheet" type="text/css" />
- <link href="/stylesheets/styleX2.css" media="screen" rel="Stylesheet" type="text/css" />
-
- stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is true =>
- <link href="/stylesheets/all.css" media="screen" rel="Stylesheet" type="text/css" />
-
- ...when caching is on, all.css is the concatenation of style1.css, styleB.css, and styleX2.css.
- Same deal for JavaScripts.
-
-* Work around the two connection per host browser limit: use asset%d.myapp.com to distribute asset requests among asset[0123].myapp.com. Use a DNS wildcard or CNAMEs to map these hosts to your asset server. See http://www.die.net/musings/page_load_time/ for background. [Jeremy Kemper]
-
-* Added default mime type for CSS (Mime::CSS) [David Heinemeier Hansson]
-
-* Added that rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified and the response body will be set to an empty string. [David Heinemeier Hansson]
-
-* Added X-Runtime to all responses with the request run time [David Heinemeier Hansson]
-
-* Add Mime::Type convenience methods to check the current mime type. [Rick Olson]
-
- request.format.html? # => true if Mime::HTML
- request.format.jpg? # => true if Mime::JPG
-
- # ActionController sample usage:
- # the session will be disabled for non html/ajax requests
- session :off, :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
-
-* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id. [Stefan Kaes]
-
-* Add a :url_based_filename => true option to ActionController::Streaming::send_file, which allows URL-based filenames. [Thomas Fuchs]
-
-* Fix that FormTagHelper#submit_tag using :disable_with should trigger the onsubmit handler of its form if available [David Heinemeier Hansson]
-
-* Fix #render_file so that TemplateError is called with the correct params and you don't get the WSOD. [Rick Olson]
-
-* Fix issue with deprecation messing up #template_root= usage. Add #prepend_view_path and #append_view_path to allow modification of a copy of the
-superclass' view_paths. [Rick Olson]
-
-* Allow Controllers to have multiple view_paths instead of a single template_root. Closes #2754 [John Long]
-
-* Add much-needed html-scanner tests. Fixed CDATA parsing bug. [Rick Olson]
-
-* improve error message for Routing for named routes. Closes #7346 [Rob Sanheim]
-
-* Added enhanced docs to routing assertions. Closes #7359 [Rob Sanheim]
-
-* fix form_for example in ActionController::Resources documentation. Closes #7362 [gnarg]
-
-* Make sure that the string returned by TextHelper#truncate is actually a string, not a char proxy -- that should only be used internally while working on a multibyte-safe way of truncating [David Heinemeier Hansson]
-
-* Added FormBuilder#submit as a delegate for FormTagHelper#submit_tag [David Heinemeier Hansson]
-
-* Allow Routes to generate all urls for a set of options by specifying :generate_all => true. Allows caching to properly set or expire all paths for a resource. References #1739. [Nicholas Seckar]
-
-* Change the query parser to map empty GET params to "" rather than nil. Closes #5694. [Nicholas Seckar]
-
-* date_select and datetime_select take a :default option. #7052 [Nik Wakelin]
- date_select "post", "written_on", :default => 3.days.from_now
- date_select "credit_card", "bill_due", :default => { :day => 20 }
-
-* select :multiple => true suffixes the attribute name with [] unless already suffixed. #6977 [nik.kakelin, ben, julik]
-
-* Improve routes documentation. #7095 [zackchandler]
-
-* mail_to :encode => 'hex' also encodes the mailto: part of the href attribute as well as the linked email when no name is given. #2061 [Jarkko Laine, pfc.pille@gmx.net]
-
-* Resource member routes require :id, eliminating the ambiguous overlap with collection routes. #7229 [dkubb]
-
-* Remove deprecated assertions. [Jeremy Kemper]
-
-* Change session restoration to allow namespaced models to be autoloaded. Closes #6348. [Nicholas Seckar]
-
-* Fix doubly appearing parameters due to string and symbol mixups. Closes #2551. [Anthony Eden]
-
-* Fix overly greedy rescues when loading helpers. Fixes #6268. [Nicholas Seckar]
-
-* Fixed NumberHelper#number_with_delimiter to use "." always for splitting the original number, not the delimiter parameter #7389 [ceefour]
-
-* Autolinking recognizes trailing and embedded . , : ; #7354 [Jarkko Laine]
-
-* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268. [imajes]
-
-* Update to script.aculo.us 1.7.0. [Thomas Fuchs]
-
-* Modernize cookie testing code, and increase coverage (Heckle++) #7101 [Kevin Clark]
-
-* Improve Test Coverage for ActionController::Routing::Route#matches_controller_and_action? (Heckle++) #7115 [Kevin Clark]
-
-* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 [Kevin Clark]
-
-* Fix #distance_of_time_in_words to report accurately against the Duration class. #7114 [eventualbuddha]
-
-* Refactor #form_tag to allow easy extending. [Rick Olson]
-
-* Update to Prototype 1.5.0. [Sam Stephenson]
-
-* RecordInvalid, RecordNotSaved => 422 Unprocessable Entity, StaleObjectError => 409 Conflict. #7097 [dkubb]
-
-* Allow fields_for to be nested inside form_for, so that the name and id get properly constructed [Jamis Buck]
-
-* Allow inGroupsOf and eachSlice to be called through rjs. #7046 [Cody Fauser]
-
-* Allow exempt_from_layout :rhtml. #6742, #7026 [Dan Manges, Squeegy]
-
-* Recognize the .txt extension as Mime::TEXT [Rick Olson]
-
-* Fix parsing of array[] CGI parameters so extra empty values aren't included. #6252 [Nicholas Seckar, aiwilliams, brentrowland]
-
-* link_to_unless_current works with full URLs as well as paths. #6891 [Jarkko Laine, Manfred Stienstra, idrifter]
-
-* Lookup the mime type for #auto_discovery_link_tag in the Mime::Type class. Closes #6941 [Josh Peek]
-
-* Fix bug where nested resources ignore a parent singleton parent's path prefix. Closes #6940 [Dan Kubb]
-
-* Fix no method error with error_messages_on. Closes #6935 [nik.wakelin Koz]
-
-* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs. Closes #6922 [Steven Bristol]
-
-* Slight doc tweak to #prepend_filter. Closes #6493 [Jeremy Voorhis]
-
-* Add more extensive documentation to the AssetTagHelper. Closes #6452 [Bob Silva]
-
-* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper. Closes #6394 [Bob Silva]
-
-* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 [Jan Prill]
-
-* Make sure html_document is reset between integration test requests. [ctm]
-
-* Set session to an empty hash if :new_session => false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError. [Josh Susser]
-
-* Routing uses URI escaping for path components and CGI escaping for query parameters. [darix, Jeremy Kemper]
-
-* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting. Closes #6128. [Rick Olson]
-
-* Singleton resources: POST /singleton => create, GET /singleton/new => new. [Jeremy Kemper]
-
-* Use 400 Bad Request status for unrescued ActiveRecord::RecordInvalid exceptions. [Jeremy Kemper]
-
-* Silence log_error deprecation warnings from inspecting deprecated instance variables. [Nate Wiger]
-
-* Only cache GET requests with a 200 OK response. #6514, #6743 [RSL, anamba]
-
-* Add a 'referer' attribute to TestRequest. [Jamis Buck]
-
-* Ensure render :json => ... skips the layout. Closes #6808 [Josh Peek]
-
-* Fix HTML::Node to output double quotes instead of single quotes. Closes #6845 [mitreandy]
-
-* Correctly report which filter halted the chain. #6699 [Martin Emde]
-
-* Fix a bug in Routing where a parameter taken from the path of the current request could not be used as a query parameter for the next. Closes #6752. [Nicholas Seckar]
-
-* Unrescued ActiveRecord::RecordNotFound responds with 404 instead of 500. [Jeremy Kemper]
-
-* Improved auto_link to match more valid urls correctly [Tobias Lütke]
-
-* Add singleton resources. [Rick Olson]
-
- map.resource :account
-
- GET /account
- GET /account;edit
- UPDATE /account
- DELETE /account
-
-* respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument. #4185 [Scott Raymond, eventualbuddha]
- # application/json response with body 'Element.show({:name: "David"})'
- respond_to do |format|
- format.json { render :json => { :name => "David" }.to_json, :callback => 'Element.show' }
- end
-
-* Makes :discard_year work without breaking multi-attribute parsing in AR. #1260, #3800 [sean@ardismg.com, jmartin@desertflood.com, stephen@touset.org, Bob Silva]
-
-* Adds html id attribute to date helper elements. #1050, #1382 [mortonda@dgrmm.net, David North, Bob Silva]
-
-* Add :index and @auto_index capability to model driven date/time selects. #847, #2655 [moriq, Doug Fales, Bob Silva]
-
-* Add :order to datetime_select, select_datetime, and select_date. #1427 [Timothee Peignier, Patrick Lenz, Bob Silva]
-
-* Added time_select to work with time values in models. Update scaffolding. #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
-
-* Added :include_seconds to select_datetime, datetime_select and time_select. #2998 [csn, Bob Silva]
-
-* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization. #363 [tomasj, Bob Silva]
-
-* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC. #3811 [Bob Silva]
-
-* Added map.root as an alias for map.connect '' [David Heinemeier Hansson]
-
-* Added Request#format to return the format used for the request as a mime type. If no format is specified, the first Request#accepts type is used. This means you can stop using respond_to for anything else than responses [David Heinemeier Hansson]. Examples:
-
- GET /posts/5.xml | request.format => Mime::XML
- GET /posts/5.xhtml | request.format => Mime::HTML
- GET /posts/5 | request.format => request.accepts.first (usually Mime::HTML for browsers)
-
-* Added the option for extension aliases to mime type registration [David Heinemeier Hansson]. Example (already in the default routes):
-
- Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
-
- ...will respond on both .html and .xhtml.
-
-* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status. [Jeremy Kemper]
-
-* Added CSV to Mime::SET so that respond_to csv will work [Cody Fauser]
-
-* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [David Heinemeier Hansson]
-
-* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [David Heinemeier Hansson]
-
-* Update Routing to complain when :controller is not specified by a route. Closes #6669. [Nicholas Seckar]
-
-* Ensure render_to_string cleans up after itself when an exception is raised. #6658 [Rob Sanheim]
-
-* Extract template_changed_since? from compile_template? so plugins may override its behavior for non-file-based templates. #6651 [Jeff Barczewski]
-
-* Update to Prototype and script.aculo.us [5579]. [Thomas Fuchs]
-
-* simple_format helper doesn't choke on nil. #6644 [jerry426]
-
-* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again [Thomas Fuchs]
-
-* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. [Nicholas Seckar]
-
-* ActionView::Base.erb_variable accessor names the buffer variable used to render templates. Defaults to _erbout; use _buf for erubis. [Rick Olson]
-
-* assert_select_rjs :remove. [Dylan Egan]
-
-* Always clear model associations from session. #4795 [sd@notso.net, andylien@gmail.com]
-
-* Update to Prototype 1.5.0_rc2. [Sam Stephenson]
-
-* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. [Sam Stephenson]
-
-* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes. #6586 [dkubb]
-
-* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename. #6401 [Andreas Schwarz, Jeremy Kemper]
-
-* assert_response supports symbolic status codes. #6569 [Kevin Clark]
- assert_response :ok
- assert_response :not_found
- assert_response :forbidden
-
-* Cache parsed query parameters. #6559 [Stefan Kaes]
-
-* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS [Thomas Fuchs]
-
-* pluralize helper interprets nil as zero. #6474 [Tim Pope]
-
-* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing. #6524 [Bob Silva]
-
-* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods. [Jeremy Kemper]
-
-* Deprecate expire_matched_fragments. Use expire_fragment instead. #6535 [Bob Silva]
-
-* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs]
-
-* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. [Rick Olson]
-
-* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick Olson]
-
-* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. [Rick Olson]
-
-* Added block-usage to TagHelper#content_tag [David Heinemeier Hansson]. Example:
-
- <% content_tag :div, :class => "strong" %>
- Hello world!
- <% end %>
-
- Will output:
- <div class="strong">Hello world!</div>
-
-* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [Bob Silva]
-
-* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [Bob Silva]
-
-* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [Bob Silva/chrismear]
-
-* Force *_url named routes to show the host in ActionView [Rick Olson]
-
- <%= url_for ... %> # no host
- <%= foo_path %> # no host
- <%= foo_url %> # host!
-
-* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. [Sam Stephenson]
-
-* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
-
-* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. [Rick Olson]
-
-* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick Olson]
-
-* Fix deprecation warnings when rendering the template error template. [Nicholas Seckar]
-
-* Fix routing to correctly determine when generation fails. Closes #6300. [psross].
-
-* Fix broken assert_generates when extra keys are being checked. [Jamis Buck]
-
-* Replace KCODE checks with String#chars for truncate. Closes #6385 [Manfred Stienstra]
-
-* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'. [Marcel Molina Jr.]
-
-* Use String#chars in TextHelper::excerpt. Closes #6386 [Manfred Stienstra]
-
-* Install named routes into ActionView::Base instead of proxying them to the view via helper_method. Closes #5932. [Nicholas Seckar]
-
-* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
-
-* Fix relative URL root matching problems. [Mark Imbriaco]
-
-* Fix filter skipping in controller subclasses. #5949, #6297, #6299 [Martin Emde]
-
-* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API). [Jeremy Kemper]
-
-* Rename test assertion to prevent shadowing. Closes #6306. [psross]
-
-* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [Philip Hallstrom]
-
-* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
-
-* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 [tzaharia]. Example:
-
- update_page_tag :defer => 'true' { |page| ... }
-
- Gives:
-
- <script defer="true" type="text/javascript">...</script>
-
- Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
-
-* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [Mike Naberezny]
-
-* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven@c3d2.de]
-
-* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 [cmselmer]
-
-* respond_to :html doesn't assume .rhtml. #6281 [Hampton Catlin]
-
-* Fixed some deprecation warnings in ActionPack [Rick Olson]
-
-* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them. #6240 [japgolly]
-
-* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead. [Jeremy Kemper]
-
-* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. [Jamis Buck]. Examples:
-
- head :status => 404 # expands to "404 Not Found"
- head :status => :not_found # expands to "404 Not Found"
- head :status => :created # expands to "201 Created"
-
-* Add head(options = {}) for responses that have no body. [Jamis Buck]. Examples:
-
- head :status => 404 # return an empty response with a 404 status
- head :location => person_path(@person), :status => 201
-
-* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain. [Rick Olson]
-
-* strip_links is case-insensitive. #6285 [tagoh, Bob Silva]
-
-* Clear the cache of possible controllers whenever Routes are reloaded. [Nicholas Seckar]
-
-* Filters overhaul including meantime filter support using around filters + blocks. #5949 [Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper]
-
-* Update RJS render tests. [sam]
-
-* Update CGI process to allow sessions to contain namespaced models. Closes #4638. [dfelstead@site5.com]
-
-* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action => 'index'). Closes #5950. [Nicholas Seckar]
-
-* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability. #3368, #6226 [sdsykes, fhanshaw@vesaria.com]
-
-* Added that respond_to blocks will automatically set the content type to be the same as is requested [David Heinemeier Hansson]. Examples:
-
- respond_to do |format|
- format.html { render :text => "I'm being sent as text/html" }
- format.rss { render :text => "I'm being sent as application/rss+xml" }
- format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
- end
-
-* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [David Heinemeier Hansson]
-
-* Added proper getters and setters for content type and charset [David Heinemeier Hansson]. Example of what we used to do:
-
- response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
-
- ...now:
-
- response.content_type = Mime::ATOM
- response.charset = "utf-8"
-
-* Updated prototype.js to 1.5.0_rc1 with latest fixes. [Rick Olson]
-
- - XPATH support
- - Make Form.getElements() return elements in the correct order
- - fix broken Form.serialize return
-
-* Declare file extensions exempt from layouts. #6219 [brandon]
- Example: ActionController::Base.exempt_from_layout 'rpdf'
-
-* Add chained replace/update support for assert_select_rjs [Rick Olson]
-
- Given RJS like...
-
- page['test1'].replace "<div id=\"1\">foo</div>"
- page['test2'].replace_html "<div id=\"2\">foo</div>"
-
- Test it with...
-
- assert_select_rjs :chained_replace
- assert_select_rjs :chained_replace, "test1"
-
- assert_select_rjs :chained_replace_html
- assert_select_rjs :chained_replace_html, "test2"
-
-* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency. #6132, #6178 [choonkeat@gmail.com]
-
-* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 [Manfred Stienstra, raphinou@yahoo.com]
-
-* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 [Eric Hodel]
-
-* Add descriptive messages to the exceptions thrown by cgi_methods. #6091, #6103 [Nicholas Seckar, Bob Silva]
-
-* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids, #6068 [petermichaux@gmail.com]
-
-* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
-
-* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [David Heinemeier Hansson]. So what used to require a nil, like this:
-
- link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
-
- ...can be written like this:
-
- link_to("Hider", :class => "hider_link") { |p| p[:something].hide }
-
-* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
-
-* Update to Prototype 1.5.0_rc1 [sam]
-
-* Added access to nested attributes in RJS #4548 [richcollins@gmail.com]. Examples:
-
- page['foo']['style'] # => $('foo').style;
- page['foo']['style']['color'] # => $('blank_slate').style.color;
- page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
- page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
-
-* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule@space.ch]
-
-* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [David Heinemeier Hansson]
-
-* Fixed FormOptionsHelper#select to respect :selected value #5813
-
-* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 [moriq@moriq.com]
-
-* Fixed TextHelper#pluralize to handle 1 as a string #5909 [rails@bencurtis.com]
-
-* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
-
-* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [David Heinemeier Hansson]
-
-* Integration tests: thoroughly test ActionController::Integration::Session. #6022 [Kevin Clark]
- (tests skipped unless you `gem install mocha`)
-
-* Added deprecation language for pagination which will become a plugin by Rails 2.0 [David Heinemeier Hansson]
-
-* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [David Heinemeier Hansson]
-
-* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [David Heinemeier Hansson]
-
-* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin@gmail.com]
-
-* radio_button_tag generates unique id attributes. #3353 [Bob Silva, somekool@gmail.com]
-
-* strip_tags passes through blank args such as nil or "". #2229, #6702 [duncan@whomwah.com, dharana]
-
-* Cleanup assert_tag :children counting. #2181 [jamie@bravenet.com]
-
-* button_to accepts :method so you can PUT and DELETE with it. #6005 [Dan Webb]
-
-* Update sanitize text helper to strip plaintext tags, and <img src="javascript:bang">. [Rick Olson]
-
-* Update routing documentation. Closes #6017 [Nathan Witmer]
-
-* Add routing tests to assert that RoutingError is raised when conditions aren't met. Closes #6016 [Nathan Witmer]
-
-* Deprecation: update docs. #5998 [Jakob Skjerning, Kevin Clark]
-
-* Make auto_link parse a greater subset of valid url formats. [Jamis Buck]
-
-* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. [Mike Clark]
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Fix send_data documentation typo. #5982 [brad@madriska.com]
-
-* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, David Heinemeier Hansson]
-
-* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Lütke]
-
-* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
-
-* Add UrlWriter to allow writing urls from Mailers and scripts. [Nicholas Seckar]
-
-* Clean up and run the Active Record integration tests by default. #5854 [Kevin Clark, Jeremy Kemper]
-
-* Correct example in cookies docs. #5832 [jessemerriman@warpmail.net]
-
-* Updated to script.aculo.us 1.6.2 [Thomas Fuchs]
-
-* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. [Nicholas Seckar]
-
-* Add controller_paths variable to Routing. [Nicholas Seckar]
-
-* Fix assert_redirected_to issue with named routes for module controllers. [Rick Olson]
-
-* Tweak RoutingError message to show option diffs, not just missing named route significant keys. [Rick Olson]
-
-* Invoke method_missing directly on hidden actions. Closes #3030. [Nicholas Seckar]
-
-* Require Tempfile explicitly for TestUploadedFile due to changes in class auto loading. [Rick Olson]
-
-* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. [Rick Olson]
-
-* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-
-* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. [Jeremy Kemper]
-
-* Add support for the param_name parameter to the auto_complete_field helper. #5026 [david.a.williams@gmail.com]
-
-* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
-
-* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [François Beausoleil]
-
-* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 [pjhyett@gmail.com]
-
-* Short documentation to mention use of Mime::Type.register. #5710 [choonkeat@gmail.com]
-
-* Make controller_path available as an instance method. #5724 [jmckible@gmail.com]
-
-* Update query parser to support adjacent hashes. [Nicholas Seckar]
-
-* Make action caching aware of different formats for the same action so that, e.g. foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. [Marcel Molina Jr.]
-
-* Restrict Request Method hacking with ?_method to POST requests. [Rick Olson]
-
-* Fix bug when passing multiple options to SimplyRestful, like :new => { :preview => :get, :draft => :get }. [Rick Olson, Josh Susser, Lars Pind]
-
-* Dup the options passed to map.resources so that multiple resources get the same options. [Rick Olson]
-
-* Fixed the new_#{resource}_url route and added named route tests for Simply Restful. [Rick Olson]
-
-* Added map.resources from the Simply Restful plugin [David Heinemeier Hansson]. Examples (the API has changed to use plurals!):
-
- map.resources :messages
- map.resources :messages, :comments
- map.resources :messages, :new => { :preview => :post }
-
-* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
-
-* TestRequest#reset_session should restore a TestSession, not a hash [Michael Koziarski]
-
-* Don't search a load-path of '.' for controller files [Jamis Buck]
-
-* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
-
-* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 [lmarlow]
-
-* Allow form_for and fields_for to work with indexed form inputs. [Jeremy Kemper, Matt Lyon]
-
- <% form_for 'post[]', @post do |f| -%>
- <% end -%>
-
-* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
-
-* Provide support for decimal columns to form helpers. Closes #5672. [Dave Thomas]
-
-* Update documentation for erb trim syntax. #5651 [matt@mattmargolis.net]
-
-* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com]
-
-* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
-
-* Update render :partial documentation. #5646 [matt@mattmargolis.net]
-
-* Integration tests behave well with render_component. #4632 [edward.frederick@revolution.com, dev.rubyonrails@maxdunn.com]
-
-* Added exception handling of missing layouts #5373 [chris@ozmm.org]
-
-* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary@panandscan.com]
-
-* Fixed that the flash should be reset when reset_session is called #5584 [Shugo Maeda]
-
-* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 [murpyh@rubychan.de]
-
-* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [David Heinemeier Hansson]
-
-* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
-
-* Avoid naming collision among compiled view methods. [Jeremy Kemper]
-
-* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [Mislav Marohnić]
-
-* Determine the correct template_root for deeply nested components. #2841 [s.brink@web.de]
-
-* Fix that routes with *path segments in the recall can generate URLs. [Rick Olson]
-
-* Fix strip_links so that it doesn't hang on multiline <acronym> tags [Jamis Buck]
-
-* Remove problematic control chars in rescue template. #5316 [Stefan Kaes]
-
-* Make sure passed routing options are not mutated by routing code. #5314 [Blair Zajac]
-
-* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. [Jamis Buck]
-
-* Escape the path before routing recognition. #3671
-
-* Make sure :id and friends are unescaped properly. #5275 [me@julik.nl]
-
-* Fix documentation for with_routing to reflect new reality. #5281 [rramdas@gmail.com]
-
-* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form). #210 [mklame@atxeu.com, matthew@walker.wattle.id.au]
-
-* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [David Heinemeier Hansson]
-
-* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [David Heinemeier Hansson]. Example: Mime::Type.register("image/gif", :gif)
-
-* Added support for Mime objects in render :content_type option [David Heinemeier Hansson]. Example: render :text => some_atom, :content_type => Mime::ATOM
-
-* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 [Manfred Stienstra <m.stienstra@fngtps.com>]
-
-* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. [Nicholas Seckar, Jamis Buck]
-
- map.connect '/foo/:id', :controller => '...', :action => '...'
- map.connect '/foo/:id.:format', :controller => '...', :action => '...'
- map.connect '/foo/:id', ..., :conditions => { :method => :get }
-
-* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper]
-
-* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com]
-
-* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [David Heinemeier Hansson]. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find :all
-
- respond_to do |format|
- format.html
- format.xml { render :xml => @posts.to_xml }
- format.rss { render :action => "feed.rxml" }
- end
- end
- end
-
- # returns HTML when requested by a browser, since the browser
- # has the HTML mimetype at the top of its priority list
- Accept: text/html
- GET /weblog
-
- # returns the XML
- Accept: application/xml
- GET /weblog
-
- # returns the HTML
- Accept: application/xml
- GET /weblog.html
-
- # returns the XML
- Accept: text/html
- GET /weblog.xml
-
- All this relies on the fact that you have a route that includes .:format.
-
-* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [David Heinemeier Hansson]
-
-* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [David Heinemeier Hansson]
-
-* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo@mk.ukrtelecom.ua]
-
-* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark]
-
-* Fix NoMethodError when parsing params like &&. [Adam Greenfield]
-
-* Fix flip flopped logic in docs for url_for's :only_path option. Closes #4998. [esad@esse.at]
-
-* form.text_area handles the :size option just like the original text_area (:size => '60x10' becomes cols="60" rows="10"). [Jeremy Kemper]
-
-* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
-
-* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick Olson]
-
- map.connect '*path', :controller => 'files', :action => 'show'
-
-* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
-
-* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-
-* Use #flush between switching from #write to #syswrite. Closes #4907. [Blair Zajac <blair@orcaware.com>]
-
-* Documentation fix: integration test scripts don't require integration_test. Closes #4914. [Frederick Ros <sl33p3r@free.fr>]
-
-* ActionController::Base Summary documentation rewrite. Closes #4900. [Kevin Clark]
-
-* Fix text_helper.rb documentation rendering. Closes #4725. [Frederick Ros]
-
-* Fixes bad rendering of JavaScriptMacrosHelper rdoc (closes #4910) [Frederick Ros]
-
-* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. [andrew@redlinesoftware.com, Marcel Molina Jr.]
-
- error_messages_for :account, :user, :subscription, :object_name => :account
-
-* Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there. Closes #4079. [Rick Olson]
-
-* Documentation for AbstractRequest. Closes #4895. [Kevin Clark]
-
-* Refactor various InstanceTag instance method to class methods. Closes #4800. [Stefan Kaes]
-
-* Remove all remaining references to @params in the documentation. [Marcel Molina Jr.]
-
-* Add documentation for redirect_to :back's RedirectBackError exception. [Marcel Molina Jr.]
-
-* Update layout and content_for documentation to use yield rather than magic @content_for instance variables. [Marcel Molina Jr.]
-
-* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: [Rick Olson]
-
- redirect_to :action => 'new'
- assert_redirected_to :controller => 'monkeys', :action => 'new'
-
-* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick Olson]
-
-* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick Olson]
-
- assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
- assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
-
-* Diff compared options with #assert_redirected_to [Rick Olson]
-
-* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck]
-
-* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii@comcast.net, Sam Stephenson]
-
-* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [David Heinemeier Hansson]
-
-* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. [Jamis Buck]
-
-* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. [Sam Stephenson]
- ex. verify :only => :speak, :method => :post,
- :render => { :status => 405, :text => "Must be post" },
- :add_headers => { "Allow" => "POST" }
-
-* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled #1897 [jeremye@bsa.ca.gov]
-
-
-*1.13.3* (March 12th, 2007)
-
-* Apply [5709] to stable.
-
-* session_enabled? works with session :off. #6680 [Jonathan del Strother]
-
-* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id. [Stefan Kaes]
-
-
-*1.13.2* (February 5th, 2007)
-
-* Add much-needed html-scanner tests. Fixed CDATA parsing bug. [Rick Olson]
-
-* improve error message for Routing for named routes. [Rob Sanheim]
-
-* Added enhanced docs to routing assertions. [Rob Sanheim]
-
-* fix form_for example in ActionController::Resources documentation. [gnarg]
-
-* Add singleton resources from trunk [Rick Olson]
-
-* select :multiple => true suffixes the attribute name with [] unless already suffixed. #6977 [nik.kakelin, ben, julik]
-
-* Improve routes documentation. #7095 [zackchandler]
-
-* Resource member routes require :id, eliminating the ambiguous overlap with collection routes. #7229 [dkubb]
-
-* Fixed NumberHelper#number_with_delimiter to use "." always for splitting the original number, not the delimiter parameter #7389 [ceefour]
-
-* Autolinking recognizes trailing and embedded . , : ; #7354 [Jarkko Laine]
-
-* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268. [imajes]
-
-* Improved auto_link to match more valid urls correctly [Tobias Lütke]
-
-
-*1.13.1* (January 18th, 2007)
-
-* Fixed content-type bug in Prototype [sam]
-
-
-*1.13.0* (January 16th, 2007)
-
-* Modernize cookie testing code, and increase coverage (Heckle++) #7101 [Kevin Clark]
-
-* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 [Kevin Clark]
-
-* Update to Prototype 1.5.0. [Sam Stephenson]
-
-* Allow exempt_from_layout :rhtml. #6742, #7026 [Dan Manges, Squeegy]
-
-* Fix parsing of array[] CGI parameters so extra empty values aren't included. #6252 [Nicholas Seckar, aiwilliams, brentrowland]
-
-* link_to_unless_current works with full URLs as well as paths. #6891 [Jarkko Laine, Manfred Stienstra, idrifter]
-
-* Fix HTML::Node to output double quotes instead of single quotes. Closes #6845 [mitreandy]
-
-* Fix no method error with error_messages_on. Closes #6935 [nik.wakelin Koz]
-
-* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs. Closes #6922 [Steven Bristol]
-
-* Slight doc tweak to #prepend_filter. Closes #6493 [Jeremy Voorhis]
-
-* Add more extensive documentation to the AssetTagHelper. Closes #6452 [Bob Silva]
-
-* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper. Closes #6394 [Bob Silva]
-
-* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 [Jan Prill]
-
-* Make sure html_document is reset between integration test requests. [ctm]
-
-* Set session to an empty hash if :new_session => false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError. [Josh Susser]
-
-* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting. Closes #6128. [Rick Olson]
-
-* Ensure render :json => ... skips the layout. #6808 [Josh Peek]
-
-* Silence log_error deprecation warnings from inspecting deprecated instance variables. [Nate Wiger]
-
-* Only cache GET requests with a 200 OK response. #6514, #6743 [RSL, anamba]
-
-* Correctly report which filter halted the chain. #6699 [Martin Emde]
-
-* respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument. #4185 [Scott Raymond, eventualbuddha]
- # application/json response with body 'Element.show({:name: "David"})'
- respond_to do |format|
- format.json { render :json => { :name => "David" }.to_json, :callback => 'Element.show' }
- end
-
-* Makes :discard_year work without breaking multi-attribute parsing in AR. #1260, #3800 [sean@ardismg.com, jmartin@desertflood.com, stephen@touset.org, Bob Silva]
-
-* Adds html id attribute to date helper elements. #1050, #1382 [mortonda@dgrmm.net, David North, Bob Silva]
-
-* Add :index and @auto_index capability to model driven date/time selects. #847, #2655 [moriq, Doug Fales, Bob Silva]
-
-* Add :order to datetime_select, select_datetime, and select_date. #1427 [Timothee Peignier, Patrick Lenz, Bob Silva]
-
-* Added time_select to work with time values in models. Update scaffolding. #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
-
-* Added :include_seconds to select_datetime, datetime_select and time_select. #2998 [csn, Bob Silva]
-
-* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization. #363 [tomasj, Bob Silva]
-
-* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC. #3811 [Bob Silva]
-
-* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status. [Jeremy Kemper]
-
-* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [David Heinemeier Hansson]
-
-* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [David Heinemeier Hansson]
-
-
-*1.13.0 RC1* (r5619, November 22nd, 2006)
-
-* Update Routing to complain when :controller is not specified by a route. Closes #6669. [Nicholas Seckar]
-
-* Ensure render_to_string cleans up after itself when an exception is raised. #6658 [rsanheim]
-
-* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs]
-
-* simple_format helper doesn't choke on nil. #6644 [jerry426]
-
-* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. [Nicholas Seckar]
-
-* Deprecate standalone components. [Jeremy Kemper]
-
-* Always clear model associations from session. #4795 [sd@notso.net, andylien@gmail.com]
-
-* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. [Sam Stephenson]
-
-* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes. #6586 [dkubb]
-
-* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename. #6401 [Andreas Schwarz, Jeremy Kemper]
-
-* assert_response supports symbolic status codes. #6569 [Kevin Clark]
- assert_response :ok
- assert_response :not_found
- assert_response :forbidden
-
-* Cache parsed query parameters. #6559 [Stefan Kaes]
-
-* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS [Thomas Fuchs]
-
-* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing. #6524 [Bob Silva]
-
-* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods. [Jeremy Kemper]
-
-* Deprecate expire_matched_fragments. Use expire_fragment instead. #6535 [Bob Silva]
-
-* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. [Rick Olson]
-
-* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick Olson]
-
-* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. [Rick Olson]
-
-* Added block-usage to TagHelper#content_tag [David Heinemeier Hansson]. Example:
-
- <% content_tag :div, :class => "strong" %>
- Hello world!
- <% end %>
-
- Will output:
- <div class="strong">Hello world!</div>
-
-* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [Bob Silva]
-
-* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [Bob Silva]
-
-* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [Bob Silva/chrismear]
-
-* Force *_url named routes to show the host in ActionView [Rick Olson]
-
- <%= url_for ... %> # no host
- <%= foo_path %> # no host
- <%= foo_url %> # host!
-
-* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. [Sam Stephenson]
-
-* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
-
-* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. [Rick Olson]
-
-* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick Olson]
-
-* Fix routing to correctly determine when generation fails. Closes #6300. [psross].
-
-* Fix broken assert_generates when extra keys are being checked. [Jamis Buck]
-
-* Replace KCODE checks with String#chars for truncate. Closes #6385 [Manfred Stienstra]
-
-* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'. [Marcel Molina Jr.]
-
-* Use String#chars in TextHelper::excerpt. Closes #6386 [Manfred Stienstra]
-
-* Fix relative URL root matching problems. [Mark Imbriaco]
-
-* Fix filter skipping in controller subclasses. #5949, #6297, #6299 [Martin Emde]
-
-* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API). [Jeremy Kemper]
-
-* Rename test assertion to prevent shadowing. Closes #6306. [psross]
-
-* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [Philip Hallstrom]
-
-* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
-
-* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 [tzaharia]. Example:
-
- update_page_tag :defer => 'true' { |page| ... }
-
- Gives:
-
- <script defer="true" type="text/javascript">...</script>
-
- Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
-
-* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [Mike Naberezny]
-
-* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven@c3d2.de]
-
-* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 [cmselmer]
-
-* respond_to :html doesn't assume .rhtml. #6281 [Hampton Catlin]
-
-* Fixed some deprecation warnings in ActionPack [Rick Olson]
-
-* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them. #6240 [japgolly]
-
-* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead. [Jeremy Kemper]
-
-* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. [Jamis Buck]. Examples:
-
- head :status => 404 # expands to "404 Not Found"
- head :status => :not_found # expands to "404 Not Found"
- head :status => :created # expands to "201 Created"
-
-* Add head(options = {}) for responses that have no body. [Jamis Buck]. Examples:
-
- head :status => 404 # return an empty response with a 404 status
- head :location => person_path(@person), :status => 201
-
-* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain. [Rick Olson]
-
-* strip_links is case-insensitive. #6285 [tagoh, Bob Silva]
-
-* Clear the cache of possible controllers whenever Routes are reloaded. [Nicholas Seckar]
-
-* Filters overhaul including meantime filter support using around filters + blocks. #5949 [Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper]
-
-* Update CGI process to allow sessions to contain namespaced models. Closes #4638. [dfelstead@site5.com]
-
-* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action => 'index'). Closes #5950. [Nicholas Seckar]
-
-* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability. #3368, #6226 [sdsykes, fhanshaw@vesaria.com]
-
-* Added that respond_to blocks will automatically set the content type to be the same as is requested [David Heinemeier Hansson]. Examples:
-
- respond_to do |format|
- format.html { render :text => "I'm being sent as text/html" }
- format.rss { render :text => "I'm being sent as application/rss+xml" }
- format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
- end
-
-* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [David Heinemeier Hansson]
-
-* Added proper getters and setters for content type and charset [David Heinemeier Hansson]. Example of what we used to do:
-
- response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
-
- ...now:
-
- response.content_type = Mime::ATOM
- response.charset = "utf-8"
-
-* Declare file extensions exempt from layouts. #6219 [brandon]
- Example: ActionController::Base.exempt_from_layout 'rpdf'
-
-* Add chained replace/update support for assert_select_rjs [Rick Olson]
-
- Given RJS like...
-
- page['test1'].replace "<div id=\"1\">foo</div>"
- page['test2'].replace_html "<div id=\"2\">foo</div>"
-
- Test it with...
-
- assert_select_rjs :chained_replace
- assert_select_rjs :chained_replace, "test1"
-
- assert_select_rjs :chained_replace_html
- assert_select_rjs :chained_replace_html, "test2"
-
-* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency. #6132, #6178 [choonkeat@gmail.com]
-
-* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 [Manfred Stienstra, raphinou@yahoo.com]
-
-* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 [Eric Hodel]
-
-* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids, #6068 [petermichaux@gmail.com]
-
-* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
-
-* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [David Heinemeier Hansson]. So what used to require a nil, like this:
-
- link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
-
- ...can be written like this:
-
- link_to("Hider", :class => "hider_link") { |p| p[:something].hide }
-
-* Added access to nested attributes in RJS #4548 [richcollins@gmail.com]. Examples:
-
- page['foo']['style'] # => $('foo').style;
- page['foo']['style']['color'] # => $('blank_slate').style.color;
- page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
- page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
-
-* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule@space.ch]
-
-* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [David Heinemeier Hansson]
-
-* Fixed FormOptionsHelper#select to respect :selected value #5813
-
-* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 [moriq@moriq.com]
-
-* Fixed TextHelper#pluralize to handle 1 as a string #5909 [rails@bencurtis.com]
-
-* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
-
-* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [David Heinemeier Hansson]
-
-* Added deprecation language for pagination which will become a plugin by Rails 2.0 [David Heinemeier Hansson]
-
-* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [David Heinemeier Hansson]
-
-* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [David Heinemeier Hansson]
-
-* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin@gmail.com]
-
-* radio_button_tag generates unique id attributes. #3353 [Bob Silva, somekool@gmail.com]
-
-* strip_tags passes through blank args such as nil or "". #2229, #6702 [duncan@whomwah.com, dharana]
-
-* Cleanup assert_tag :children counting. #2181 [jamie@bravenet.com]
-
-* button_to accepts :method so you can PUT and DELETE with it. #6005 [Dan Webb]
-
-* Update sanitize text helper to strip plaintext tags, and <img src="javascript:bang">. [Rick Olson]
-
-* Add routing tests to assert that RoutingError is raised when conditions aren't met. Closes #6016 [Nathan Witmer]
-
-* Make auto_link parse a greater subset of valid url formats. [Jamis Buck]
-
-* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. [Mike Clark]
-
-* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, David Heinemeier Hansson]
-
-* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Lütke]
-
-* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
-
-* Add UrlWriter to allow writing urls from Mailers and scripts. [Nicholas Seckar]
-
-* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. [Nicholas Seckar]
-
-* Add controller_paths variable to Routing. [Nicholas Seckar]
-
-* Fix assert_redirected_to issue with named routes for module controllers. [Rick Olson]
-
-* Tweak RoutingError message to show option diffs, not just missing named route significant keys. [Rick Olson]
-
-* Invoke method_missing directly on hidden actions. Closes #3030. [Nicholas Seckar]
-
-* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. [Rick Olson]
-
-* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-
-* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. [Jeremy Kemper]
-
-* Add support for the param_name parameter to the auto_complete_field helper. #5026 [david.a.williams@gmail.com]
-
-* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
-
-* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [François Beausoleil]
-
-* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 [pjhyett@gmail.com]
-
-* Make controller_path available as an instance method. #5724 [jmckible@gmail.com]
-
-* Update query parser to support adjacent hashes. [Nicholas Seckar]
-
-* Make action caching aware of different formats for the same action so that, e.g. foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. [Marcel Molina Jr.]
-
-* Restrict Request Method hacking with ?_method to POST requests. [Rick Olson]
-
-* Fixed the new_#{resource}_url route and added named route tests for Simply Restful. [Rick Olson]
-
-* Added map.resources from the Simply Restful plugin [David Heinemeier Hansson]. Examples (the API has changed to use plurals!):
-
- map.resources :messages
- map.resources :messages, :comments
- map.resources :messages, :new => { :preview => :post }
-
-* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
-
-* TestRequest#reset_session should restore a TestSession, not a hash [Michael Koziarski]
-
-* Don't search a load-path of '.' for controller files [Jamis Buck]
-
-* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
-
-* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 [lmarlow]
-
-* Allow form_for and fields_for to work with indexed form inputs. [Jeremy Kemper, Matt Lyon]
-
- <% form_for 'post[]', @post do |f| -%>
- <% end -%>
-
-* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
-
-* Provide support for decimal columns to form helpers. Closes #5672. [Dave Thomas]
-
-* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com]
-
-* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
-
-* Integration tests behave well with render_component. #4632 [edward.frederick@revolution.com, dev.rubyonrails@maxdunn.com]
-
-* Added exception handling of missing layouts #5373 [chris@ozmm.org]
-
-* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary@panandscan.com]
-
-* Fixed that the flash should be reset when reset_session is called #5584 [Shugo Maeda]
-
-* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 [murpyh@rubychan.de]
-
-* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [David Heinemeier Hansson]
-
-* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
-
-* Avoid naming collision among compiled view methods. [Jeremy Kemper]
-
-* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [Mislav Marohnić]
-
-* Determine the correct template_root for deeply nested components. #2841 [s.brink@web.de]
-
-* Fix that routes with *path segments in the recall can generate URLs. [Rick Olson]
-
-* Fix strip_links so that it doesn't hang on multiline <acronym> tags [Jamis Buck]
-
-* Remove problematic control chars in rescue template. #5316 [Stefan Kaes]
-
-* Make sure passed routing options are not mutated by routing code. #5314 [Blair Zajac]
-
-* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. [Jamis Buck]
-
-* Escape the path before routing recognition. #3671
-
-* Make sure :id and friends are unescaped properly. #5275 [me@julik.nl]
-
-* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form). #210 [mklame@atxeu.com, matthew@walker.wattle.id.au]
-
-* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [David Heinemeier Hansson]
-
-* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [David Heinemeier Hansson]. Example: Mime::Type.register("image/gif", :gif)
-
-* Added support for Mime objects in render :content_type option [David Heinemeier Hansson]. Example: render :text => some_atom, :content_type => Mime::ATOM
-
-* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 [Manfred Stienstra <m.stienstra@fngtps.com>]
-
-* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. [Nicholas Seckar, Jamis Buck]
-
- map.connect '/foo/:id', :controller => '...', :action => '...'
- map.connect '/foo/:id.:format', :controller => '...', :action => '...'
- map.connect '/foo/:id', ..., :conditions => { :method => :get }
-
-* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper]
-
-* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com]
-
-* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [David Heinemeier Hansson]. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find :all
-
- respond_to do |format|
- format.html
- format.xml { render :xml => @posts.to_xml }
- format.rss { render :action => "feed.rxml" }
- end
- end
- end
-
- # returns HTML when requested by a browser, since the browser
- # has the HTML mimetype at the top of its priority list
- Accept: text/html
- GET /weblog
-
- # returns the XML
- Accept: application/xml
- GET /weblog
-
- # returns the HTML
- Accept: application/xml
- GET /weblog.html
-
- # returns the XML
- Accept: text/html
- GET /weblog.xml
-
- All this relies on the fact that you have a route that includes .:format.
-
-* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [David Heinemeier Hansson]
-
-* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [David Heinemeier Hansson]
-
-* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo@mk.ukrtelecom.ua]
-
-* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark]
-
-* Fix NoMethodError when parsing params like &&. [Adam Greenfield]
-
-* form.text_area handles the :size option just like the original text_area (:size => '60x10' becomes cols="60" rows="10"). [Jeremy Kemper]
-
-* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
-
-* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick Olson]
-
- map.connect '*path', :controller => 'files', :action => 'show'
-
-* Use #flush between switching from #write to #syswrite. Closes #4907. [Blair Zajac <blair@orcaware.com>]
-
-* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. [andrew@redlinesoftware.com, Marcel Molina Jr.]
-
- error_messages_for :account, :user, :subscription, :object_name => :account
-
-* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: [Rick Olson]
-
- redirect_to :action => 'new'
- assert_redirected_to :controller => 'monkeys', :action => 'new'
-
-* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick Olson]
-
- assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
- assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
-
-* Diff compared options with #assert_redirected_to [Rick Olson]
-
-* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck]
-
-* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii@comcast.net, Sam Stephenson]
-
-* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. [Jamis Buck]
-
-* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. [Sam Stephenson]
- ex. verify :only => :speak, :method => :post,
- :render => { :status => 405, :text => "Must be post" },
- :add_headers => { "Allow" => "POST" }
-
-
-*1.12.5* (August 10th, 2006)
-
-* Updated security fix
-
-
-*1.12.4* (August 8th, 2006)
-
-* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick Olson]
-
-* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [David Heinemeier Hansson]
-
-* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled. #1897 [jeremye@bsa.ca.gov]
-
-* Fixed that real files and symlinks should be treated the same when compiling templates. #5438 [zachary@panandscan.com]
-
-* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 [Manfred Stienstra <m.stienstra@fngtps.com>]
-
-* Update documentation for erb trim syntax. #5651 [matt@mattmargolis.net]
-
-* Short documentation to mention use of Mime::Type.register. #5710 [choonkeat@gmail.com]
-
-
-*1.12.3* (June 28th, 2006)
-
-* Fix broken traverse_to_controller. We now:
- Look for a _controller.rb file under RAILS_ROOT to load.
- If we find it, we require_dependency it and return the controller it defined. (If none was defined we stop looking.)
- If we don't find it, we look for a .rb file under RAILS_ROOT to load. If we find it, and it loads a constant we keep looking.
- Otherwise we check to see if a directory of the same name exists, and if it does we create a module for it.
-
-
-*1.12.2* (June 27th, 2006)
-
-* Refinement to avoid exceptions in traverse_to_controller.
-
-* (Hackish) Fix loading of arbitrary files in Ruby's load path by traverse_to_controller. [Nicholas Seckar]
-
-
-*1.12.1* (April 6th, 2006)
-
-* Fixed that template extensions would be cached development mode #4624 [Stefan Kaes]
-
-* Update to Prototype 1.5.0_rc0 [Sam Stephenson]
-
-* Honor skipping filters conditionally for only certain actions even when the parent class sets that filter to conditionally be executed only for the same actions. #4522 [Marcel Molina Jr.]
-
-* Delegate xml_http_request in integration tests to the session instance. [Jamis Buck]
-
-* Update the diagnostics template skip the useless '<controller not set>' text. [Nicholas Seckar]
-
-* CHANGED DEFAULT: Don't parse YAML input by default, but keep it available as an easy option [David Heinemeier Hansson]
-
-* Add additional autocompleter options [aballai, Thomas Fuchs]
-
-* Fixed fragment caching of binary data on Windows #4493 [bellis@deepthought.org]
-
-* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
-
-* Added automated timestamping to AssetTagHelper methods for stylesheets, javascripts, and images when Action Controller is run under Rails [David Heinemeier Hansson]. Example:
-
- image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?1143664135" />'
-
- ...to avoid frequent stats (not a problem for most people), you can set RAILS_ASSET_ID in the ENV to avoid stats:
-
- ENV["RAILS_ASSET_ID"] = "2345"
- image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?2345" />'
-
- This can be used by deployment managers to set the asset id by application revision
-
-
-*1.12.0* (March 27th, 2006)
-
-* Add documentation for respond_to. [Jamis Buck]
-
-* Fixed require of bluecloth and redcloth when gems haven't been loaded #4446 [murphy@cYcnus.de]
-
-* Update to Prototype 1.5.0_pre1 [Sam Stephenson]
-
-* Change #form_for and #fields_for so that the second argument is not required [Dave Thomas]
-
- <% form_for :post, @post, :url => { :action => 'create' } do |f| -%>
-
- becomes...
-
- <% form_for :post, :url => { :action => 'create' } do |f| -%>
-
-* Update to script.aculo.us 1.6 [Thomas Fuchs]
-
-* Enable application/x-yaml processing by default [Jamis Buck]
-
-* Fix double url escaping of remote_function. Add :escape => false option to ActionView's url_for. [Nicholas Seckar]
-
-* Add :script option to in_place_editor to support evalScripts (closes #4194) [Cody Fauser]
-
-* Fix mixed case enumerable methods in the JavaScript Collection Proxy (closes #4314) [Cody Fauser]
-
-* Undo accidental escaping for mail_to; add regression test. [Nicholas Seckar]
-
-* Added nicer message for assert_redirected_to (closes #4294) [court3nay]
-
- assert_redirected_to :action => 'other_host', :only_path => false
-
- when it was expecting...
-
- redirected_to :action => 'other_host', :only_path => true, :host => 'other.test.host'
-
- gives the error message...
-
- response is not a redirection to all of the options supplied (redirection is <{:only_path=>false, :host=>"other.test.host", :action=>"other_host"}>), difference: <{:only_path=>"true", :host=>"other.test.host"}>
-
-* Change url_for to escape the resulting URLs when called from a view. [Nicholas Seckar, coffee2code]
-
-* Added easy support for testing file uploads with fixture_file_upload #4105 [turnip@turnipspatch.com]. Example:
-
- # Looks in Test::Unit::TestCase.fixture_path + '/files/spongebob.png'
- post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
-
-* Fixed UrlHelper#current_page? to behave even when url-escaped entities are present #3929 [jeremy@planetargon.com]
-
-* Add ability for relative_url_root to be specified via an environment variable RAILS_RELATIVE_URL_ROOT. [isaac@reuben.com, Nicholas Seckar]
-
-* Fixed link_to "somewhere", :post => true to produce valid XHTML by using the parentnode instead of document.body for the instant form #3007 [Bob Silva]
-
-* Added :function option to PrototypeHelper#observe_field/observe_form that allows you to call a function instead of submitting an ajax call as the trigger #4268 [jonathan@daikini.com]
-
-* Make Mime::Type.parse consider q values (if any) [Jamis Buck]
-
-* XML-formatted requests are typecast according to "type" attributes for :xml_simple [Jamis Buck]
-
-* Added protection against proxy setups treating requests as local even when they're not #3898 [Steve Purcell]
-
-* Added TestRequest#raw_post that simulate raw_post from CgiRequest #3042 [François Beausoleil]
-
-* Underscore dasherized keys in formatted requests [Jamis Buck]
-
-* Add MimeResponds::Responder#any for managing multiple types with identical responses [Jamis Buck]
-
-* Make the xml_http_request testing method set the HTTP_ACCEPT header [Jamis Buck]
-
-* Add Verification to scaffolds. Prevent destructive actions using GET [Michael Koziarski]
-
-* Avoid hitting the filesystem when using layouts by using a File.directory? cache. [Stefan Kaes, Nicholas Seckar]
-
-* Simplify ActionController::Base#controller_path [Nicholas Seckar]
-
-* Added simple alert() notifications for RJS exceptions when config.action_view.debug_rjs = true. [Sam Stephenson]
-
-* Added :content_type option to render, so you can change the content type on the fly [David Heinemeier Hansson]. Example: render :action => "atom.rxml", :content_type => "application/atom+xml"
-
-* CHANGED DEFAULT: The default content type for .rxml is now application/xml instead of type/xml, see http://www.xml.com/pub/a/2004/07/21/dive.html for reason [David Heinemeier Hansson]
-
-* Added option to render action/template/file of a specific extension (and here by template type). This means you can have multiple templates with the same name but a different extension [David Heinemeier Hansson]. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find :all
-
- respond_to do |type|
- type.html # using defaults, which will render weblog/index.rhtml
- type.xml { render :action => "index.rxml" }
- type.js { render :action => "index.rjs" }
- end
- end
- end
-
-* Added better support for using the same actions to output for different sources depending on the Accept header [David Heinemeier Hansson]. Example:
-
- class WeblogController < ActionController::Base
- def create
- @post = Post.create(params[:post])
-
- respond_to do |type|
- type.js { render } # renders create.rjs
- type.html { redirect_to :action => "index" }
- type.xml do
- headers["Location"] = url_for(:action => "show", :id => @post)
- render(:nothing, :status => "201 Created")
- end
- end
- end
- end
-
-* Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 [David Heinemeier Hansson]
-
-* Integration test's url_for now runs in the context of the last request (if any) so after post /products/show/1 url_for :action => 'new' will yield /product/new [Tobias Lütke]
-
-* Re-added mixed-in helper methods for the JavascriptGenerator. Moved JavascriptGenerators methods to a module that is mixed in after the helpers are added. Also fixed that variables set in the enumeration methods like #collect are set correctly. Documentation added for the enumeration methods [Rick Olson]. Examples:
-
- page.select('#items li').collect('items') do |element|
- element.hide
- end
- # => var items = $$('#items li').collect(function(value, index) { return value.hide(); });
-
-* Added plugin support for parameter parsers, which allows for better support for REST web services. By default, posts submitted with the application/xml content type is handled by creating a XmlSimple hash with the same name as the root element of the submitted xml. More handlers can easily be registered like this:
-
- # Assign a new param parser to a new content type
- ActionController::Base.param_parsers['application/atom+xml'] = Proc.new do |data|
- node = REXML::Document.new(post)
- { node.root.name => node.root }
- end
-
- # Assign the default XmlSimple to a new content type
- ActionController::Base.param_parsers['application/backpack+xml'] = :xml_simple
-
-Default YAML web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functionality back. As part of this new plugin support, request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type [Tobias Lütke]
-
-* Fixed Effect.Appear in effects.js to work with floats in Safari #3524, #3813, #3044 [Thomas Fuchs]
-
-* Fixed that default image extension was not appended when using a full URL with AssetTagHelper#image_tag #4032, #3728 [rubyonrails@beautifulpixel.com]
-
-* Added that page caching will only happen if the response code is less than 400 #4033 [g.bucher@teti.ch]
-
-* Add ActionController::IntegrationTest to allow high-level testing of the way the controllers and routes all work together [Jamis Buck]
-
-* Added support to AssetTagHelper#javascript_include_tag for having :defaults appear anywhere in the list, so you can now make one call ala javascript_include_tag(:defaults, "my_scripts") or javascript_include_tag("my_scripts", :defaults) depending on how you want the load order #3506 [Bob Silva]
-
-* Added support for visual effects scoped queues to the visual_effect helper #3530 [Abdur-Rahman Advany]
-
-* Added .rxml (and any non-rhtml template, really) supportfor CaptureHelper#content_for and CaptureHelper#capture #3287 [Brian Takita]
-
-* Added script.aculo.us drag and drop helpers to RJS [Thomas Fuchs]. Examples:
-
- page.draggable 'product-1'
- page.drop_receiving 'wastebasket', :url => { :action => 'delete' }
- page.sortable 'todolist', :url => { action => 'change_order' }
-
-* Fixed that form elements would strip the trailing [] from the first parameter #3545 [ruby@bobsilva.com]
-
-* During controller resolution, update the NameError suppression to check for the expected constant. [Nicholas Seckar]
-
-* Update script.aculo.us to V1.5.3 [Thomas Fuchs]
-
-* Added various InPlaceEditor options, #3746, #3891, #3896, #3906 [Bill Burcham, ruairi, sl33p3r]
-
-* Added :count option to pagination that'll make it possible for the ActiveRecord::Base.count call to using something else than * for the count. Especially important for count queries using DISTINCT #3839 [Stefan Kaes]
-
-* Update script.aculo.us to V1.5.2 [Thomas Fuchs]
-
-* Added element and collection proxies to RJS [David Heinemeier Hansson]. Examples:
-
- page['blank_slate'] # => $('blank_slate');
- page['blank_slate'].show # => $('blank_slate').show();
- page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
-
- page.select('p') # => $$('p');
- page.select('p.welcome b').first # => $$('p.welcome b').first();
- page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
-
-* Add JavaScriptGenerator#replace for replacing an element's "outer HTML". #3246 [tom@craz8.com, Sam Stephenson]
-
-* Remove over-engineered form_for code for a leaner implementation. [Nicholas Seckar]
-
-* Document form_for's :html option. [Nicholas Seckar]
-
-* Major components cleanup and speedup. #3527 [Stefan Kaes]
-
-* Fix problems with pagination and :include. [Kevin Clark]
-
-* Add ActiveRecordTestCase for testing AR integration. [Kevin Clark]
-
-* Add Unit Tests for pagination [Kevin Clark]
-
-* Add :html option for specifying form tag options in form_for. [Sam Stephenson]
-
-* Replace dubious controller parent class in filter docs. #3655, #3722 [info@rhalff.com, eigentone@gmail.com]
-
-* Don't interpret the :value option on text_area as an html attribute. Set the text_area's value. #3752 [gabriel@gironda.org]
-
-* Fix remote_form_for creates a non-ajax form. [Rick Olson]
-
-* Don't let arbitrary classes match as controllers -- a potentially dangerous bug. [Nicholas Seckar]
-
-* Fix Routing tests. Fix routing where failing to match a controller would prevent the rest of routes from being attempted. [Nicholas Seckar]
-
-* Add :builder => option to form_for and friends. [Nicholas Seckar, Rick Olson]
-
-* Fix controller resolution to avoid accidentally inheriting a controller from a parent module. [Nicholas Seckar]
-
-* Set sweeper's @controller to nil after a request so that the controller may be collected between requests. [Nicholas Seckar]
-
-* Subclasses of ActionController::Caching::Sweeper should be Reloadable. [Rick Olson]
-
-* Document the :xhr option for verifications. #3666 [leeo]
-
-* Added :only and :except controls to skip_before/after_filter just like for when you add filters [David Heinemeier Hansson]
-
-* Ensure that the instance variables are copied to the template when performing render :update. [Nicholas Seckar]
-
-* Add the ability to call JavaScriptGenerator methods from helpers called in update blocks. [Sam Stephenson] Example:
- module ApplicationHelper
- def update_time
- page.replace_html 'time', Time.now.to_s(:db)
- page.visual_effect :highlight, 'time'
- end
- end
-
- class UserController < ApplicationController
- def poll
- render :update { |page| page.update_time }
- end
- end
-
-* Add render(:update) to ActionView::Base. [Sam Stephenson]
-
-* Fix render(:update) to not render layouts. [Sam Stephenson]
-
-* Fixed that SSL would not correctly be detected when running lighttpd/fcgi behind lighttpd w/mod_proxy #3548 [Steve Purcell]
-
-* Added the possibility to specify atomatic expiration for the memcachd session container #3571 [Stefan Kaes]
-
-* Change layout discovery to take into account the change in semantics with File.join and nil arguments. [Marcel Molina Jr.]
-
-* Raise a RedirectBackError if redirect_to :back is called when there's no HTTP_REFERER defined #3049 [Kevin Clark]
-
-* Treat timestamps like datetimes for scaffolding purposes #3388 [Maik Schmidt]
-
-* Fix IE bug with link_to "something", :post => true #3443 [Justin Palmer]
-
-* Extract Test::Unit::TestCase test process behavior into an ActionController::TestProcess module. [Sam Stephenson]
-
-* Pass along blocks from render_to_string to render. [Sam Stephenson]
-
-* Add render :update for inline RJS. [Sam Stephenson] Example:
- class UserController < ApplicationController
- def refresh
- render :update do |page|
- page.replace_html 'user_list', :partial => 'user', :collection => @users
- page.visual_effect :highlight, 'user_list'
- end
- end
- end
-
-* allow nil objects for error_messages_for [Michael Koziarski]
-
-* Refactor human_size to exclude decimal place if it is zero. [Marcel Molina Jr.]
-
-* Update to Prototype 1.5.0_pre0 [Sam Stephenson]
-
-* Automatically discover layouts when a controller is namespaced. #2199, #3424 [me@jonnii.com rails@jeffcole.net Marcel Molina Jr.]
-
-* Add support for multiple proxy servers to CgiRequest#host [gaetanot@comcast.net]
-
-* Documentation typo fix. #2367 [Blair Zajac]
-
-* Remove Upload Progress. #2871 [Sean Treadway]
-
-* Fix typo in function name mapping in auto_complete_field. #2929 #3446 [doppler@gmail.com phil.ross@gmail.com]
-
-* Allow auto-discovery of third party template library layouts. [Marcel Molina Jr.]
-
-* Have the form builder output radio button, not check box, when calling the radio button helper. #3331 [LouisStAmour@gmail.com]
-
-* Added assignment of the Autocompleter object created by JavaScriptMacroHelper#auto_complete_field to a local javascript variables [David Heinemeier Hansson]
-
-* Added :on option for PrototypeHelper#observe_field that allows you to specify a different callback hook to have the observer trigger on [David Heinemeier Hansson]
-
-* Added JavaScriptHelper#button_to_function that works just like JavaScriptHelper#link_to_function but uses a button instead of a href [David Heinemeier Hansson]
-
-* Added that JavaScriptHelper#link_to_function will honor existing :onclick definitions when adding the function call [David Heinemeier Hansson]
-
-* Added :disable_with option to FormTagHelper#submit_tag to allow for easily disabled submit buttons with different text [David Heinemeier Hansson]
-
-* Make auto_link handle nil by returning quickly if blank? [Scott Barron]
-
-* Make auto_link match urls with a port number specified. [Marcel Molina Jr.]
-
-* Added support for toggling visual effects to ScriptaculousHelper::visual_effect, #3323. [Thomas Fuchs]
-
-* Update to script.aculo.us to 1.5.0 rev. 3343 [Thomas Fuchs]
-
-* Added :select option for JavaScriptMacroHelper#auto_complete_field that makes it easier to only use part of the auto-complete suggestion as the value for insertion [Thomas Fuchs]
-
-* Added delayed execution of Javascript from within RJS #3264 [devslashnull@gmail.com]. Example:
-
- page.delay(20) do
- page.visual_effect :fade, 'notice'
- end
-
-* Add session ID to default logging, but remove the verbose description of every step [David Heinemeier Hansson]
-
-* Add the following RJS methods: [Sam Stephenson]
-
- * alert - Displays an alert() dialog
- * redirect_to - Changes window.location.href to simulate a browser redirect
- * call - Calls a JavaScript function
- * assign - Assigns to a JavaScript variable
- * << - Inserts an arbitrary JavaScript string
-
-* Fix incorrect documentation for form_for [Nicholas Seckar]
-
-* Don't include a layout when rendering an rjs template using render's :template option. [Marcel Molina Jr.]
-
-*1.1.2* (December 13th, 2005)
-
-* Become part of Rails 1.0
-
-* Update to script.aculo.us 1.5.0 final (equals 1.5.0_rc6) [Thomas Fuchs]
-
-* Update to Prototype 1.4.0 final [Sam Stephenson]
-
-* Added form_remote_for (form_for meets form_remote_tag) [David Heinemeier Hansson]
-
-* Update to script.aculo.us 1.5.0_rc6
-
-* More robust relative url root discovery for SCGI compatibility. This solves the 'SCGI routes problem' -- you no longer need to prefix all your routes with the name of the SCGI mountpoint. #3070 [Dave Ringoen]
-
-* Fix docs for text_area_tag. #3083. [Christopher Cotton]
-
-* Change form_for and fields_for method signatures to take object name and object as separate arguments rather than as a Hash. [David Heinemeier Hansson]
-
-* Introduce :selected option to the select helper. Allows you to specify a selection other than the current value of object.method. Specify :selected => nil to leave all options unselected. #2991 [Jonathan Viney <jonathan@bluewire.net.nz>]
-
-* Initialize @optional in routing code to avoid warnings about uninitialized access to an instance variable. [Nicholas Seckar]
-
-* Make ActionController's render honor the :locals option when rendering a :file. #1665. [Emanuel Borsboom, Marcel Molina Jr.]
-
-* Allow assert_tag(:conditions) to match the empty string when a tag has no children. Closes #2959. [Jamis Buck]
-
-* Update html-scanner to handle CDATA sections better. Closes #2970. [Jamis Buck]
-
-* Don't put flash in session if sessions are disabled. [Jeremy Kemper]
-
-* Strip out trailing &_= for raw post bodies. Closes #2868. [Sam Stephenson]
-
-* Pass multiple arguments to Element.show and Element.hide in JavaScriptGenerator instead of using iterators. [Sam Stephenson]
-
-* Improve expire_fragment documentation. #2966 [court3nay]
-
-* Correct docs for automatic layout assignment. #2610. [Charles M. Gerungan]
-
-* Always create new AR sessions rather than trying too hard to avoid database traffic. #2731 [Jeremy Kemper]
-
-* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). [Sam Stephenson]
-
-* Use Element.update('id', 'html') instead of $('id').innerHTML = 'html' in JavaScriptGenerator#replace_html so that script tags are evaluated. [Sam Stephenson]
-
-* Make rjs templates always implicitly skip out on layouts. [Marcel Molina Jr.]
-
-* Correct length for the truncate text helper. #2913 [Stefan Kaes]
-
-* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. [Sam Stephenson]
-
-* Add support for new rjs templates which wrap an update_page block. [Marcel Molina Jr.]
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-* Correct time_zone_options_for_select docs. #2892 [pudeyo@rpi.com]
-
-* Remove the unused, slow response_dump and session_dump variables from error pages. #1222 [lmarlow]
-
-* Performance tweaks: use Set instead of Array to speed up prototype helper include? calls. Avoid logging code if logger is nil. Inline commonly-called template presence checks. #2880, #2881, #2882, #2883 [Stefan Kaes]
-
-* MemCache store may be given multiple addresses. #2869 [Ryan Carver <ryan@fivesevensix.com>]
-
-* Handle cookie parsing irregularity for certain Nokia phones. #2530 [zaitzow@gmail.com]
-
-* Added PrototypeHelper::JavaScriptGenerator and PrototypeHelper#update_page for easily modifying multiple elements in an Ajax response. [Sam Stephenson] Example:
-
- update_page do |page|
- page.insert_html :bottom, 'list', '<li>Last item</li>'
- page.visual_effect :highlight, 'list'
- page.hide 'status-indicator', 'cancel-link'
- end
-
- generates the following JavaScript:
-
- new Insertion.Bottom("list", "<li>Last item</li>");
- new Effect.Highlight("list");
- ["status-indicator", "cancel-link"].each(Element.hide);
-
-* Refactored JavaScriptHelper into PrototypeHelper and ScriptaculousHelper [Sam Stephenson]
-
-* Update to latest script.aculo.us version (as of [3031])
-
-* Updated docs for in_place_editor, fixes a couple bugs and offers extended support for external controls [Justin Palmer]
-
-* Update documentation for render :file. #2858 [Tom Werner]
-
-* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb. #2855 [court3nay]
-
-* Added FormHelper#form_for and FormHelper#fields_for that makes it easier to work with forms for single objects also if they don't reside in instance variables [David Heinemeier Hansson]. Examples:
-
- <% form_for :person, @person, :url => { :action => "update" } do |f| %>
- First name: <%= f.text_field :first_name %>
- Last name : <%= f.text_field :last_name %>
- Biography : <%= f.text_area :biography %>
- Admin? : <%= f.check_box :admin %>
- <% end %>
-
- <% form_for :person, person, :url => { :action => "update" } do |person_form| %>
- First name: <%= person_form.text_field :first_name %>
- Last name : <%= person_form.text_field :last_name %>
-
- <% fields_for :permission => person.permission do |permission_fields| %>
- Admin? : <%= permission_fields.check_box :admin %>
- <% end %>
- <% end %>
-
-* options_for_select allows any objects which respond_to? :first and :last rather than restricting to Array and Range. #2824 [Jacob Robbins <jrobbins@cmj.com>, Jeremy Kemper]
-
-* The auto_link text helper accepts an optional block to format the link text for each url and email address. Example: auto_link(post.body) { |text| truncate(text, 10) } [Jeremy Kemper]
-
-* assert_tag uses exact matches for string conditions, instead of partial matches. Use regex to do partial matches. #2799 [Jamis Buck]
-
-* CGI::Session::ActiveRecordStore.data_column_name = 'foobar' to use a different session data column than the 'data' default. [nbpwie102@sneakemail.com]
-
-* Do not raise an exception when default helper is missing; log a debug message instead. It's nice to delete empty helpers. [Jeremy Kemper]
-
-* Controllers with acronyms in their names (e.g. PDFController) require the correct default helper (PDFHelper in file pdf_helper.rb). #2262 [jeff@opendbms.com]
-
-
-*1.11.0* (November 7th, 2005)
-
-* Added request as instance method to views, so you can do <%= request.env["HTTP_REFERER"] %>, just like you can already access response, session, and the likes [David Heinemeier Hansson]
-
-* Fix conflict with assert_tag and Glue gem #2255 [david.felstead@gmail.com]
-
-* Add documentation to assert_tag indicating that it only works with well-formed XHTML #1937, #2570 [Jamis Buck]
-
-* Added action_pack.rb stub so that ActionPack::Version loads properly [Sam Stephenson]
-
-* Added short-hand to assert_tag so assert_tag :tag => "span" can be written as assert_tag "span" [David Heinemeier Hansson]
-
-* Added skip_before_filter/skip_after_filter for easier control of the filter chain in inheritance hierachies [David Heinemeier Hansson]. Example:
-
- class ApplicationController < ActionController::Base
- before_filter :authenticate
- end
-
- class WeblogController < ApplicationController
- # will run the :authenticate filter
- end
-
- class SignupController < ActionController::Base
- # will not run the :authenticate filter
- skip_before_filter :authenticate
- end
-
-* Added redirect_to :back as a short-hand for redirect_to(request.env["HTTP_REFERER"]) [David Heinemeier Hansson]
-
-* Change javascript_include_tag :defaults to not use script.aculo.us loader, which facilitates the use of plugins for future script.aculo.us and third party javascript extensions, and provide register_javascript_include_default for plugins to specify additional JavaScript files to load. Removed slider.js and builder.js from actionpack. [Thomas Fuchs]
-
-* Fix problem where redirecting components can cause an infinite loop [Rick Olson]
-
-* Added support for the queue option on visual_effect [Thomas Fuchs]
-
-* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs]
-
-* Fix that render :text didn't interpolate instance variables #2629, #2626 [Stefan Kaes]
-
-* Fix line number detection and escape RAILS_ROOT in backtrace Regexp [Nicholas Seckar]
-
-* Fixed document.getElementsByClassName from Prototype to be speedy again [Sam Stephenson]
-
-* Recognize ./#{RAILS_ROOT} as RAILS_ROOT in error traces [Nicholas Seckar]
-
-* Remove ARStore session fingerprinting [Nicholas Seckar]
-
-* Fix obscure bug in ARStore [Nicholas Seckar]
-
-* Added TextHelper#strip_tags for removing HTML tags from a string (using HTMLTokenizer) #2229 [marcin@junkheap.net]
-
-* Added a reader for flash.now, so it's possible to do stuff like flash.now[:alert] ||= 'New if not set' #2422 [Caio Chassot]
-
-
-*1.10.2* (October 26th, 2005)
-
-* Reset template variables after using render_to_string [Stefan Kaes]
-
-* Expose the session model backing CGI::Session
-
-* Abbreviate RAILS_ROOT in traces
-
-
-*1.10.1* (October 19th, 2005)
-
-* Update error trace templates [Nicholas Seckar]
-
-* Stop showing generated routing code in application traces [Nicholas Seckar]
-
-
-*1.10.0* (October 16th, 2005)
-
-* Make string-keys locals assigns optional. Add documentation describing depreciated state [Stefan Kaes]
-
-* Improve line number detection for template errors [Nicholas Seckar]
-
-* Update/clean up documentation (rdoc)
-
-* Upgrade to Prototype 1.4.0_rc0 [Sam Stephenson]
-
-* Added assert_vaild. Reports the proper AR error messages as fail message when the passed record is invalid [Tobias Lütke]
-
-* Add temporary support for passing locals to render using string keys [Nicholas Seckar]
-
-* Clean up error pages by providing better backtraces [Nicholas Seckar]
-
-* Raise an exception if an attempt is made to insert more session data into the ActiveRecordStore data column than the column can hold. #2234. [justin@textdrive.com]
-
-* Removed references to assertions.rb from actionpack assert's backtraces. Makes error reports in functional unit tests much less noisy. [Tobias Lütke]
-
-* Updated and clarified documentation for JavaScriptHelper to be more concise about the various options for including the JavaScript libs. [Thomas Fuchs]
-
-* Hide "Retry with Breakpoint" button on error pages until feature is functional. [David Heinemeier Hansson]
-
-* Fix Request#host_with_port to use the standard port when Rails is behind a proxy. [Nicholas Seckar]
-
-* Escape query strings in the href attribute of URLs created by url_helper. #2333 [Michael Schuerig <michael@schuerig.de>]
-
-* Improved line number reporting for template errors [Nicholas Seckar]
-
-* Added :locals support for render :inline #2463 [mdabney@cavoksolutions.com]
-
-* Unset the X-Requested-With header when using the xhr wrapper in functional tests so that future requests aren't accidentally xhr'ed #2352 [me@julik.nl, Sam Stephenson]
-
-* Unescape paths before writing cache to file system. #1877. [Damien Pollet]
-
-* Wrap javascript_tag contents in a CDATA section and add a cdata_section method to TagHelper #1691 [Michael Schuerig, Sam Stephenson]
-
-* Misc doc fixes (typos/grammar/etc). #2445. [coffee2code]
-
-* Speed improvement for session_options. #2287. [Stefan Kaes]
-
-* Make cacheing binary files friendly with Windows. #1975. [Rich Olson]
-
-* Convert boolean form options form the tag_helper. #809. [Michael Schuerig <michael@schuerig.de>]
-
-* Fixed that an instance variable with the same name as a partial should be implicitly passed as the partial :object #2269 [court3nay]
-
-* Update Prototype to V1.4.0_pre11, script.aculo.us to [2502] [Thomas Fuchs]
-
-* Make assert_tag :children count appropriately. Closes #2181. [jamie@bravenet.com]
-
-* Forced newer versions of RedCloth to use hard breaks [David Heinemeier Hansson]
-
-* Added new scriptaculous options for auto_complete_field #2343 [Manfred Stienstra]
-
-* Don't prepend the asset host if the string is already a fully-qualified URL
-
-* Updated to script.aculo.us V1.5.0_rc2 and Prototype to V1.4.0_pre7 [Thomas Fuchs]
-
-* Undo condition change made in [2345] to prevent normal parameters arriving as StringIO.
-
-* Tolerate consecutive delimiters in query parameters. #2295 [darashi@gmail.com]
-
-* Streamline render process, code cleaning. Closes #2294. [skae]
-
-* Keep flash after components are rendered. #2291 [Rick Olson, Scott]
-
-* Shorten IE file upload path to filename only to match other browsers. #1507 [court3nay]
-
-* Fix open/save dialog in IE not opening files send with send_file/send_data, #2279 [Thomas Fuchs]
-
-* Fixed that auto_discovery_link_tag couldn't take a string as the URL [David Heinemeier Hansson]
-
-* Fixed problem with send_file and WEBrick using stdout #1812 [David Heinemeier Hansson]
-
-* Optimized tag_options to not sort keys, which is no longer necessary when assert_dom_equal and friend is available #1995 [skae]
-
-* Added assert_dom_equal and assert_dom_not_equal to compare tags generated by the helpers in an order-indifferent manner #1995 [skae]
-
-* Fixed that Request#domain caused an exception if the domain header wasn't set in the original http request #1795 [Michael Koziarski]
-
-* Make the truncate() helper multi-byte safe (assuming $KCODE has been set to something other than "NONE") #2103
-
-* Add routing tests from #1945 [ben@groovie.org]
-
-* Add a routing test case covering #2101 [Nicholas Seckar]
-
-* Cache relative_url_root for all webservers, not just Apache #2193 [skae]
-
-* Speed up cookie use by decreasing string copying #2194 [skae]
-
-* Fixed access to "Host" header with requests made by crappy old HTTP/1.0 clients #2124 [Marcel Molina Jr.]
-
-* Added easy assignment of fragment cache store through use of symbols for included stores (old way still works too)
-
- Before:
- ActionController::Base.fragment_cache_store =
- ActionController::Base::Caching::Fragments::FileStore.new("/path/to/cache/directory")
-
- After:
- ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"
-
-* Added ActionController::Base.session_store=, session_store, and session_options to make it easier to tweak the session options (instead of going straight to ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)
-
-* Added TextHelper#cycle to cycle over an array of values on each hit (useful for alternating row colors etc) #2154 [dave-ml@dribin.org]
-
-* Ensure that request.path never returns nil. Closes #1675 [Nicholas Seckar]
-
-* Add ability to specify Route Regexps for controllers. Closes #1917. [Sebastian Kanthak]
-
-* Provide Named Route's hash methods as helper methods. Closes #1744. [Nicholas Seckar, Steve Purcell]
-
-* Added :multipart option to ActiveRecordHelper#form to make it possible to add file input fields #2034 [jstirk@oobleyboo.com]
-
-* Moved auto-completion and in-place editing into the Macros module and their helper counterparts into JavaScriptMacrosHelper
-
-* Added in-place editing support in the spirit of auto complete with ActionController::Base.in_place_edit_for, JavascriptHelper#in_place_editor_field, and Javascript support from script.aculo.us #2038 [Jon Tirsen]
-
-* Added :disabled option to all data selects that'll make the elements inaccessible for change #2167, #253 [eigentone]
-
-* Fixed that TextHelper#auto_link_urls would include punctuation in the links #2166, #1671 [eigentone]
-
-* Fixed that number_to_currency(1000, {:precision => 0})) should return "$1,000", instead of "$1,000." #2122 [sd@notso.net]
-
-* Allow link_to_remote to use any DOM-element as the parent of the form elements to be submitted #2137 [erik@ruby-lang.nl]. Example:
-
- <tr id="row023">
- <td><input name="foo"/></td>
- <td><input name="bar"/></td>
- <td><%= link_to_remote 'Save', :update => "row023",
- :submit => "row023", :url => {:action => 'save_row'} %></td>
- </tr>
-
-* Fixed that render :partial would fail when :object was a Hash (due to backwards compatibility issues) #2148 [Sam Stephenson]
-
-* Fixed JavascriptHelper#auto_complete_for to only include unique items #2153 [Thomas Fuchs]
-
-* Fixed all AssetHelper methods to work with relative paths, such that javascript_include_tag('stdlib/standard') will look in /javascripts/stdlib/standard instead of '/stdlib/standard/' #1963
-
-* Avoid extending view instance with helper modules each request. Closes #1979
-
-* Performance improvements to CGI methods. Closes #1980 [Stefan Kaes]
-
-* Added :post option to UrlHelper#link_to that makes it possible to do POST requests through normal ahref links using Javascript
-
-* Fixed overwrite_params
-
-* Added ActionController::Base.benchmark and ActionController::Base.silence to allow for easy benchmarking and turning off the log
-
-* Updated vendor copy of html-scanner to support better xml parsing
-
-* Added :popup option to UrlHelper#link_to #1996 [gabriel.gironda@gmail.com]. Examples:
-
- link_to "Help", { :action => "help" }, :popup => true
- link_to "Busy loop", { :action => "busy" }, :popup => ['new_window', 'height=300,width=600']
-
-* Drop trailing \000 if present on RAW_POST_DATA (works around bug in Safari Ajax implementation) #918
-
-* Fix observe_field to fall back to event-based observation if frequency <= 0 #1916 [Michael Schubert]
-
-* Allow use of the :with option for submit_to_remote #1936 [jon@instance-design.co.uk]
-
-* AbstractRequest#domain returns nil when host is an ip address #2012 [Kevin Clark]
-
-* ActionController documentation update #2051 [François Beausoleil]
-
-* Yield @content_for_ variables to templates #2058 [Sam Stephenson]
-
-* Make rendering an empty partial collection behave like :nothing => true #2080 [Sam Stephenson]
-
-* Add option to specify the singular name used by pagination.
-
-* Use string key to obtain action value. Allows indifferent hashes to be disabled.
-
-* Added ActionView::Base.cache_template_loading back.
-
-* Rewrote compiled templates to decrease code complexity. Removed template load caching in favour of compiled caching. Fixed template error messages. [Nicholas Seckar]
-
-* Fix Routing to handle :some_param => nil better. [Nicholas Seckar, Luminas]
-
-* Add support for :include with pagination (subject to existing constraints for :include with :limit and :offset) #1478 [Michael Schubert]
-
-* Prevent the benchmark module from blowing up if a non-HTTP/1.1 request is processed
-
-* Added :use_short_month option to select_month helper to show month names as abbreviations
-
-* Make link_to escape the javascript in the confirm option #1964 [nicolas.pouillard@gmail.com]
-
-* Make assert_redirected_to properly check URL's passed as strings #1910 [Scott Barron]
-
-* Make sure :layout => false is always used when rendering inside a layout
-
-* Use raise instead of assert_not_nil in Test::Unit::TestCase#process to ensure that the test variables (controller, request, response) have been set
-
-* Make sure assigns are built for every request when testing #1866
-
-* Allow remote_addr to be queried on TestRequest #1668
-
-* Fixed bug when a partial render was passing a local with the same name as the partial
-
-* Improved performance of test app req/sec with ~10% refactoring the render method #1823 [Stefan Kaes]
-
-* Improved performance of test app req/sec with 5-30% through a series of Action Pack optimizations #1811 [Stefan Kaes]
-
-* Changed caching/expiration/hit to report using the DEBUG log level and errors to use the ERROR log level instead of both using INFO
-
-* Added support for per-action session management #1763
-
-* Improved rendering speed on complicated templates by up to 100% (the more complex the templates, the higher the speedup) #1234 [Stefan Kaes]. This did necessasitate a change to the internals of ActionView#render_template that now has four parameters. Developers of custom view handlers (like Amrita) need to update for that.
-
-* Added options hash as third argument to FormHelper#input, so you can do input('person', 'zip', :size=>10) #1719 [jeremye@bsa.ca.gov]
-
-* Added Base#expires_in(seconds)/Base#expires_now to control HTTP content cache headers #1755 [Thomas Fuchs]
-
-* Fixed line number reporting for Builder template errors #1753 [piotr]
-
-* Fixed assert_routing so that testing controllers in modules works as expected [Nicholas Seckar, Rick Olson]
-
-* Fixed bug with :success/:failure callbacks for the JavaScriptHelper methods #1730 [court3nay/Thomas Fuchs]
-
-* Added named_route method to RouteSet instances so that RouteSet instance methods do not prevent certain names from being used. [Nicholas Seckar]
-
-* Fixed routes so that routes which do not specify :action in the path or in the requirements have a default of :action => 'index', In addition, fixed url generation so that :action => 'index' does not need to be provided for such urls. [Nicholas Seckar, Markjuh]
-
-* Worked around a Safari bug where it wouldn't pass headers through if the response was zero length by having render :nothing return ' ' instead of ''
-
-* Fixed Request#subdomains to handle "foo.foo.com" correctly
-
-
-*1.9.1* (11 July, 2005)
-
-* Fixed that auto_complete_for didn't force the input string to lower case even as the db comparison was
-
-* Fixed that Action View should always use the included Builder, never attempt to require the gem, to ensure compatibility
-
-* Added that nil options are not included in tags, so tag("p", :ignore => nil) now returns <p /> not <p ignore="" /> but that tag("p", :ignore => "") still includes it #1465 [Michael Schuerig]
-
-* Fixed that UrlHelper#link_to_unless/link_to_if used html_escape on the name if no link was to be applied. This is unnecessary and breaks its use with images #1649 [joergd@pobox.com]
-
-* Improved error message for DoubleRenderError
-
-* Fixed routing to allow for testing of *path components #1650 [Nicholas Seckar]
-
-* Added :handle as an option to sortable_element to restrict the drag handle to a given class #1642 [thejohnny]
-
-* Added a bunch of script.aculo.us features #1644, #1677, #1695 [Thomas Fuchs]
- * Effect.ScrollTo, to smoothly scroll the page to an element
- * Better Firefox flickering handling on SlideUp/SlideDown
- * Removed a possible memory leak in IE with draggables
- * Added support for cancelling dragging my hitting ESC
- * Added capability to remove draggables/droppables and redeclare sortables in dragdrop.js (this makes it possible to call sortable_element on the same element more than once, e.g. in AJAX returns that modify the sortable element. all current sortable 'stuff' on the element will be discarded and the sortable will be rebuilt)
- * Always reset background color on Effect.Highlight; this make change backwards-compatibility, to be sure include style="background-color:(target-color)" on your elements or else elements will fall back to their CSS rules (which is a good thing in most circumstances)
- * Removed circular references from element to prevent memory leaks (still not completely gone in IE)
- * Changes to class extension in effects.js
- * Make Effect.Highlight restore any previously set background color when finishing (makes effect work with CSS classes that set a background color)
- * Fixed myriads of memory leaks in IE and Gecko-based browsers [David Zülke]
- * Added incremental and local autocompleting and loads of documentation to controls.js [Ivan Krstic]
- * Extended the auto_complete_field helper to accept tokens option
- * Changed object extension mechanism to favor Object.extend to make script.aculo.us easily adaptable to support 3rd party libs like IE7.js [David Zülke]
-
-* Fixed that named routes didn't use the default values for action and possible other parameters #1534 [Nicholas Seckar]
-
-* Fixed JavascriptHelper#visual_effect to use camelize such that :blind_up will work #1639 [pelletierm@eastmedia.net]
-
-* Fixed that a SessionRestoreError was thrown if a model object was placed in the session that wasn't available to all controllers. This means that it's no longer necessary to use the 'model :post' work-around in ApplicationController to have a Post model in your session.
-
-
-*1.9.0* (6 July, 2005)
-
-* Added logging of the request URI in the benchmark statement (makes it easy to grep for slow actions)
-
-* Added javascript_include_tag :defaults shortcut that'll include all the default javascripts included with Action Pack (prototype, effects, controls, dragdrop)
-
-* Cache several controller variables that are expensive to calculate #1229 [Stefan Kaes]
-
-* The session class backing CGI::Session::ActiveRecordStore may be replaced with any class that duck-types with a subset of Active Record. See docs for details #1238 [Stefan Kaes]
-
-* Fixed that hashes was not working properly when passed by GET to lighttpd #849 [Nicholas Seckar]
-
-* Fixed assert_template nil will be true when no template was rendered #1565 [maceywj@telus.net]
-
-* Added :prompt option to FormOptions#select (and the users of it, like FormOptions#select_country etc) to create "Please select" style descriptors #1181 [Michael Schuerig]
-
-* Added JavascriptHelper#update_element_function, which returns a Javascript function (or expression) that'll update a DOM element according to the options passed #933 [mortonda@dgrmm.net]. Examples:
-
- <%= update_element_function("products", :action => :insert, :position => :bottom, :content => "<p>New product!</p>") %>
-
- <% update_element_function("products", :action => :replace, :binding => binding) do %>
- <p>Product 1</p>
- <p>Product 2</p>
- <% end %>
-
-* Added :field_name option to DateHelper#select_(year|month|day) to deviate from the year/month/day defaults #1266 [Marcel Molina Jr.]
-
-* Added JavascriptHelper#draggable_element and JavascriptHelper#drop_receiving_element to facilitate easy dragging and dropping through the script.aculo.us libraries #1578 [Thomas Fuchs]
-
-* Added that UrlHelper#mail_to will now also encode the default link title #749 [f.svehla@gmail.com]
-
-* Removed the default option of wrap=virtual on FormHelper#text_area to ensure XHTML compatibility #1300 [thomas@columbus.rr.com]
-
-* Adds the ability to include XML CDATA tags using Builder #1563 [Josh Knowles]. Example:
-
- xml.cdata! "some text" # => <![CDATA[some text]]>
-
-* Added evaluation of <SCRIPT> blocks in content returned to Ajax calls #1577 [Thomas Fuchs/court3nay/Sean Treadway]
-
-* Directly generate paths with a leading slash instead of tacking it on later. #1543 [Nicholas Seckar]
-
-* Fixed errant parameter modification in functional tests. #1542 [Nicholas Seckar]
-
-* Routes fail with leading slash #1540 [Nicholas Seckar]
-
-* Added support for graceful error handling of Ajax calls #1217 [Jamis Buck/Thomas Fuchs]. Example:
-
- link_to_remote(
- "test",
- :url => { :action => "faulty" },
- :update => { :success => "good", :failure => "bad" },
- 403 => "alert('Forbidden- got ya!')",
- 404 => "alert('Nothing there...?')",
- :failure => "alert('Unkown error ' + request.status)")
-
-* Attempt to explicitly flush the output at the end of CgiProcess#out
-
-* Fixed assert_redirected_to to handle absolute controller paths properly #1472 [Rick Olson/Nicholas Seckar]
-
-* Added event-based observations when frequency is not set on observe_field/form #1474 [flash@vanklinkenbergsoftware.nl]
-
-* Added script.aculo.us Javascripts (controls.js, dragdrop.js, effects.js) (NEEDS MORE DESCRIPTION) #1509 [Thomas Fuchs]
-
-* Fixed prototype to consider all fields it doesn't know as text (such as Safari's search) just like the browser in its serialization #1497 [Sean Treadway]
-
-* Improved performance of Routes generation by a factor of 5 #1434 [Nicholas Seckar]
-
-* Added named routes (NEEDS BETTER DESCRIPTION) #1434 [Nicholas Seckar]
-
-* Improved AbstractRequest documentation #1483 [court3nay]
-
-* Added ActionController::Base.allow_concurrency to control whether the application is thread-safe, so multi-threaded servers like WEBrick knows whether to apply a mutex around the performance of each action. Turned off by default. EXPERIMENTAL FEATURE.
-
-* Added TextHelper#word_wrap(text, line_length = 80) #1449 [tuxie@dekadance.se]
-
-* Added a fall-through action for form_remote_tag that'll be used in case Javascript is unavailable #1459 [Scott Barron]. Example:
-
- form_remote_tag :html => { :action => url_for(:controller => "some", :action => "place") }
-
-* Added :xhr => true/false option to verify so you can ensure that a request is coming from an Ajax call or not #1464 [Thomas Fuchs]
-
-* Added tag_options as a third parameter to AssetHelper#auto_discovery_link_tag to control options like the title of the link #1430 [Kevin Clark]
-
-* Added option to pass in parameters to CaptureHelper#capture, so you can create more advanced view helper methods #1466 [duane.johnson@gmail.com]. Example:
-
- <% show_calendar(:year => 2005, :month => 6) do |day, options| %>
- <% options[:bgcolor] = '#dfd' if 10..15.include? day %>
- [<%= day %>]
- <% end %>
-
-* Changed the default name of the input tag generated by FormTagHelper#submit_tag from "submit" to "commit" so it doesn't clash with form.submit() calls in Javascript #1271
-
-* Fixed relative urls support for lighttpd #1048 [Nicholas Seckar/maznawak@nerim.net]
-
-* Correct distance_of_time_in_words for integer arguments and make the second arg optional, treating the first arg as a duration in seconds. #1458 [madrobby <thomas@fesch.at>]
-
-* Fixed query parser to deal gracefully with equal signs inside keys and values #1345 [gorou].
- Example: /?sig=abcdef=:foobar=&x=y will pass now.
-
-* Added Cuba to country list #1351 [todd]
-
-* Fixed radio_button to work with numeric values #1352 [demetrius]
-
-* Added :extension option to NumberHelper#number_to_phone #1361 [delynnb]
-
-* Added button_to as a form-based solution to deal with harmful actions that should be hidden behind POSTs. This makes it just as easy as link_to to create a safe trigger for actions like destroy, although it's limited by being a block element, the fixed look, and a no-no inside other forms. #1371 [tom@moertel.com]
-
-* Fixed image_tag so an exception is not thrown just because the image is missing and alt value can't be generated #1395 [Marcel Molina Jr.]
-
-* Added a third parameter to TextHelper#auto_link called href_options for specifying additional tag options on the links generated #1401 [tyler.kovacs@gmail.com]. Example: auto_link(text, :all, { :target => "_blank" }) to have all the generated links open in a new window.
-
-* Fixed TextHelper#highlight to return the text, not nil, if the phrase is blank #1409 [Patrick Lenz]
-
-* Fixed TagHelper such that :name and 'name' keys in the options doesn't result in two attributes #1455 [take_tk]
-
-* Ensure that helpers are only available to the controllers where they are defined and their subclasses. #1394 [kdole@tamu.edu]
-
-* render("foo/bar") works with a layout again
-
-* Fixed double-singularization on scaffolded pagination call (Address would be turned into Addres) #1216, #1404 [nilsga]
-
-* Removed the require hack used by functional testing to work around an earlier bug in rake.
-
-* Allow distance_of_time_in_words to work with any value that responds to #to_time (like dates) #969
-
-* Support :render option for :verify #1440 [Tobias Lütke]
-
-* Updated vendor copy of html-scanner lib to 0.5.2, for bug fixes and optimizations. The :content option may be used as expected--to find a tag whose textual content is a particular value--in assert_tag, now.
-
-* Changed test requests to come from 0.0.0.0 instead of 127.0.0.1 such that they don't trigger debugging screens on exceptions, but instead call rescue_action_in_public
-
-* Modernize scaffolding to match the generator: use the new render method and change style from the warty @params["id"] to the sleek params[:id]. #1367
-
-* Include :id in the action generated by the form helper method. Then, for example, the controller can do Model.find(params[:id]) for both edit and update actions. Updated scaffolding to take advantage. #1367
-
-* Add assertions with friendly messages to TestCase#process to ensure that @controller, @request, and @response are set. #1367
-
-* Arrays, hashes sent via multipart posts are converted to strings #1032 [dj@omelia.org, me@julik.nl]
-
-* render(:layout => true) is a synonym for render(:layout => nil)
-
-* Make sure the benchmarking render method always returns the output of the render.
-
-* render(:action), render(:template) and render() are the only three calls that default to using a layout. All other render calls assume :layout => false. This also fixes send_file, which was applying a layout if one existed for the current action.
-
-* verify with :redirect_to won't redirect if a redirect or render has already been performed #1350
-
-* render(:partial => true) is identical to the behavior of the deprecated render_partial()
-
-* Fixed render(:partial => "...") to use an empty Hash for the local assigns #1365
-
-* Fixed Caching::Fragments::FileStore.delete to not raise an exception if the delete fails.
-
-* Deprecated all render_* methods in favor of consolidating all rendering behavior in Base#render(options). This enables more natural use of combining options, such as layouts. Examples:
-
- +---------------------------------------------------------------+-------------------------------------------------------+
- | BEFORE | AFTER |
- +---------------------------------------------------------------+-------------------------------------------------------+
- | render_with_layout "weblog/show", "200 OK", "layouts/dialog" | render :action => "show", :layout => "dialog" |
- | render_without_layout "weblog/show" | render :action => "show", :layout => false |
- | render_action "error", "404 Not Found" | render :action => "error", :status => "404 Not Found" |
- | render_template "xml.div('stuff')", "200 OK", :rxml | render :inline => "xml.div('stuff')", :type => :rxml |
- | render_text "hello world!" | render :text => "hello world!" |
- | render_partial_collection "person", @people, nil, :a => 1 | render :partial => "person", :collection => @people, |
- | | :locals => { :a => 1 } |
- +---------------------------------------------------------------+-------------------------------------------------------+
-
-* Deprecated redirect_to_path and redirect_to_url in favor of letting redirect_to do the right thing when passed either a path or url.
-
-* Fixed use of an integer as return code for renders, so render_text "hello world", 404 now works #1327
-
-* Fixed assert_redirect_to to work with redirect_to_path #869 [Nicholas Seckar]
-
-* Fixed escaping of :method option in remote_form_tag #1218 [Rick Olson]
-
-* Added Serbia and Montenegro to the country_select #1239 [todd@robotcoop.com]
-
-* Fixed Request#remote_ip in testing #1251 [Jeremy Kemper]
-
-* Fixed that compute_public_path should recognize external URLs, so image_tag("http://www.example.com/images/icon.gif") is not prefixed with the relative url path #1254 [victor-ronr-trac@carotena.net]
-
-* Added support for descending year values in DateHelper#select_year, like select_year(Date.today, :start_year => 2005, :end_year => 1900), which would count down from 2005 to 1900 instead of the other way #1274 [nwoods@mail.com]
-
-* Fixed that FormHelper#checkbox should return a checked checkbox if the value is the same as checked_value #1286 [Florian Weber]
-
-* Fixed Form.disable in Prototype #1317 [Wintermute]
-
-* Added accessors to logger, params, response, session, flash, and headers from the view, so you can write <% logger.info "stuff" %> instead of <% @logger.info "others" %> -- more consistent with the preferred way of accessing these attributes and collections from the controller
-
-* Added support for POST data in form of YAML or XML, which is controller through the Content-Type header. Example request:
-
- Content-Type: application/xml
- <request><item><content>HelloWorld</content></item></request>
-
- ...is the same as:
-
- Content-Type: application/x-yaml
- ---
- item:
- content: HelloWorld
-
- ...is the same as:
-
- item[content]=HelloWorld
-
- Which in the end turns into { "item" => { "content" => "HelloWorld" } }. This makes it a lot easier to publish REST web services on top of your regular actions (as they won't care).
-
- Example Curl call:
-
- curl -H 'Content-Type: application/xml' -d '<request><item><content>KillMeMore</content></item></request>' http://www.example.com/service
-
- You can query to find out whether a given request came through as one of these types with:
- - request.post_format? (:url_encoded, :xml or :yaml)
- - request.formatted_post? (for either xml or yaml)
- - request.xml_post?
- - request.yaml_post?
-
-* Added bundling of XmlSimple by Maik Schmidt
-
-* Fixed that render_partial_collection should always return a string (and not sometimes an array, despite <%= %> not caring)
-
-* Added TextHelper#sanitize that can will remove any Javascript handlers, blocks, and forms from an input of HTML. This allows for use of HTML on public sites, but still be free of XSS issues. #1277 [Jamis Buck]
-
-* Fixed the HTML scanner used by assert_tag where a infinite loop could be caused by a stray less-than sign in the input #1270 [Jamis Buck]
-
-* Added functionality to assert_tag, so you can now do tests on the siblings of a node, to assert that some element comes before or after the element in question, or just to assert that some element exists as a sibling #1226 [Jamis Buck]
-
-* Added better error handling for regexp caching expiration
-
-* Fixed handling of requests coming from unknown HTTP methods not to kill the server
-
-* Added that both AssetHelper#stylesheet_link_tag and AssetHelper#javascript_include_tag now accept an option hash as the last parameter, so you can do stuff like: stylesheet_link_tag "style", :media => "all"
-
-* Added FormTagHelper#image_submit_tag for making submit buttons that uses images
-
-* Added ActionController::Base.asset_host that will then be used by all the asset helpers. This enables you to easily offload static content like javascripts and images to a separate server tuned just for that.
-
-* Fixed action/fragment caching using the filestore when a directory and a file wanted to use the same name. Now there's a .cache prefix that sidesteps the conflict #1188 [imbcmdth@hotmail.com]
-
-* Fixed missing id uniqueness in FormTag#radio_button #1207 [Jarkko Laine]
-
-* Fixed assert_redirected_to to work with :only_path => false #1204 [Alisdair McDiarmid]
-
-* Fixed render_partial_collection to output an empty string instead of nil when handed an empty array #1202 [Ryan Carver]
-
-* Improved the speed of regular expression expirations for caching by a factor of 10 #1221 [Jamis Buck]
-
-* Removed dumping of template assigns on the rescue page as it would very easily include a ton of data making page loads take seconds (and the information was rarely helpful) #1222
-
-* Added BenchmarkHelper that can measure the execution time of a block in a template and reports the result to the log. Example:
-
- <% benchmark "Notes section" do %>
- <%= expensive_notes_operation %>
- <% end %>
-
- Will add something like "Notes section (0.345234)" to the log.
-
-* Added ActionController::Caching::Sweeper as an improved an easier to use sweeper. The new sweepers work on a single-step approach instead of two-steps like the old ones. Before
-
- def after_save(record)
- @list = record.is_a?(List) ? record : record.list
- end
-
- def filter(controller)
- controller.expire_page(:controller => "lists", :action => %w( show public feed ), :id => @list.id)
- controller.expire_action(:controller => "lists", :action => "all")
- @list.shares.each { |share| controller.expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
- end
-
- ..after:
-
- def after_save(record)
- list = record.is_a?(List) ? record : record.list
- expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
- expire_action(:controller => "lists", :action => "all")
- list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
- end
-
- The new sweepers can also observe on the actions themselves by implementing methods according to (before|after)_$controller_$action. Example of a callback that'll be called after PagesController#update_title has been performed:
-
- def after_pages_update_title
- expire_fragment(%r{pages/#{controller.assigns["page"].id}/.*})
- end
-
- Note that missing_method is delegated to the controller instance, which is assigned in a before filter. This means that you can call expire_fragment instead of @controller.expire_fragment.
-
-* Added that Fragments#expire_fragment now accepts as a regular expression as the name thereby deprecating expire_matched_fragments
-
-* Fixed that fragments shouldn't use the current host and the path as part of the key like pages does
-
-* Added conditions to around_filters just like before_filter and after_filter
-
-
-*1.8.1* (20th April, 2005)
-
-* Added xml_http_request/xhr method for simulating XMLHttpRequest in functional tests #1151 [Sam Stephenson]. Example:
-
- xhr :post, :index
-
-* Fixed that Ajax.Base.options.asynchronous wasn't being respected in Ajax.Request (thanks Jon Casey)
-
-* Fixed that :get, :post, and the others should take a flash array as the third argument just like process #1144 [rails@cogentdude.com]
-
-* Fixed a problem with Flash.now
-
-* Fixed stringification on all assigned hashes. The sacrifice is that assigns[:person] won't work in testing. Instead assigns["person"] or assigns(:person) must be used. In other words, the keys of assigns stay strings but we've added a method-based accessor to appease the need for symbols.
-
-* Fixed that rendering a template would require a connection to the database #1146
-
-
-*1.8.0* (19th April, 2005)
-
-* Added assert_tag and assert_no_tag as a much improved alternative to the deprecated assert_template_xpath_match #1126 [Jamis Buck]
-
-* Deprecated the majority of all the testing assertions and replaced them with a much smaller core and access to all the collections the old assertions relied on. That way the regular test/unit assertions can be used against these. Added documentation about how to use it all.
-
-* Added a wide range of new Javascript effects:
- * Effect.Puff zooms the element out and makes it smoothly transparent at the same time, giving a "puff" illusion #996 [thomas@fesch.at]
- After the animation is completed, the display property will be set to none.
- This effect will work on relative and absolute positioned elements.
-
- * Effect.Appear as the opposite of Effect.Fade #990 [thomas@fesch.at]
- You should return elements with style="display:none;" or a like class for this to work best and have no chance of flicker.
-
- * Effect.Squish for scaling down an element and making it disappear at the end #972 [thomas@fesch.at]
-
- * Effect.Scale for smoothly scaling images or text up and down #972 [thomas@fesch.at]
-
- * Effect.Fade which smoothly turns opacity from 100 to 0 and then hides the element #960 [thomas@fesch.at]
-
-* Added Request#xml_http_request? (and an alias xhr?) to that'll return true when the request came from one of the Javascript helper methods (Ajax). This can be used to give one behavior for modern browsers supporting Ajax, another to old browsers #1127 [Sam Stephenson]
-
-* Changed render_partial to take local assigns as the second parameter instead of an explicit object and then the assigns. So the API changes from:
-
- <%= render_partial "account", person, "rules" => regulations.rules %>
-
- ...to:
-
- <%= render_partial "account", :account => person, :rules => regulations.rules %>
-
- The old API will still work, though, and render_partial "account" will still assume :account => @account.
-
-* Added support for web servers that use PATH_INFO instead of REQUEST_URI like IIS #1014 [BradG/Nicholas Seckar]
-
-* Added graceful handling of PUT, DELETE, and OPTIONS requests for a complete coverage of REST functionality #1136 [Josh Knowles]
-
-* Fixed that you can now pass an alternative :href option to link_to_function/remote in order to point to somewhere other than # if the javascript fails or is turned off. You can do the same with form_remote_tag by passing in :action. #1113 [Sam Stephenson]
-
-* Fixed DateHelper to return values on the option tags such that they'll work properly in IE with form_remote_tag #1024 [Scott Raymond]
-
-* Fixed FormTagHelper#check_box to respect checked #1049 [DelynnB]
-
-* Added that render_partial called from a controller will use the action name as default #828 [Dan Peterson]
-
-* Added Element.toggle, Element.show, and Element.hide to the prototype javascript library. Toggle.display has been deprecated, but will still work #992 [Lucas Carlson]
-
-* Added that deleting a cookie should not just set it to an empty string but also instantly expire it #1118 [todd@robotcoop.com]
-
-* Added AssetTagHelper#image_path, AssetTagHelper#javascript_path, and AssetTagHelper#stylesheet_path #1110 [Larry Halff]
-
-* Fixed url_for(nil) in functional tests #1116 [Alisdair McDiarmid]
-
-* Fixed error handling of broken layouts #1115 [Michael Schubert]
-
-* Added submit_to_remote that allows you to trigger an Ajax form submition at the click of the submission button, which allows for multiple targets in a single form through the use of multiple submit buttons #930 [yrashk@gmail.com]
-
-* Fixed pagination to work with joins #1034 [scott@sigkill.org]
-
-* Fixed that *rest parameter in map.connect couldn't accept an empty list #1037 [Dee Zsombor]
-
-* Added :confirm option to link_to_remote just like link_to has #1082 [yrashk@fp.org.ua]
-
-* Added minute_step as an option to select_minute (and the helpers that use it) to jump in larger increments than just 1 minute. At 15, it would return 0, 15, 30, 45 options #1085 [ordwaye@evergreen.edu]
-
-* Fixed that an exception would be thrown when an empty form was submitted #1090 [jan@ulbrich-boerwang.de]
-
-* Moved TextHelper#human_size to NumberHelper#number_to_human_size, but kept an deprecated alias to the old method name
-
-* Fixed that the content-type for some browsers could include an additional \r which made wonky things happen #1067 [Thomas Fuchs]
-
-* Fixed that radio buttons shouldn't have a default size attribute #1074 [hendrik@mans.de]
-
-* Added ActionView::Helpers::InstanceTag::DEFAULT_RADIO_OPTIONS that contains a hash of default options for radio buttons #1074 [hendrik@mans.de]
-
-* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 [Nicholas Seckar]
-
-* Added JavascriptHelper#periodically_call_remote in order to create areas of a page that update automatically at a set interval #945 [Jon Tirsen]
-
-* Fixed Cache#expire_matched_fragments that couldn't recognize the difference between string and url_for options #1030 [Stefan Kaes]
-
-* Added simulation of @request.request_uri in functional tests #1038 [Jamis Buck]
-
-* Fixed autolinking to work better in more cases #1013 [Jamis Buck]
-
-* Added the possible of using symbols in form helpers that relate to instance variables like text_field :account, :name in addition to text_field "account", "name"'
-
-* Fixed javascript_include_tag to output type instead of language and conform to XHTML #1018 [Rick Olson]
-
-* Added NumberHelper for common string representations like phone number, currency, and percentage #1015 [DeLynn]
-
-* Added pagination for scaffolding (10 items per page) #964 [mortonda@dgrmm.net]
-
-* Added assert_no_cookie and fixed assert_cookie_equal to deal with non-existing cookies #979 [Jeremy Kemper]
-
-* Fixed :overwrite_param so it doesn't delete but reject elements from @request.parameters #982 [raphinou@yahoo.com]
-
-* Added :method option to verify for ensuring that either GET, POST, etc is allowed #984 [Jamis Buck]
-
-* Added options to set cc, bcc, subject, and body for UrlHelper#mail_to #966 [DeLynn]
-
-* Fixed include_blank for select_hour/minute/second #527 [edward@debian.org]
-
-* Improved the message display on the exception handler pages #963 [Johan Sorensen]
-
-* Fixed that on very rare occasions, webrick would raise a NoMethodError: private method 'split' called for nil #1001 [Flurin Egger]
-
-* Fixed problem with page caching #958 [Rick Olson]
-
-
-*1.7.0* (27th March, 2005)
-
-* Added ActionController::Base.page_cache_extension for setting the page cache file extension (the default is .html) #903 [Andreas]
-
-* Fixed "bad environment variable value" exception caused by Safari, Apache, and Ajax calls #918
-
-* Fixed that pagination_helper would ignore :params #947 [Sebastian Kanthak]
-
-* Added :owerwrite_params back to url_for and friends -- it was AWL since the introduction of Routes #921 [raphinou]
-
-* Added :position option to link_to_remote/form_remote_tag that can be either :before, :top, :bottom, or :after and specifies where the return from the method should be inserted #952 [Matthew McCray/Sam Stephenson]
-
-* Added Effect.Highlight to prototype.js to do Yellow Fade Technique (of 37signals' fame) on any container #952 [Sam Stephenson/court3nay]
-
-* Added include_seconds option as the third parameter to distance_of_time_in_words which will render "less than a minute" in higher resolution ("less than 10 seconds" etc) #944 [thomas@fesch.at]
-
-* Added fourth option to process in test cases to specify the content of the flash #949 [Jamis Buck]
-
-* Added Verifications that allows you to specify preconditions to actions in form of statements like <tt>verify :only => :update_post, :params => "admin_privileges", :redirect_to => { :action => "settings" }</tt>, which ensure that the update_post action is only called if admin_privileges is available as a parameter -- otherwise the user is redirected to settings. #897 [Jamis Buck]
-
-* Fixed Form.Serialize for the JavascriptHelper to also seriliaze password fields #934 [dweitzman@gmail.com]
-
-* Added JavascriptHelper#escape_javascript as a public method (was private) and made it escape both single and double quotes and new lines #940 [mortonda@dgrmm.net]
-
-* Added trailing_slash option to url_for, so you can generate urls ending in a slash. Note that is currently not recommended unless you need it for special reasons since it breaks caching #937 [stian@grytoyr.net]
-
-* Added expire_matched_fragments(regular_expression) to clear out a lot of fragment caches at once #927 [Rick Olson]
-
-* Fixed the problems with : and ? in file names for fragment caches on Windows #927 [Rick Olson]
-
-* Added TextHelper#human_size for formatting file sizes, like human_size(1234567) => 1.2 MB #943 [thomas@fesch.at]
-
-* Fixed link_to :confirm #936 [Nicholas Seckar]
-
-* Improved error reporting especially around never shallowing exceptions. Debugging helpers should be much easier now #980 [Nicholas Seckar]
-
-* Fixed Toggle.display in prototype.js #902 [Lucas Carlson]
-
-
-*1.6.0* (22th March, 2005)
-
-* Added a JavascriptHelper and accompanying prototype.js library that opens the world of Ajax to Action Pack with a large array of options for dynamically interacting with an application without reloading the page #884 [Sam Stephenson/David]
-
-* Added pagination support through both a controller and helper add-on #817 [Sam Stephenson]
-
-* Fixed routing and helpers to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Lütke]
-
-* Added a much improved Flash module that allows for finer-grained control on expiration and allows you to flash the current action #839 [Caio Chassot]. Example of flash.now:
-
- class SomethingController < ApplicationController
- def save
- ...
- if @something.save
- # will redirect, use flash
- flash[:message] = 'Save successful'
- redirect_to :action => 'list'
- else
- # no redirect, message is for current action, use flash.now
- flash.now[:message] = 'Save failed, review'
- render_action 'edit'
- end
- end
- end
-
-* Added to_param call for parameters when composing an url using url_for from something else than strings #812 [Sam Stephenson]. Example:
-
- class Page
-   def initialize(number)
-     @number = number
-   end
-   # ...
-   def to_param
-     @number.to_s
-   end
- end
-
- You can now use instances of Page with url_for:
-
- class BarController < ApplicationController
-   def baz
-     page = Page.new(4)
-     url = url_for :page => page # => "http://foo/bar/baz?page=4"
-   end
- end
-
-* Fixed form helpers to query Model#id_before_type_cast instead of Model#id as a temporary workaround for Ruby 1.8.2 warnings #818 [DeLynn B]
-
-* Fixed TextHelper#markdown to use blank? instead of empty? so it can deal with nil strings passed #814 [Johan Sörensen]
-
-* Added TextHelper#simple_format as a non-dependency text presentation helper #814 [Johan Sörensen]
-
-* Added that the html options disabled, readonly, and multiple can all be treated as booleans. So specifying <tt>disabled => :true</tt> will give <tt>disabled="disabled"</tt>. #809 [mindel]
-
-* Added path collection syntax for Routes that will gobble up the rest of the url and pass it on to the controller #830 [rayners]. Example:
-
- map.connect 'categories/*path_info', :controller => 'categories', :action => 'show'
-
- A request for /categories/top-level-cat, would give @params[:path_info] with "top-level-cat".
- A request for /categories/top-level-cat/level-1-cat, would give @params[:path_info] with "top-level-cat/level-1-cat" and so forth.
-
- The @params[:path_info] return is really an array, but where to_s has been overwritten to do join("/").
-
-* Fixed options_for_select on selected line issue #624 [Florian Weber]
-
-* Added CaptureHelper with CaptureHelper#capture and CaptureHelper#content_for. See documentation in helper #837 [Tobias Lütke]
-
-* Fixed :anchor use in url_for #821 [Nicholas Seckar]
-
-* Removed the reliance on PATH_INFO as it was causing problems for caching and inhibited the new non-vhost support #822 [Nicholas Seckar]
-
-* Added assigns shortcut for @response.template.assigns to controller test cases [Jeremy Kemper]. Example:
-
- Before:
-
- def test_list
- assert_equal 5, @response.template.assigns['recipes'].size
- assert_equal 8, @response.template.assigns['categories'].size
- end
-
- After:
-
- def test_list
- assert_equal 5, assigns(:recipes).size
- assert_equal 8, assigns(:categories).size
- end
-
-* Added TagHelper#image_tag and deprecated UrlHelper#link_image_to (recommended approach is to combine image_tag and link_to instead)
-
-* Fixed textilize to be resilient to getting nil parsed (by using Object#blank? instead of String#empty?)
-
-* Fixed that the :multipart option in FormTagHelper#form_tag would be ignored [Yonatan Feldman]
-
-
-*1.5.1* (7th March, 2005)
-
-* Fixed that the routes.rb file wouldn't be found on symlinked setups due to File.expand_path #793 [piotr@t-p-l.com]
-
-* Changed ActiveRecordStore to use Marshal instead of YAML as the latter proved troublesome in persisting circular dependencies. Updating existing applications MUST clear their existing session table from data to start using this updated store #739 [Jamis Buck]
-
-* Added shortcut :id assignment to render_component and friends (before you had to go through :params) #784 [Lucas Carlson]
-
-* Fixed that map.connect should convert arguments to strings #780 [Nicholas Seckar]
-
-* Added UrlHelper#link_to_if/link_to_unless to enable other conditions that just link_to_unless_current #757 [mindel]
-
-* Fixed that single quote was not escaped in a UrlHelper#link_to javascript confirm #549 [Scott Barron]
-
-* Removed the default border on link_image_to (it broke xhtml strict) -- can be specified with :border => 0 #517 [?/caleb]
-
-* Fixed that form helpers would treat string and symbol keys differently in html_options (and possibly create duplicate entries) #112 [Jeremy Kemper]
-
-* Fixed that broken pipe errors (clients disconnecting in mid-request) could bring down a fcgi process
-
-* Added the original exception message to session recall errors (so you can see which class wasnt required)
-
-* Fixed that RAILS_ROOT might not be defined when AP was loaded, so do a late initialization of the ROUTE_FILE #761 [Scott Barron]
-
-* Fix request.path_info and clear up LoadingModule behavior #754 [Nicholas Seckar]
-
-* Fixed caching to be aware of extensions (so you can cache files like api.wsdl or logo.png) #734 [Nicholas Seckar]
-
-* Fixed that Routes would raise NameErrors if a controller component contains characters that are not valid constant names #733 [Nicholas Seckar]
-
-* Added PATH_INFO access from the request that allows urls like the following to be interpreted by rails: http://www.example.com/dispatcher.cgi/controller/action -- that makes it possible to use rails as a CGI under lighttpd and would also allow (for example) Rublog to be ported to rails without breaking existing links to Rublog-powered blogs. #728 [Jamis Buck]
-
-* Fixed that caching the root would result in .html not index.html #731, #734 [alisdair/Nicholas Seckar]
-
-
-*1.5.0* (24th February, 2005)
-
-* Added Routing as a replacement for mod_rewrite pretty urls [Nicholas Seckar]. Read more in ActionController::Base.url_for and on http://manuals.rubyonrails.com/read/book/9
-
-* Added components that allows you to call other actions for their rendered response while execution another action. You can either delegate the entire response rendering or you can mix a partial response in with your other content. Read more on http://manuals.rubyonrails.com/read/book/14
-
-* Fixed that proxy IPs do not follow all RFC1918 nets #251 [caleb@aei-tech.com]
-
-* Added Base#render_to_string to parse a template and get the result back as a string #479
-
-* Fixed that send_file/data can work even if render* has been called before in action processing to render the content of a file to be send for example #601
-
-* Added FormOptionsHelper#time_zone_select and FormOptionsHelper#time_zone_options_for_select to work with the new value object TimeZone in Active Support #688 [Jamis Buck]
-
-* Added FormHelper#file_field and FormTagHelper#file_field_tag for creating file upload fields
-
-* Added :order option for date_select that allows control over the order in which the date dropdowns is used and which of them should be used #619 [Tim Bates]. Examples:
-
- date_select("post", "written_on", :order => [:day, :month, :year])
- date_select("user", "birthday", :order => [:month, :day])
-
-* Added ActionView::Base.register_template_handler for easy integration of an alternative template language to ERb and Builder. See test/controller/custom_handler_test.rb for a usage example #656 [Jamis Buck]
-
-* Added AssetTagHelper that provides methods for linking a HTML page together with other assets, such as javascripts, stylesheets, and feeds.
-
-* Added FormTagHelper that provides a number of methods for creating form tags that doesn't rely on conventions with an object assigned to the template like FormHelper does. With the FormTagHelper, you provide the names and values yourself.
-
-* Added Afghanistan, Iran, and Iraq to the countries list used by FormOptions#country_select and FormOptions#country_options_for_select
-
-* Renamed link_to_image to link_image_to (since thats what it actually does) -- kept alias for the old method name
-
-* Fixed textilize for RedCloth3 to keep doing hardbreaks
-
-* Fixed that assert_template_xpath_matches did not indicate when a path was not found #658 [Eric Hodel]
-
-* Added TextHelper#auto_link to turn email addresses and urls into ahrefs
-
-* Fixed that on validation errors, scaffold couldn't find template #654 [mindel]
-
-* Added Base#hide_action(*names) to hide public methods from a controller that would otherwise have been callable through the URL. For the majority of cases, its preferred just to make the methods you don't want to expose protected or private (so they'll automatically be hidden) -- but if you must have a public method, this is a way to make it uncallable. Base#hidden_actions retrieve the list of all hidden actions for the controller #644 [Nicholas Seckar]
-
-* Fixed that a bunch of methods from ActionController::Base was accessible as actions (callable through a URL) when they shouldn't have been #644 [Nicholas Seckar]
-
-* Added UrlHelper#current_page?(options) method to check if the url_for options passed corresponds to the current page
-
-* Fixed https handling on other ports than 443 [Alan Gano]
-
-* Added follow_redirect method for functional tests that'll get-request the redirect that was made. Example:
-
- def test_create_post
- post :create, "post" => { "title" => "Exciting!" }
- assert_redirected_to :action => "show"
-
- follow_redirect
- assert_rendered_file "post/show"
- end
-
- Limitation: Only works for redirects to other actions within the same controller.
-
-* Fixed double requiring of models with the same name as the controller
-
-* Fixed that query params could be forced to nil on a POST due to the raw post fix #562 [moriq@moriq.com]
-
-* Fixed that cookies shouldn't be frozen in TestRequest #571 [Eric Hodel]
-
-
-*1.4.0* (January 25th, 2005)
-
-* Fixed problems with ActiveRecordStore under the development environment in Rails
-
-* Fixed the ordering of attributes in the xml-decleration of Builder #540 [woeye]
-
-* Added @request.raw_post as a convenience access to @request.env['RAW_POST_DATA'] #534 [Tobias Lütke]
-
-* Added support for automatic id-based indexing for lists of items #532 [dblack]. Example:
-
- <% @students.each do |@student| %>
- <%= text_field "student[]", "first_name", :size => "20" %>
- <%= text_field "student[]", "last_name" %>
- <%= text_field "student[]", "grade", :size => "5" %>
- <% end %>
-
- ...would produce, for say David Black with id 123 and a grace of C+:
-
- <input id="student_123_first_name" name="student[123][first_name]" size="20" size="30" type="text" value="David" />
- <input id="student_123_last_name" name="student[123][last_name]" size="30" type="text" value="Black" />
- <input id="student_123_grade" name="student[123][grade]" size="5" type="text" value="C+" />
-
-* Added :application_prefix to url_for and friends that makes it easier to setup Rails in non-vhost environments #516 [Jamis Buck]
-
-* Added :encode option to mail_to that'll allow you to masquarede the email address behind javascript or hex encoding #494 [Lucas Carlson]
-
-* Fixed that the content-header was being set to application/octet_stream instead of application/octet-stream on send_date/file [Alexey]
-
-* Removed the need for passing the binding when using CacheHelper#cache
-
-* Added TestResponse#binary_content that'll return as a string the data sent through send_data/send_file for testing #500 [Alexey]
-
-* Added @request.env['RAW_POST_DATA'] for people who need access to the data before Ruby's CGI has parsed it #505 [Jeremy Kemper]
-
-* Fixed that a default fragment store wan't being set to MemoryStore as intended.
-
-* Fixed that all redirect and render calls now return true, so you can use the pattern of "do and return". Example:
-
- def show
- redirect_to(:action => "login") and return unless @person.authenticated?
- render_text "I won't happen unless the person is authenticated"
- end
-
-* Added that renders and redirects called in before_filters will have the same effect as returning false: stopping the chain. Example:
-
- class WeblogController
- before_filter { |c| c.send(:redirect_to_url("http://www.farfaraway.com")}) }
-
- def hello
- render_text "I will never be called"
- end
- end
-
-
-* Added that only one render or redirect can happen per action. The first call wins and subsequent calls are ignored. Example:
-
- def do_something
- redirect_to :action => "elsewhere"
- render_action "overthere"
- end
-
- Only the redirect happens. The rendering call is simply ignored.
-
-
-*1.3.1* (January 18th, 2005)
-
-* Fixed a bug where cookies wouldn't be set if a symbol was used instead of a string as the key
-
-* Added assert_cookie_equal to assert the contents of a named cookie
-
-* Fixed bug in page caching that prevented it from working at all
-
-
-*1.3.0* (January 17th, 2005)
-
-* Added an extensive caching module that offers three levels of granularity (page, action, fragment) and a variety of stores.
- Read more in ActionController::Caching.
-
-* Added the option of passing a block to ActiveRecordHelper#form in order to add more to the auto-generated form #469 [dom@sisna.com]
-
- form("entry", :action => "sign") do |form|
- form << content_tag("b", "Department")
- form << collection_select("department", "id", @departments, "id", "name")
- end
-
-* Added arrays as a value option for params in url_for and friends #467 [Eric Anderson]. Example:
-
- url_for(:controller => 'user', :action => 'delete', :params => { 'username' => %( paul john steve ) } )
- # => /user/delete?username[]=paul&username[]=john&username[]=steve
-
-* Fixed that controller tests can now assert on the use of cookies #466 [Alexey]
-
-* Fixed that send_file would "remember" all the files sent by adding to the headers again and again #458 [Jeremy Kemper]
-
-* Fixed url rewriter confusion when the controller or action name was a substring of the controller_prefix or action_prefix
-
-* Added conditional layouts like <tt>layout "weblog_standard", :except => :rss</tt> #452 [Marcel Molina Jr.]
-
-* Fixed that MemCacheStore wasn't included by default and added default MemCache object pointing to localhost #447 [Lucas Carlson]
-
-* Added fourth argument to render_collection_of_partials that allows you to specify local_assigns -- just like render_partial #432 [Ryan Davis]
-
-* Fixed that host would choke when cgi.host returned nil #432 [Tobias Lütke]
-
-* Added that form helpers now take an index option #448 [Tim Bates]
-
- Example:
- text_field "person", "name", "index" => 3
-
- Becomes:
- <input type="text" name="person[3][name]" id="person_3_name" value="<%= @person.name %>" />
-
-* Fixed three issues with retrying breakpoints #417 [Florian Gross]
-
- 1. Don't screw up pages that use multiple values for the same parameter (?foo=bar&foo=qux was converted to ?foo=barqux)
- 2. Don't screw up all forms when you click the "Retry with Breakpoint" link multiple times instead of reloading
- (This caused the parameters to be added multiple times for GET forms leading to trouble.)
- 3. Don't add ?BP-RETRY=1 multiple times
-
-* Added that all renders and redirects now return false, so they can be used as the last line in before_filters to stop execution.
-
- Before:
- def authenticate
- unless @session[:authenticated]
- redirect_to :controller => "account", :action => "login"
- return false
- end
- end
-
- After:
- def authenticate
- redirect_to(:controller => "account", :action => "login") unless @session[:authenticated]
- end
-
-* Added conditional filters #431 [Marcel Molina Jr.]. Example:
-
- class JournalController < ActionController::Base
- # only require authentication if the current action is edit or delete
- before_filter :authorize, :only_on => [ :edit, :delete ]
-
- private
- def authorize
- # redirect to login unless authenticated
- end
- end
-
-* Added Base#render_nothing as a cleaner way of doing render_text "" when you're not interested in returning anything but an empty response.
-
-* Added the possibility of passing nil to UrlHelper#link_to to use the link itself as the name
-
-
-*1.2.0* (January 4th, 2005)
-
-* Added MemCacheStore for storing session data in Danga's MemCache system [Bob Cottrell]
- Depends on: MemCached server (http://www.danga.com/memcached/), MemCache client (http://raa.ruby-lang.org/project/memcache/)
-
-* Added thread-safety to the DRbStore #66, #389 [Ben Stiglitz]
-
-* Added DateHelper#select_time and DateHelper#select_second #373 [Scott Baron]
-
-* Added :host and :protocol options to url_for and friends to redirect to another host and protocol than the current.
-
-* Added class declaration for the MissingFile exception #388 [Kent Sibilev]
-
-* Added "short hypertext note with a hyperlink to the new URI(s)" to redirects to fulfill compliance with RFC 2616 (HTTP/1.1) section 10.3.3 #397 [Tim Bates]
-
-* Added second boolean parameter to Base.redirect_to_url and Response#redirect to control whether the redirect is permanent or not (301 vs 302) #375 [Hodel]
-
-* Fixed redirects when the controller and action is named the same. Still haven't fixed same controller, module, and action, though #201 [Josh Peek]
-
-* Fixed problems with running multiple functional tests in Rails under 1.8.2 by including hack for test/unit weirdness
-
-* Fixed that @request.remote_ip didn't work in the test environment #369 [Bruno Mattarollo]
-
-
-*1.1.0*
-
-* Added search through session to clear out association caches at the end of each request. This makes it possible to place Active Record objects
- in the session without worrying about stale data in the associations (the main object is still subject to caching, naturally) #347 [Tobias Lütke]
-
-* Added more informative exception when using helper :some_helper and the helper requires another file that fails, you'll get an
- error message tells you what file actually failed to load, rather than falling back on assuming it was the helper file itself #346 [dblack]
-
-* Added use of *_before_type_cast for all input and text fields. This is helpful for getting "100,000" back on a integer-based
- validation where the value would normally be "100".
-
-* Added Request#port_string to get something like ":8080" back on 8080 and "" on 80 (or 443 with https).
-
-* Added Request#domain (returns string) and Request#subdomains (returns array).
-
-* Added POST support for the breakpoint retries, so form processing that raises an exception can be retried with the original request [Florian Gross]
-
-* Fixed regression with Base#reset_session that wouldn't use the DEFAULT_SESSION_OPTIONS [adam@the-kramers.net]
-
-* Fixed error rendering of rxml documents to not just swallow the exception and return 0 (still not guessing the right line, but hey)
-
-* Fixed that textilize and markdown would instantiate their engines even on empty strings. This also fixes #333 [Ulysses]
-
-* Fixed UrlHelper#link_to_unless so it doesn't care if the id is a string or fixnum [Ryan Davis]
-
-
-*1.0.1*
-
-* Fixed a bug that would cause an ApplicationController to require itself three times and hence cause filters to be run three times [evl]
-
-
-*1.0*
-
-* Added that controllers will now attempt to require a model dependency with their name and in a singular attempt for their name.
- So both PostController and PostsController will automatically have the post.rb model required. If no model is found, no error is raised,
- as it is then expected that no match is available and the programmer will have included his own models.
-
-* Fixed DateHelper#date_select so that you can pass include_blank as an option even if you don't use start_year and end_year #59 [what-a-day]
-
-* Added that controllers will now search for a layout in $template_root/layouts/$controller_name.r(html|xml), so PostsController will look
- for layouts/posts.rhtml or layouts/posts.rxml and automatically configure this layout if found #307 [Marcel Molina Jr.]
-
-* Added FormHelper#radio_button to work with radio buttons like its already possible with check boxes [Michael Koziarski]
-
-* Added TemplateError#backtrace that makes it much easier to debug template errors from unit and functional tests
-
-* Added display of error messages with scaffolded form pages
-
-* Added option to ERB templates to swallow newlines by using <% if something -%> instead of just <% if something %>. Example:
-
- class SomeController < ApplicationController
- <% if options[:scaffold] %>
- scaffold :<%= singular_name %>
- <% end %>
- helper :post
-
- ...produces this on post as singular_name:
-
- class SomeController < ApplicationController
-
- scaffold :post
-
- helper :post
-
- ...where as:
-
- class SomeController < ApplicationController
- <% if options[:scaffold] -%>
- scaffold :<%= singular_name %>
- <% end -%>
- helper :post
-
- ...produces:
-
- class SomeController < ApplicationController
- scaffold :post
- helper :post
-
- [This undocumented gem for ERb was uncovered by bitsweat]
-
-* Fixed CgiRequest so that it'll now accept session options with Symbols as keys (as the documentation points out) [Suggested by Andreas]
-
-* Added that render_partial will always by default include a counter with value 1 unless there is a counter passed in via the
- local_assigns hash that overrides it. As a result, render_collection_of_partials can still be written in terms of render_partial
- and partials that make use of a counter can be called without problems from both render_collection_of_partials as well as
- render_partial #295 [Marcel Molina Jr.]
-
-* Fixed CgiRequest#out to fall back to #write if $stdout doesn't have #syswrite [Jeremy Kemper]
-
-* Fixed all helpers so that they use XHTML compliant double quotes for values instead of single quotes [htonl/Jeremy Kemper]
-
-* Added link_to_image(src, options = {}, html_options = {}). Documentation:
-
- Creates a link tag to the image residing at the +src+ using an URL created by the set of +options+. See the valid options in
- link:classes/ActionController/Base.html#M000021. It's also possible to pass a string instead of an options hash to
- get a link tag that just points without consideration. The <tt>html_options</tt> works jointly for the image and ahref tag by
- letting the following special values enter the options on the image and the rest goes to the ahref:
-
- ::alt: If no alt text is given, the file name part of the +src+ is used (capitalized and without the extension)
- ::size: Supplied as "XxY", so "30x45" becomes width="30" and height="45"
- ::align: Sets the alignment, no special features
-
- The +src+ can be supplied as a...
- * full path, like "/my_images/image.gif"
- * file name, like "rss.gif", that gets expanded to "/images/rss.gif"
- * file name without extension, like "logo", that gets expanded to "/images/logo.png"
-
-* Fixed to_input_field_tag so it no longer explicitly uses InstanceTag.value if value was specified in the options hash [evl]
-
-* Added the possibility of having validate be protected for assert_(in)valid_column #263 [Tobias Lütke]
-
-* Added that ActiveRecordHelper#form now calls url_for on the :action option.
-
-* Added all the HTTP methods as alternatives to the generic "process" for functional testing #276 [Tobias Lütke]. Examples:
-
- # Calls Controller#miletone with a GET request
- process :milestone
-
- # Calls Controller#miletone with a POST request that has parameters
- post :milestone, { "name" => "David" }
-
- # Calls Controller#milestone with a HEAD request that has both parameters and session data
- head :milestone, { "id" => 1 }, { "user_id" => 23 }
-
- This is especially useful for testing idiomatic REST web services.
-
-* Added proper handling of HEAD requests, so that content isn't returned (Request#head? added as well) #277 [Eric Hodel]
-
-* Added indifference to whether @headers["Content-Type"], @headers["Content-type"], or @headers["content-type"] is used.
-
-* Added TestSession#session_id that returns an empty string to make it easier to functional test applications that doesn't use
- cookie-based sessions #275 [jcf]
-
-* Fixed that cached template loading would still check the file system to see if the file existed #258 [Andreas Schwarz]
-
-* Added options to tailor header tag, div id, and div class on ActiveRecordHelper#error_messages_for [Josh Peek]
-
-* Added graceful handling of non-alphanumeric names and misplaced brackets in input parameters [Jeremy Kemper]
-
-* Added a new container for cookies that makes them more intuative to use. The old methods of cookie and @cookies have been deprecated.
-
- Examples for writing:
-
- cookies["user_name"] = "david" # => Will set a simple session cookie
- cookies["login"] = { "value" => "XJ-122", "expires" => Time.now + 360} # => Will set a cookie that expires in 1 hour
-
- Examples for reading:
-
- cookies["user_name"] # => "david"
- cookies.size # => 2
-
- Read more in ActionController::Cookies
-
- NOTE: If you were using the old accessor (cookies instead of @cookies), this could potentially break your code -- if you expect a full cookie object!
-
-* Added the opportunity to defined method_missing on a controller which will handle all requests for actions not otherwise defined #223 [timb]
-
-* Fixed AbstractRequest#remote_ip for users going through proxies - Patch #228 [Eric Hodel]
-
-* Added Request#ssl? which is shorthand for @request.protocol == "https://"
-
-* Added the choice to call form_tag with no arguments (resulting in a form posting to current action) [Jeremy Kemper]
-
-* Upgraded to Builder 1.2.1
-
-* Added :module as an alias for :controller_prefix to url_for and friends, so you can do redirect_to(:module => "shop", :controller => "purchases")
- and go to /shop/purchases/
-
-* Added support for controllers in modules through @params["module"].
-
-* Added reloading for dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use those environments for development.
- This is turned on by default, but can be turned off with ActionController::Base.reload_dependencies = false in production environments.
-
- NOTE: This will only have an effect if you use the new model, service, and observer class methods to mark dependencies. All libraries loaded through
- require will be "forever" cached. You can, however, use ActionController::Base.load_or_require("library") to get this behavior outside of the new
- dependency style.
-
-* Added that controllers will automatically require their own helper if possible. So instead of doing:
-
- class MsgController < ApplicationController
- helper :msg
- end
-
- ...you can just do:
-
- class MsgController < ApplicationController
- end
-
-* Added dependencies_on(layer) to query the dependencies of a controller. Examples:
-
- MsgController.dependencies_on(:model) # => [ :post, :comment, :attachment ]
- MsgController.dependencies_on(:service) # => [ :notification_service ]
- MsgController.dependencies_on(:observer) # => [ :comment_observer ]
-
-* Added a new dependency model with the class methods model, service, and observer. Example:
-
- class MsgController < ApplicationController
- model :post, :comment, :attachment
- service :notification_service
- observer :comment_observer
- end
-
- These new "keywords" remove the need for explicitly calling 'require' in most cases. The observer method even instantiates the
- observer as well as requiring it.
-
-* Fixed that link_to would escape & in the url again after url_for already had done so
-
-
-*0.9.5* (28)
-
-* Added helper_method to designate that a given private or protected method you should available as a helper in the view. [Jeremy Kemper]
-
-* Fixed assert_rendered_file so it actually verifies if that was the rendered file [htonl]
-
-* Added the option for sharing partial spacer templates just like partials themselves [radsaq]
-
-* Fixed that Russia was named twice in country_select [alexey]
-
-* Fixed request_origin to use remote_ip instead of remote_addr [Jeremy Kemper]
-
-* Fixed link_to breakage when nil was passed for html_options [alexey]
-
-* Fixed redirect_to on a virtual server setup with apache with a port other than the default where it would forget the port number [seanohalpin]
-
-* Fixed that auto-loading webrick on Windows would cause file uploads to fail [Jeremy Kemper]
-
-* Fixed issues with sending files on WEBrick by setting the proper binmode [Jeremy Kemper]
-
-* Added send_data as an alternative to send_file when the stream is not read off the filesystem but from a database or generated live [Jeremy Kemper]
-
-* Added a new way to include helpers that doesn't require the include hack and can go without the explicit require. [Jeremy Kemper]
-
- Before:
-
- module WeblogHelper
- def self.included(controller) #:nodoc:
- controller.ancestors.include?(ActionController::Base) ? controller.add_template_helper(self) : super
- end
- end
-
- require 'weblog_helper'
- class WeblogController < ActionController::Base
- include WeblogHelper
- end
-
- After:
-
- module WeblogHelper
- end
-
- class WeblogController < ActionController::Base
- helper :weblog
- end
-
-* Added a default content-type of "text/xml" to .rxml renders [Ryan Platte]
-
-* Fixed that when /controller/index was requested by the browser, url_for would generates wrong URLs [Ryan Platte]
-
-* Fixed a bug that would share cookies between users when using FastCGI and mod_ruby [The Robot Co-op]
-
-* Added an optional third hash parameter to the process method in functional tests that takes the session data to be used [alexey]
-
-* Added UrlHelper#mail_to to make it easier to create mailto: style ahrefs
-
-* Added better error messages for layouts declared with the .rhtml extension (which they shouldn't) [geech]
-
-* Added another case to DateHelper#distance_in_minutes to return "less than a minute" instead of "0 minutes" and "1 minute" instead of "1 minutes"
-
-* Added a hidden field to checkboxes generated with FormHelper#check_box that will make sure that the unchecked value (usually 0)
- is sent even if the checkbox is not checked. This relieves the controller from doing custom checking if the checkbox wasn't
- checked. BEWARE: This might conflict with your run-on-the-mill work-around code. [Tobias Lütke]
-
-* Fixed error_message_on to just use the first if more than one error had been added [Marcel Molina Jr.]
-
-* Fixed that URL rewriting with /controller/ was working but /controller was not and that you couldn't use :id on index [geech]
-
-* Fixed a bug with link_to where the :confirm option wouldn't be picked up if the link was a straight url instead of an option hash
-
-* Changed scaffolding of forms to use <label> tags instead of <b> to please W3C [evl]
-
-* Added DateHelper#distance_of_time_in_words_to_now(from_time) that works like distance_of_time_in_words,
- but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
-
-* Added assert_flash_equal(expected, key, message), assert_session_equal(expected, key, message),
- assert_assigned_equal(expected, key, message) to test the contents of flash, session, and template assigns.
-
-* Improved the failure report on assert_success when the action triggered a redirection [alexey].
-
-* Added "markdown" to accompany "textilize" as a TextHelper method for converting text to HTML using the Markdown syntax.
- BlueCloth must be installed in order for this method to become available.
-
-* Made sure that an active session exists before we attempt to delete it [Samuel]
-
-* Changed link_to with Javascript confirmation to use onclick instead of onClick for XHTML validity [Scott Barron]
-
-
-*0.9.0 (43)*
-
-* Added support for Builder-based templates for files with the .rxml extension. These new templates are an alternative to ERb that
- are especially useful for generating XML content, such as this RSS example from Basecamp:
-
- xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
- xml.channel do
- xml.title(@feed_title)
- xml.link(@url)
- xml.description "Basecamp: Recent items"
- xml.language "en-us"
- xml.ttl "40"
-
- for item in @recent_items
- xml.item do
- xml.title(item_title(item))
- xml.description(item_description(item)) if item_description(item)
- xml.pubDate(item_pubDate(item))
- xml.guid(@person.firm.account.url + @recent_items.url(item))
- xml.link(@person.firm.account.url + @recent_items.url(item))
-
- xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
- end
- end
- end
- end
-
- ...which will generate something like:
-
- <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <channel>
- <title>Web Site Redesign</title>
- <link>http://www.basecamphq.com/clients/travelcenter/1/</link>
- <description>Basecamp: Recent items</description>
- <language>en-us</language>
- <ttl>40</ttl>
- <item>
- <title>Post: don't you know</title>
- <description>&amp;lt;p&amp;gt;deeper and down&amp;lt;/p&amp;gt;</description>
- <pubDate>Fri, 20 Aug 2004 21:13:50 CEST</pubDate>
- <guid>http://www.basecamphq.com/clients/travelcenter/1/msg/assets/96976/comments</guid>
- <link>http://www.basecamphq.com/clients/travelcenter/1/msg/assets/96976/comments</link>
- <dc:creator>David H. Heinemeier</dc:creator>
- </item>
- <item>
- <title>Milestone completed: Design Comp 2</title>
- <pubDate>Mon, 9 Aug 2004 14:42:06 CEST</pubDate>
- <guid>http://www.basecamphq.com/clients/travelcenter/1/milestones/#49</guid>
- <link>http://www.basecamphq.com/clients/travelcenter/1/milestones/#49</link>
- </item>
- </channel>
- </rss>
-
- The "xml" local variable is automatically available in .rxml templates. You construct the template by calling a method with the name
- of the tag you want. Options for the tag can be specified as a hash parameter to that method.
-
- Builder-based templates can be mixed and matched with the regular ERb ones. The only thing that differentiates them is the extension.
- No new methods have been added to the public interface to handle them.
-
- Action Pack ships with a version of Builder, but it will use the RubyGems version if you have one installed.
-
- Read more about Builder on: http://onestepback.org/index.cgi/Tech/Ruby/StayingSimple.rdoc
-
- [Builder is created by Jim Weirich]
-
-* Added much improved support for functional testing [what-a-day].
-
- # Old style
- def test_failing_authenticate
- @request.request_uri = "/login/authenticate"
- @request.action = "authenticate"
- @request.request_parameters["user_name"] = "nop"
- @request.request_parameters["password"] = ""
-
- response = LoginController.process_test(@request)
-
- assert_equal "The username and/or password you entered is invalid.", response.session["flash"]["alert"]
- assert_equal "http://37signals.basecamp.com/login/", response.headers["location"]
- end
-
- # New style
- def test_failing_authenticate
- process :authenticate, "user_name" => "nop", "password" => ""
- assert_flash_has 'alert'
- assert_redirected_to :action => "index"
- end
-
- See a full example on http://codepaste.org/view/paste/334
-
-* Increased performance by up to 100% with a revised cookie class that fixes the performance problems with the
- default one that ships with 1.8.1 and below. It replaces the inheritance on SimpleDelegator with DelegateClass(Array)
- following the suggestion from Matz on:
- http://groups.google.com/groups?th=e3a4e68ba042f842&seekm=c3sioe%241qvm%241%40news.cybercity.dk#link14
-
-* Added caching for compiled ERb templates. On Basecamp, it gave between 8.5% and 71% increase in performance [Andreas Schwarz].
-
-* Added implicit counter variable to render_collection_of_partials [Marcel Molina Jr.]. From the docs:
-
- <%= render_collection_of_partials "ad", @advertisements %>
-
- This will render "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display. An iteration counter
- will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the
- example above, the template would be fed +ad_counter+.
-
-* Fixed problems with two sessions being maintained on reset_session that would particularly screw up ActiveRecordStore.
-
-* Fixed reset_session to start an entirely new session instead of merely deleting the old. So you can now safely access @session
- after calling reset_ression and expect it to work.
-
-* Added @request.get?, @request.post?, @request.put?, @request.delete? as convenience query methods for @request.method [geech]
-
-* Added @request.method that'll return a symbol representing the HTTP method, such as :get, :post, :put, :delete [geech]
-
-* Changed @request.remote_ip and @request.host to work properly even when a proxy is in front of the application [geech]
-
-* Added JavaScript confirm feature to link_to. Documentation:
-
- The html_options have a special feature for creating javascript confirm alerts where if you pass
- :confirm => 'Are you sure?', the link will be guarded with a JS popup asking that question.
- If the user accepts, the link is processed, otherwise not.
-
-* Added link_to_unless_current as a UrlHelper method [Sam Stephenson]. Documentation:
-
- Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
- controller, action, and id are the same as the link's, in which case only the name is returned (or the
- given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
- to the page currently being viewed.
-
-* Fixed that UrlRewriter (the driver for url_for, link_to, etc) would blow up when the anchor was an integer [alexey]
-
-* Added that layouts defined with no directory defaults to layouts. So layout "weblog/standard" will use
- weblog/standard (as always), but layout "standard" will use layouts/standard.
-
-* Fixed that partials (or any template starting with an underscore) was publically viewable [Marten]
-
-* Added HTML escaping to text_area helper.
-
-* Added :overwrite_params to url_for and friends to keep the parameters as they were passed to the current action and only overwrite a subset.
- The regular :params will clear the slate so you need to manually add in existing parameters if you want to reuse them. [raphinou]
-
-* Fixed scaffolding problem with composite named objects [Moo Jester]
-
-* Added the possibility for shared partials. Example:
-
- <%= render_partial "advertisement/ad", ad %>
-
- This will render the partial "advertisement/_ad.rhtml" regardless of which controller this is being called from.
-
- [Jacob Fugal]
-
-* Fixed crash when encountering forms that have empty-named fields [James Prudente]
-
-* Added check_box form helper method now accepts true/false as well as 1/0 [what-a-day]
-
-* Fixed the lacking creation of all directories with install.rb [Dave Steinberg]
-
-* Fixed that date_select returns valid XHTML selected options [Andreas Schwarz]
-
-* Fixed referencing an action with the same name as a controller in url_for [what-a-day]
-
-* Fixed the destructive nature of Base#attributes= on the argument [Kevin Watt]
-
-* Changed ActionControllerError to decent from StandardError instead of Exception. It can now be caught by a generic rescue.
-
-* Added SessionRestoreError that is raised when a session being restored holds objects where there is no class available.
-
-* Added block as option for inline filters. So what used to be written as:
-
- before_filter Proc { |controller| return false if controller.params["stop_action"] }
-
- ...can now be as:
-
- before_filter { |controller| return false if controller.params["stop_action"] }
-
- [Jeremy Kemper]
-
-* Made the following methods public (was protected): url_for, controller_class_name, controller_name, action_name
- This makes it easier to write filters without cheating around the encapsulation with send.
-
-* ActionController::Base#reset_session now sticks even if you access @session afterwards [Kent Sibilev]
-
-* Improved the exception logging so the log file gets almost as much as in-browser debugging.
-
-* Changed base class setup from AbstractTemplate/ERbTemplate to ActionView::Base. This change should be harmless unless you were
- accessing Action View directly in which case you now need to reference the Base class.\
-
-* Added that render_collection_of_partials returns nil if the collection is empty. This makes showing a “no items” message easier.
- For example: <%= render_collection_of_partials("message", @messages) || "No messages found." %> [Sam Stephenson]
-
-* Added :month_before_year as an option to date_select to get the month select before the year. Especially useful for credit card forms.
-
-* Added :add_month_numbers to select_month to get options like "3 - March".
-
-* Removed Base.has_active_layout? as it couldn't answer the question without the instance. Use Base#active_layout instead.
-
-* Removed redundant call to update on ActionController::Base#close_session [Andreas Schwarz]
-
-* Fixed that DRb Store accidently started its own server (instead of just client) [Andreas]
-
-* Fixed strip_links so it now works across multiple lines [Chad Fowler]
-
-* Fixed the TemplateError exception to show the proper trace on to_s (useful for unit test debugging)
-
-* Implemented class inheritable attributes without eval [Caio Chassot]
-
-* Made TextHelper#concat accept binding as it would otherwise not work
-
-* The FormOptionsHelper will now call to_s on the keys and values used to generate options
-
-
-*0.8.5*
-
-* Introduced passing of locally scoped variables between templates:
-
- You can pass local variables to sub templates by using a hash of with the variable
- names as keys and the objects as values:
-
- <%= render "shared/header", { "headline" => "Welcome", "person" => person } %>
-
- These can now be accessed in shared/header with:
-
- Headline: <%= headline %>
- First name: <%= person.first_name %>
-
-* Introduced the concept of partials as a certain type of sub templates:
-
- There's also a convenience method for rendering sub templates within the current
- controller that depends on a single object (we call this kind of sub templates for
- partials). It relies on the fact that partials should follow the naming convention
- of being prefixed with an underscore -- as to separate them from regular templates
- that could be rendered on their own. In the template for Advertiser#buy, we could have:
-
- <% for ad in @advertisements %>
- <%= render_partial "ad", ad %>
- <% end %>
-
- This would render "advertiser/_ad.rhtml" and pass the local variable +ad+
- for the template to display.
-
- == Rendering a collection of partials
-
- The example of partial use describes a familar pattern where a template needs
- to iterate over a array and render a sub template for each of the elements.
- This pattern has been implemented as a single method that accepts an array and
- renders a partial by the same name of as the elements contained within. So the
- three-lined example in "Using partials" can be rewritten with a single line:
-
- <%= render_collection_of_partials "ad", @advertisements %>
-
- So this will render "advertiser/_ad.rhtml" and pass the local variable +ad+ for
- the template to display.
-
-* Improved send_file by allowing a wide range of options to be applied [Jeremy Kemper]:
-
- Sends the file by streaming it 4096 bytes at a time. This way the
- whole file doesn't need to be read into memory at once. This makes
- it feasible to send even large files.
-
- Be careful to sanitize the path parameter if it coming from a web
- page. send_file(@params['path'] allows a malicious user to
- download any file on your server.
-
- Options:
- * <tt>:filename</tt> - specifies the filename the browser will see.
- Defaults to File.basename(path).
- * <tt>:type</tt> - specifies an HTTP content type.
- Defaults to 'application/octet-stream'.
- * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
- Valid values are 'inline' and 'attachment' (default).
- * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream
- the file. Defaults to 4096.
-
- The default Content-Type and Content-Disposition headers are
- set to download arbitrary binary files in as many browsers as
- possible. IE versions 4, 5, 5.5, and 6 are all known to have
- a variety of quirks (especially when downloading over SSL).
-
- Simple download:
- send_file '/path/to.zip'
-
- Show a JPEG in browser:
- send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
-
- Read about the other Content-* HTTP headers if you'd like to
- provide the user with more information (such as Content-Description).
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
-
- Also be aware that the document may be cached by proxies and browsers.
- The Pragma and Cache-Control headers declare how the file may be cached
- by intermediaries. They default to require clients to validate with
- the server before releasing cached responses. See
- http://www.mnot.net/cache_docs/ for an overview of web caching and
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
- for the Cache-Control header spec.
-
-* Added pluralize method to the TextHelper that makes it easy to get strings like "1 message", "3 messages"
-
-* Added proper escaping for the rescues [Andreas Schwarz]
-
-* Added proper escaping for the option and collection tags [Andreas Schwarz]
-
-* Fixed NaN errors on benchmarking [Jim Weirich]
-
-* Fixed query string parsing for URLs that use the escaped versions of & or ; as part of a key or value
-
-* Fixed bug with custom Content-Type headers being in addition to rather than instead of the default header.
- (This bug didn't matter with neither CGI or mod_ruby, but FCGI exploded on it) [With help from Ara T. Howard]
-
-
-*0.8.0*
-
-* Added select, collection_select, and country_select to make it easier for Active Records to set attributes through
- drop-down lists of options. Example:
-
- <%= select "person", "gender", %w( Male Female ) %>
-
- ...would give the following:
-
- <select name="person[gender]" id="person_gender"><option>Male</option><option>Female</option></select>
-
-* Added an option for getting multiple values on a single form name into an array instead of having the last one overwrite.
- This is especially useful for groups of checkboxes, which can now be written as:
-
- <input type="checkbox" name="rights[]" value="CREATE" />
- <input type="checkbox" name="rights[]" value="UPDATE" />
- <input type="checkbox" name="rights[]" value="DELETE" />
-
- ...and retrieved in the controller action with:
-
- @params["rights"] # => [ "CREATE", "UPDATE", "DELETE" ]
-
- The old behavior (where the last one wins, "DELETE" in the example) is still available. Just don't add "[]" to the
- end of the name. [Scott Baron]
-
-* Added send_file which uses the new render_text block acceptance to make it feasible to send large files.
- The files is sent with a bunch of voodoo HTTP headers required to get arbitrary files to download as
- expected in as many browsers as possible (eg, IE hacks). Example:
-
- def play_movie
- send_file "/movies/that_movie.avi"
- end
-
- [Jeremy Kemper]
-
-* render_text now accepts a block for deferred rendering. Useful for streaming large files, displaying
- a “please wait” message during a complex search, etc. Streaming example:
-
- render_text do |response|
- File.open(path, 'rb') do |file|
- while buf = file.read(1024)
- print buf
- end
- end
- end
-
- [Jeremy Kemper]
-
-* Added a new Tag Helper that can generate generic tags programmatically insted of through HTML. Example:
-
- tag("br", "clear" => "all") => <br clear="all" />
-
- ...that's usually not terribly interesting (unless you have a lot of options already in a hash), but it
- gives way for more specific tags, like the new form tag:
-
- form_tag({ :controller => "weblog", :action => "update" }, { :multipart => "true", "style" => "width: 200px"}) =>
- <form action="/weblog/update" enctype="multipart/formdata" style="width: 200px">
-
- There's even a "pretty" version for people who don't like to open tags in code and close them in HTML:
-
- <%= start_form_tag :action => "update" %>
- # all the input fields
- <%= end_form_tag %>
-
- (end_form_tag just returns "</form>")
-
-* The selected parameter in options_for_select may now also an array of values to be selected when
- using a multiple select. Example:
-
- options_for_select([ "VISA", "Mastercard", "Discover" ], ["VISA", "Discover"]) =>
- <option selected>VISA</option>\n<option>Mastercard</option>\n<option selected>Discover</option>
-
- [Scott Baron]
-
-* Changed the URL rewriter so controller_prefix and action_prefix can be used in isolation. You can now do:
-
- url_for(:controller_prefix => "clients")
-
- ...or:
-
- url_for(:action_prefix => "category/messages")
-
- Neither would have worked in isolation before (:controller_prefix required a :controller and :action_prefix required an :action)
-
-* Started process of a cleaner separation between Action Controller and ERb-based Action Views by introducing an
- abstract base class for views. And Amita adapter could be fitted in more easily now.
-
-* The date helper methods date_select and datetime_select now also use the field error wrapping
- (div with class fieldWithErrors by default).
-
-* The date helper methods date_select and datetime_select can now discard selects
-
-* Added option on AbstractTemplate to specify a different field error wrapping. Example:
-
- ActionView::AbstractTemplate.field_error_proc = Proc.new do |html, instance|
- "<p>#{instance.method_name + instance.error_message}</p><div style='background-color: red'>#{html}</div>"
- end
-
- ...would give the following on a Post#title (text field) error:
-
- <p>Title can't be empty</p>
- <div style='background-color: red'>
- <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
- </div>
-
-* The UrlHelper methods url_for and link_to will now by default only return paths, not complete URIs.
- That should make it easier to fit a Rails application behind a proxy or load-balancer.
- You can overwrite this by passing :only_path => false as part of the options. [Suggested by U235]
-
-* Fixed bug with having your own layout for use with scaffolding [Kevin Radloff]
-
-* Fixed bug where redirect_to_path didn't append the port on non-standard ports [dhawkins]
-
-* Scaffolding plays nicely with single-table inheritance (LoadErrors are caught) [Jeremy Kemper]
-
-* Scaffolding plays nice with plural models like Category/categories [Jeremy Kemper]
-
-* Fixed missing suffix appending in scaffolding [Kevin Radloff]
-
-
-*0.7.9*
-
-* The "form" method now present boolean fields from PostgreSQL as drop-down menu. [Scott]
-
-* Scaffolding now automatically attempts to require the class that's being scaffolded.
-
-* Scaffolding will use the current active layout, instead of its own, if one has been specified. Example:
-
- class WeblogController < ActionController::Base
- layout "layouts/weblog"
- scaffold :post
- end
-
- [Suggested by Scott]
-
-* Changed url_for (and all the that drives, like redirect_to, link_to, link_for) so you can pass it a symbol instead of a hash.
- This symbol is a method reference which is then called to calculate the url. Example:
-
- class WeblogController < ActionController::Base
- def update
- # do some update
- redirect_to :dashboard_url
- end
-
- protected
- def dashboard_url
- if @project.active?
- url_for :controller => "project", :action => "dashboard"
- else
- url_for :controller => "account", :action => "dashboard"
- end
- end
- end
-
-* Added default_url_options to specialize behavior for all url_for (and friends) calls:
-
- Overwrite to implement a number of default options that all url_for-based methods will use.
- The default options should come in form of a hash, just like the one you would use for
- url_for directly. Example:
-
- def default_url_options(options)
- { :controller_prefix => @project.active? ? "projects/" : "accounts/" }
- end
-
- As you can infer from the example, this is mostly useful for situations where you want to
- centralize dynamic dissions about the urls as they stem from the business domain. Please note
- that any individual url_for call can always override the defaults set by this method.
-
-
-* Changed url_for so that an "id" passed in the :params is not treated special. You need to use the dedicated :id to get
- the special auto path-params treatment. Considering the url http://localhost:81/friends/list
-
- url_for(:action => "show", :params => { "id" => 5 })
- ...used to give http://localhost:81/friends/show/5
- ......now gives http://localhost:81/friends/show?id=5
-
- If you want the automated id behavior, do:
-
- url_for(:action => "show", :id => 5 )
- ....which gives http://localhost:81/friends/show/5
-
-
-* Fixed problem with anchor being inserted before path parameters with url_for (and friends)
-
-
-*0.7.8*
-
-* Fixed session bug where you couldn't store any objects that didn't exist in the standard library
- (such as Active Record objects).
-
-* Added reset_session method for Action Controller objects to clear out all objects in the session.
-
-* Fixed that exceptions raised during filters are now also caught by the default rescues
-
-* Added new around_filter for doing before and after filtering with a single object [Florian Weber]:
-
- class WeblogController < ActionController::Base
- around_filter BenchmarkingFilter.new
-
- # Before this action is performed, BenchmarkingFilter#before(controller) is executed
- def index
- end
- # After this action has been performed, BenchmarkingFilter#after(controller) is executed
- end
-
- class BenchmarkingFilter
- def initialize
- @runtime
- end
-
- def before
- start_timer
- end
-
- def after
- stop_timer
- report_result
- end
- end
-
-* Added the options for specifying a different name and id for the form helper methods than what is guessed [Florian Weber]:
-
- text_field "post", "title"
- ...just gives: <input id="post_title" name="post[title]" size="30" type="text" value="" />
-
- text_field "post", "title", "id" => "title_for_post", "name" => "first_post_title"
- ...can now give: <input id="title_for_post" name="first_post_title" size="30" type="text" value="" />
-
-* Added DebugHelper with a single "debug" method for doing pretty dumps of objects in the view
- (now used in the default rescues to better present the contents of session and template variables)
-
-* Added note to log about the templates rendered within layouts (before just the layout was shown)
-
-* Fixed redirects on https setups [Andreas]
-
-* Fixed scaffolding problem on the edit action when using :suffix => true [Scott]
-
-* Fixed scaffolding problem where implementing list.rhtml wouldn't work for the index action
-
-* URLs generated now uses &amp; instead of just & so pages using it can validate with W3C [Spotted by Andreas]
-
-
-*0.7.7*
-
-* Fixed bug in CGI extension that prevented multipart forms from working
-
-
-*0.7.6*
-
-* Included ERB::Util so all templates can easily escape HTML content with <%=h @person.content %>
-
-* All requests are now considered local by default, so everyone will be exposed to detailed debugging screens on errors.
- When the application is ready to go public, set ActionController::Base.consider_all_requests_local to false,
- and implement the protected method local_request? in the controller to determine when debugging screens should be shown.
-
-* Fixed three bugs with the url_for/redirect_to/link_to handling. Considering the url http://localhost:81/friends/show/1
-
- url_for(:action => "list")
- ...used to give http://localhost:81/friends/list/1
- ......now gives http://localhost:81/friends/list
-
- url_for(:controller => "friends", :action => "destroy", :id => 5)
- ...used to give http://localhost:81/friends/destroy
- ......now gives http://localhost:81/friends/destroy/5
-
- Considering the url http://localhost:81/teachers/show/t
-
- url_for(:action => "list", :id => 5)
- ...used to give http://localhost:81/5eachers/list/t
- ......now gives http://localhost:81/teachers/list/5
-
- [Reported by David Morton & Radsaq]
-
-* Logs exception to logfile in addition to showing them for local requests
-
-* Protects the eruby load behind a begin/rescue block. eRuby is not required to run ActionController.
-
-* Fixed install.rb to also install clean_logger and the templates
-
-* Added ActiveRecordStore as a session option. Read more in lib/action_controller/session/active_record_store.rb [Tim Bates]
-
-* Change license to MIT License (and included license file in package)
-
-* Application error page now returns status code 500 instead of 200
-
-* Fixed using Procs as layout handlers [Florian Weber]
-
-* Fixed bug with using redirects ports other than 80
-
-* Added index method that calls list on scaffolding
-
-
-*0.7.5*
-
-* First public release
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
new file mode 100644
index 0000000000..ec53efa6b7
--- /dev/null
+++ b/actionpack/CHANGELOG.md
@@ -0,0 +1,5913 @@
+## Rails 4.0.0 (unreleased) ##
+
+* `expires_in` accepts a `must_revalidate` flag. If true, "must-revalidate"
+ is added to the Cache-Control header. *fxn*
+
+* Add `date_field` and `date_field_tag` helpers which render an `input[type="date"]` tag *Olek Janiszewski*
+
+* Adds `image_url`, `javascript_url`, `stylesheet_url`, `audio_url`, `video_url`, and `font_url`
+ to assets tag helper. These URL helpers will return the full path to your assets. This is useful
+ when you are going to reference this asset from external host. *Prem Sichanugrist*
+
+* Default responder will now always use your overridden block in `respond_with` to render your response. *Prem Sichanugrist*
+
+* Allow `value_method` and `text_method` arguments from `collection_select` and
+ `options_from_collection_for_select` to receive an object that responds to `:call`,
+ such as a `proc`, to evaluate the option in the current element context. This works
+ the same way with `collection_radio_buttons` and `collection_check_boxes`.
+
+ *Carlos Antonio da Silva + Rafael Mendonça França*
+
+* Add `collection_check_boxes` form helper, similar to `collection_select`:
+ Example:
+
+ collection_check_boxes :post, :author_ids, Author.all, :id, :name
+ # Outputs something like:
+ <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" />
+ <label for="post_author_ids_1">D. Heinemeier Hansson</label>
+ <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
+ <label for="post_author_ids_2">D. Thomas</label>
+ <input name="post[author_ids][]" type="hidden" value="" />
+
+ The label/check_box pairs can be customized with a block.
+
+ *Carlos Antonio da Silva + Rafael Mendonça França*
+
+* Add `collection_radio_buttons` form helper, similar to `collection_select`:
+ Example:
+
+ collection_radio_buttons :post, :author_id, Author.all, :id, :name
+ # Outputs something like:
+ <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" />
+ <label for="post_author_id_1">D. Heinemeier Hansson</label>
+ <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
+ <label for="post_author_id_2">D. Thomas</label>
+
+ The label/radio_button pairs can be customized with a block.
+
+ *Carlos Antonio da Silva + Rafael Mendonça França*
+
+* check_box with `:form` html5 attribute will now replicate the `:form`
+ attribute to the hidden field as well. *Carlos Antonio da Silva*
+
+* `label` form helper accepts :for => nil to not generate the attribute. *Carlos Antonio da Silva*
+
+* Add `:format` option to number_to_percentage *Rodrigo Flores*
+
+* Add `config.action_view.logger` to configure logger for ActionView. *Rafael Mendonça França*
+
+* Deprecated ActionController::Integration in favour of ActionDispatch::Integration
+
+* Deprecated ActionController::IntegrationTest in favour of ActionDispatch::IntegrationTest
+
+* Deprecated ActionController::PerformanceTest in favour of ActionDispatch::PerformanceTest
+
+* Deprecated ActionController::AbstractRequest in favour of ActionDispatch::Request
+
+* Deprecated ActionController::Request in favour of ActionDispatch::Request
+
+* Deprecated ActionController::AbstractResponse in favour of ActionDispatch::Response
+
+* Deprecated ActionController::Response in favour of ActionDispatch::Response
+
+* Deprecated ActionController::Routing in favour of ActionDispatch::Routing
+
+* check_box helper with :disabled => true will generate a disabled hidden field to conform with the HTML convention where disabled fields are not submitted with the form.
+ This is a behavior change, previously the hidden tag had a value of the disabled checkbox.
+ *Tadas Tamosauskas*
+
+* `favicon_link_tag` helper will now use the favicon in app/assets by default. *Lucas Caton*
+
+* `ActionView::Helpers::TextHelper#highlight` now defaults to the
+ HTML5 `mark` element. *Brian Cardarella*
+
+## Rails 3.2.0 (January 20, 2012) ##
+
+* Add `config.action_dispatch.default_charset` to configure default charset for ActionDispatch::Response. *Carlos Antonio da Silva*
+
+* Deprecate setting default charset at controller level, use the new `config.action_dispatch.default_charset` instead. *Carlos Antonio da Silva*
+
+* Deprecate ActionController::UnknownAction in favour of AbstractController::ActionNotFound. *Carlos Antonio da Silva*
+
+* Deprecate ActionController::DoubleRenderError in favour of AbstractController::DoubleRenderError. *Carlos Antonio da Silva*
+
+* Deprecate method_missing handling for not found actions, use action_missing instead. *Carlos Antonio da Silva*
+
+* Deprecate ActionController#rescue_action, ActionController#initialize_template_class, and ActionController#assign_shortcuts.
+ These methods were not being used internally anymore and are going to be removed in Rails 4. *Carlos Antonio da Silva*
+
+* Use a BodyProxy instead of including a Module that responds to
+ close. Closes #4441 if Active Record is disabled assets are delivered
+ correctly *Santiago Pastorino*
+
+* Rails initialization with initialize_on_precompile = false should set assets_dir *Santiago Pastorino*
+
+* Add font_path helper method *Santiago Pastorino*
+
+* Depends on rack ~> 1.4.0 *Santiago Pastorino*
+
+* Add :gzip option to `caches_page`. The default option can be configured globally using `page_cache_compression` *Andrey Sitnik*
+
+* The ShowExceptions middleware now accepts a exceptions application that is responsible to render an exception when the application fails. The application is invoked with a copy of the exception in `env["action_dispatch.exception"]` and with the PATH_INFO rewritten to the status code. *José Valim*
+
+* Add `button_tag` support to ActionView::Helpers::FormBuilder.
+
+ This support mimics the default behavior of `submit_tag`.
+
+ Example:
+
+ <%= form_for @post do |f| %>
+ <%= f.button %>
+ <% end %>
+
+* Date helpers accept a new option, `:use_two_digit_numbers = true`, that renders select boxes for months and days with a leading zero without changing the respective values.
+ For example, this is useful for displaying ISO8601-style dates such as '2011-08-01'. *Lennart Fridén and Kim Persson*
+
+* Make ActiveSupport::Benchmarkable a default module for ActionController::Base, so the #benchmark method is once again available in the controller context like it used to be *DHH*
+
+* Deprecated implied layout lookup in controllers whose parent had a explicit layout set:
+
+ class ApplicationController
+ layout "application"
+ end
+
+ class PostsController < ApplicationController
+ end
+
+ In the example above, Posts controller will no longer automatically look up for a posts layout.
+
+ If you need this functionality you could either remove `layout "application"` from ApplicationController or explicitly set it to nil in PostsController. *José Valim*
+
+* Rails will now use your default layout (such as "layouts/application") when you specify a layout with `:only` and `:except` condition, and those conditions fail. *Prem Sichanugrist*
+
+ For example, consider this snippet:
+
+ class CarsController
+ layout 'single_car', :only => :show
+ end
+
+ Rails will use 'layouts/single_car' when a request comes in `:show` action, and use 'layouts/application' (or 'layouts/cars', if exists) when a request comes in for any other actions.
+
+* form_for with +:as+ option uses "#{action}_#{as}" as css class and id:
+
+ Before:
+
+ form_for(@user, :as => 'client') # => "<form class="client_new">..."
+
+ Now:
+
+ form_for(@user, :as => 'client') # => "<form class="new_client">..."
+
+ *Vasiliy Ermolovich*
+
+* Allow rescue responses to be configured through a railtie as in `config.action_dispatch.rescue_responses`. Please look at ActiveRecord::Railtie for an example *José Valim*
+
+* Allow fresh_when/stale? to take a record instead of an options hash *DHH*
+
+* Assets should use the request protocol by default or default to relative if no request is available *Jonathan del Strother*
+
+* Log "Filter chain halted as CALLBACKNAME rendered or redirected" every time a before callback halts *José Valim*
+
+* You can provide a namespace for your form to ensure uniqueness of id attributes on form elements.
+ The namespace attribute will be prefixed with underscore on the generate HTML id. *Vasiliy Ermolovich*
+
+ Example:
+
+ <%= form_for(@offer, :namespace => 'namespace') do |f| %>
+ <%= f.label :version, 'Version' %>:
+ <%= f.text_field :version %>
+ <% end %>
+
+* Refactor ActionDispatch::ShowExceptions. The controller is responsible for choosing to show exceptions when `consider_all_requests_local` is false.
+
+ It's possible to override `show_detailed_exceptions?` in controllers to specify which requests should provide debugging information on errors. The default value is now false, meaning local requests in production will no longer show the detailed exceptions page unless `show_detailed_exceptions?` is overridden and set to `request.local?`.
+
+* Responders now return 204 No Content for API requests without a response body (as in the new scaffold) *José Valim*
+
+* Added ActionDispatch::RequestId middleware that'll make a unique X-Request-Id header available to the response and enables the ActionDispatch::Request#uuid method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog *DHH*
+
+* Limit the number of options for select_year to 1000.
+
+ Pass the :max_years_allowed option to set your own limit.
+
+ *Libo Cannici*
+
+* Passing formats or handlers to render :template and friends is deprecated. For example: *Nick Sutterer & José Valim*
+
+ render :template => "foo.html.erb"
+
+ Instead, you can provide :handlers and :formats directly as option:
+ render :template => "foo", :formats => [:html, :js], :handlers => :erb
+
+* Changed log level of warning for missing CSRF token from :debug to :warn. *Mike Dillon*
+
+* content_tag_for and div_for can now take the collection of records. It will also yield the record as the first argument if you set a receiving argument in your block *Prem Sichanugrist*
+
+ So instead of having to do this:
+
+ @items.each do |item|
+ content_tag_for(:li, item) do
+ Title: <%= item.title %>
+ end
+ end
+
+ You can now do this:
+
+ content_tag_for(:li, @items) do |item|
+ Title: <%= item.title %>
+ end
+
+* send_file now guess the mime type *Esad Hajdarevic*
+
+* Mime type entries for PDF, ZIP and other formats were added *Esad Hajdarevic*
+
+* Generate hidden input before select with :multiple option set to true.
+ This is useful when you rely on the fact that when no options is set,
+ the state of select will be sent to rails application. Without hidden field
+ nothing is sent according to HTML spec *Bogdan Gusiev*
+
+* Refactor ActionController::TestCase cookies *Andrew White*
+
+ Assigning cookies for test cases should now use cookies[], e.g:
+
+ cookies[:email] = 'user@example.com'
+ get :index
+ assert_equal 'user@example.com', cookies[:email]
+
+ To clear the cookies, use clear, e.g:
+
+ cookies.clear
+ get :index
+ assert_nil cookies[:email]
+
+ We now no longer write out HTTP_COOKIE and the cookie jar is
+ persistent between requests so if you need to manipulate the environment
+ for your test you need to do it before the cookie jar is created.
+
+* ActionController::ParamsWrapper on ActiveRecord models now only wrap
+ attr_accessible attributes if they were set, if not, only the attributes
+ returned by the class method attribute_names will be wrapped. This fixes
+ the wrapping of nested attributes by adding them to attr_accessible.
+
+## Rails 3.1.4 (unreleased) ##
+
+* Allow to use asset_path on named_routes aliasing RailsHelper's
+ asset_path to path_to_asset *Adrian Pike*
+
+* Assets should use the request protocol by default or default to
+ relative if no request is available *Jonathan del Strother*
+
+## Rails 3.1.3 (November 20, 2011) ##
+
+* Fix using `translate` helper with a html translation which uses the `:count` option for
+ pluralization.
+
+ *Jon Leighton*
+
+## Rails 3.1.2 (November 18, 2011) ##
+
+* Fix XSS security vulnerability in the `translate` helper method. When using interpolation
+ in combination with HTML-safe translations, the interpolated input would not get HTML
+ escaped. *GH 3664*
+
+ Before:
+
+ translate('foo_html', :something => '<script>') # => "...<script>..."
+
+ After:
+
+ translate('foo_html', :something => '<script>') # => "...&lt;script&gt;..."
+
+ *Sergey Nartimov*
+
+* Upgrade sprockets dependency to ~> 2.1.0
+
+* Ensure that the format isn't applied twice to the cache key, else it becomes impossible
+ to target with expire_action.
+
+ *Christopher Meiklejohn*
+
+* Swallow error when can't unmarshall object from session.
+
+ *Bruno Zanchet*
+
+* Implement a workaround for a bug in ruby-1.9.3p0 where an error would be raised
+ while attempting to convert a template from one encoding to another.
+
+ Please see http://redmine.ruby-lang.org/issues/5564 for details of the bug.
+
+ The workaround is to load all conversions into memory ahead of time, and will
+ only happen if the ruby version is *exactly* 1.9.3p0. The hope is obviously that
+ the underlying problem will be resolved in the next patchlevel release of
+ 1.9.3.
+
+ *Jon Leighton*
+
+* Ensure users upgrading from 3.0.x to 3.1.x will properly upgrade their flash object in session (issues #3298 and #2509)
+
+## Rails 3.1.1 (October 07, 2011) ##
+
+* javascript_path and stylesheet_path now refer to /assets if asset pipelining
+ is on. *Santiago Pastorino*
+
+* button_to support form option. Now you're able to pass for example
+ 'data-type' => 'json'. *ihower*
+
+* image_path and image_tag should use /assets if asset pipelining is turned
+ on. Closes #3126 *Santiago Pastorino and christos*
+
+* Avoid use of existing precompiled assets during rake assets:precompile run.
+ Closes #3119 *Guillermo Iguaran*
+
+* Copy assets to nondigested filenames too *Santiago Pastorino*
+
+* Give precedence to `config.digest = false` over the existence of
+ manifest.yml asset digests *christos*
+
+* escape options for the stylesheet_link_tag method *Alexey Vakhov*
+
+* Re-launch assets:precompile task using (Rake.)ruby instead of Kernel.exec so
+ it works on Windows *cablegram*
+
+* env var passed to process shouldn't be modified in process method. *Santiago
+ Pastorino*
+
+* `rake assets:precompile` loads the application but does not initialize
+ it.
+ To the app developer, this means configuration add in
+ config/initializers/* will not be executed.
+ Plugins developers need to special case their initializers that are
+ meant to be run in the assets group by adding :group => :assets. *José Valim*
+
+* Sprockets uses config.assets.prefix for asset_path *asee*
+
+* FileStore key_file_path properly limit filenames to 255 characters. *phuibonhoa*
+
+* Fix Hash#to_query edge case with html_safe strings. *brainopia*
+
+* Allow asset tag helper methods to accept :digest => false option in order to completely avoid the digest generation.
+ Useful for linking assets from static html files or from emails when the user could probably look at an older html email with an older asset. *Santiago Pastorino*
+
+* Don't mount Sprockets server at config.assets.prefix if config.assets.compile is false. *Mark J. Titorenko*
+
+* Set relative url root in assets when controller isn't available for Sprockets (eg. Sass files using asset_path). Fixes #2435 *Guillermo Iguaran*
+
+* Fix basic auth credential generation to not make newlines. GH #2882
+
+* Fixed the behavior of asset pipeline when config.assets.digest and config.assets.compile are false and requested asset isn't precompiled.
+ Before the requested asset were compiled anyway ignoring that the config.assets.compile flag is false. *Guillermo Iguaran*
+
+* CookieJar is now Enumerable. Fixes #2795
+
+* Fixed AssetNotPrecompiled error raised when rake assets:precompile is compiling certain .erb files. See GH #2763 #2765 #2805 *Guillermo Iguaran*
+
+* Manifest is correctly placed in assets path when default assets prefix is changed. Fixes #2776 *Guillermo Iguaran*
+
+* Fixed stylesheet_link_tag and javascript_include_tag to respect additional options passed by the users when debug is on. *Guillermo Iguaran*
+
+
+## Rails 3.1.0 (August 30, 2011) ##
+
+* Param values are `paramified` in controller tests. *David Chelimsky*
+
+* x_sendfile_header now defaults to nil and config/environments/production.rb doesn't set a particular value for it. This allows servers to set it through X-Sendfile-Type. *Santiago Pastorino*
+
+* The submit form helper does not generate an id "object_name_id" anymore. *fbrusatti*
+
+* Make sure respond_with with :js tries to render a template in all cases *José Valim*
+
+* json_escape will now return a SafeBuffer string if it receives SafeBuffer string *tenderlove*
+
+* Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string *Prem Sichanugrist*
+
+* Fix escape_js to work correctly with the new SafeBuffer restriction *Paul Gallagher*
+
+* Brought back alternative convention for namespaced models in i18n *thoefer*
+
+ Now the key can be either "namespace.model" or "namespace/model" until further deprecation.
+
+* It is prohibited to perform a in-place SafeBuffer mutation *tenderlove*
+
+ The old behavior of SafeBuffer allowed you to mutate string in place via
+ method like `sub!`. These methods can add unsafe strings to a safe buffer,
+ and the safe buffer will continue to be marked as safe.
+
+ An example problem would be something like this:
+
+ <%= link_to('hello world', @user).sub!(/hello/, params[:xss]) %>
+
+ In the above example, an untrusted string (`params[:xss]`) is added to the
+ safe buffer returned by `link_to`, and the untrusted content is successfully
+ sent to the client without being escaped. To prevent this from happening
+ `sub!` and other similar methods will now raise an exception when they are called on a safe buffer.
+
+ In addition to the in-place versions, some of the versions of these methods which return a copy of the string will incorrectly mark strings as safe. For example:
+
+ <%= link_to('hello world', @user).sub(/hello/, params[:xss]) %>
+
+ The new versions will now ensure that *all* strings returned by these methods on safe buffers are marked unsafe.
+
+ You can read more about this change in http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2e516e7acc96c4fb
+
+* Warn if we cannot verify CSRF token authenticity *José Valim*
+
+* Allow AM/PM format in datetime selectors *Aditya Sanghi*
+
+* Only show dump of regular env methods on exception screen (not all the rack crap) *DHH*
+
+* auto_link has been removed with no replacement. If you still use auto_link
+ please install the rails_autolink gem:
+ http://github.com/tenderlove/rails_autolink
+
+ *tenderlove*
+
+* Added streaming support, you can enable it with: *José Valim*
+
+ class PostsController < ActionController::Base
+ stream :only => :index
+ end
+
+ Please read the docs at `ActionController::Streaming` for more information.
+
+* Added `ActionDispatch::Request.ignore_accept_header` to ignore accept headers and only consider the format given as parameter *José Valim*
+
+* Created `ActionView::Renderer` and specified an API for `ActionView::Context`, check those objects for more information *José Valim*
+
+* Added `ActionController::ParamsWrapper` to wrap parameters into a nested hash, and will be turned on for JSON request in new applications by default *Prem Sichanugrist*
+
+ This can be customized by setting `ActionController::Base.wrap_parameters` in `config/initializer/wrap_parameters.rb`
+
+* RJS has been extracted out to a gem. *fxn*
+
+* Implicit actions named not_implemented can be rendered. *Santiago Pastorino*
+
+* Wildcard route will always match the optional format segment by default. *Prem Sichanugrist*
+
+ For example if you have this route:
+
+ match '*pages' => 'pages#show'
+
+ by requesting '/foo/bar.json', your `params[:pages]` will be equals to "foo/bar" with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `:format => false` like this:
+
+ match '*pages' => 'pages#show', :format => false
+
+* Added Base.http_basic_authenticate_with to do simple http basic authentication with a single class method call *DHH*
+
+ class PostsController < ApplicationController
+ USER_NAME, PASSWORD = "dhh", "secret"
+
+ before_filter :authenticate, :except => [ :index ]
+
+ def index
+ render :text => "Everyone can see me!"
+ end
+
+ def edit
+ render :text => "I'm only accessible if you know the password"
+ end
+
+ private
+ def authenticate
+ authenticate_or_request_with_http_basic do |user_name, password|
+ user_name == USER_NAME && password == PASSWORD
+ end
+ end
+ end
+
+ ..can now be written as
+
+ class PostsController < ApplicationController
+ http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
+
+ def index
+ render :text => "Everyone can see me!"
+ end
+
+ def edit
+ render :text => "I'm only accessible if you know the password"
+ end
+ end
+
+* Allow you to add `force_ssl` into controller to force browser to transfer data via HTTPS protocol on that particular controller. You can also specify `:only` or `:except` to specific it to particular action. *DHH and Prem Sichanugrist*
+
+* Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash *DHH*
+
+ form_for(@post, remote: true, method: :delete) instead of form_for(@post, remote: true, html: { method: :delete })
+
+* Make JavaScriptHelper#j() an alias for JavaScriptHelper#escape_javascript() -- note this then supersedes the Object#j() method that the JSON gem adds within templates using the JavaScriptHelper *DHH*
+
+* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. *Prem Sichanugrist, fxn*
+
+* URL parameters which return false for to_param now appear in the query string (previously they were removed) *Andrew White*
+
+* URL parameters which return nil for to_param are now removed from the query string *Andrew White*
+
+* ActionDispatch::MiddlewareStack now uses composition over inheritance. It is
+ no longer an array which means there may be methods missing that were not tested.
+
+* Add an :authenticity_token option to form_tag for custom handling or to omit the token (pass :authenticity_token => false). *Jakub Kuźma, Igor Wiedler*
+
+* HTML5 button_tag helper. *Rizwan Reza*
+
+* Template lookup now searches further up in the inheritance chain. *Artemave*
+
+* Brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not. *Piotr Sarnacki*
+
+* url_for and named url helpers now accept :subdomain and :domain as options, *Josh Kalderimis*
+
+* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). *Josh Kalderimis*
+
+* Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). *Piotr Sarnacki*
+
+* Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options:
+
+ tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)})
+ # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
+
+ Keys are dasherized. Values are JSON-encoded, except for strings and symbols. *Stephen Celis*
+
+* Deprecate old template handler API. The new API simply requires a template handler to respond to call. *José Valim*
+
+* :rhtml and :rxml were finally removed as template handlers. *José Valim*
+
+* Moved etag responsibility from ActionDispatch::Response to the middleware stack. *José Valim*
+
+* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. *José Valim*
+
+* file_field automatically adds :multipart => true to the enclosing form. *Santiago Pastorino*
+
+* Renames csrf_meta_tag -> csrf_meta_tags, and aliases csrf_meta_tag for backwards compatibility. *fxn*
+
+* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. *Yehuda Katz, Carl Lerche*
+
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.6 (April 5, 2011) ##
+
+* Fixed XSS vulnerability in `auto_link`. `auto_link` no longer marks input as
+ html safe. Please make sure that calls to auto_link() are wrapped in a
+ sanitize(), or a raw() depending on the type of input passed to auto_link().
+ For example:
+
+ <%= sanitize(auto_link(some_user_input)) %>
+
+ Thanks to Torben Schulz for reporting this. The fix can be found here:
+ 61ee3449674c591747db95f9b3472c5c3bd9e84d
+
+* Fixes the output of `rake routes` to be correctly match to the behavior of the application, as the regular expression used to match the path is greedy and won't capture the format part by default *Prem Sichanugrist*
+
+* Fixes an issue with number_to_human when converting values which are less than 1 but greater than -1 *Josh Kalderimis*
+
+* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. *Prem Sichanugrist, fxn*
+
+* URL parameters which return nil for to_param are now removed from the query string *Andrew White*
+
+* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 *Santiago Pastorino*
+
+* Make TranslationHelper#translate use the :rescue_format option in I18n 0.5.0 *Sven Fuchs*
+
+* Fix regression: javascript_include_tag shouldn't raise if you register an expansion key with nil or [] value *Santiago Pastorino*
+
+* Fix Action caching bug where an action that has a non-cacheable response always renders a nil response body. It now correctly renders the response body. *Cheah Chu Yeow*
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* When ActiveRecord::Base objects are sent to predicate methods, the id of the object should be sent to ARel, not the ActiveRecord::Base object.
+
+* :constraints routing should only do sanity checks against regular expressions. String arguments are OK.
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* The helper number_to_currency accepts a new :negative_format option to be able to configure how to render negative amounts. *Don Wilson*
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* No Changes, just a version bump.
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* password_field renders with nil value by default making the use of passwords secure by default, if you want to render you should do for instance f.password_field(:password, :value => @user.password) *Santiago Pastorino*
+
+* Symbols and strings in routes should yield the same behavior. Note this may break existing apps that were using symbols with the new routes API. *José Valim*
+
+* Add clear_helpers as a way to clean up all helpers added to this controller, maintaining just the helper with the same name as the controller. *José Valim*
+
+* Support routing constraints in functional tests. *Andrew White*
+
+* Add a header that tells Internet Explorer (all versions) to use the best available standards support. *Yehuda Katz*
+
+* Allow stylesheet/javascript extensions to be changed through railties. *Josh Kalderimis*
+
+* link_to, button_to, and tag/tag_options now rely on html_escape instead of escape_once. *fxn*
+
+* url_for returns always unescaped strings, and the :escape option is gone. *fxn*
+
+* Added accept-charset parameter and _snowman hidden field to force the contents
+ of Rails POSTed forms to be in UTF-8 *Yehuda Katz*
+
+* Upgrade to Rack 1.2.1 *Jeremy Kemper*
+
+* Allow :path to be given to match/get/post/put/delete instead of :path_names in the new router *Carlos Antônio da Silva*
+
+* Added resources_path_names to the new router DSL *José Valim*
+
+* Allow options to be given to the namespace method in the new router *Carlos Antônio da Silva*
+
+* Deprecate :name_prefix in the new router DSL *José Valim*
+
+* Add shallow routes back to the new router *Diego Carrion, Andrew White*
+
+ resources :posts do
+ shallow do
+ resources :comments
+ end
+ end
+
+ You can now use comment_path for /comments/1 instead of post_comment_path for /posts/1/comments/1.
+
+* Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 *Guillermo Álvarez*
+
+* Removed textilize, textilize_without_paragraph and markdown helpers. *Santiago Pastorino*
+
+* Remove middleware laziness *José Valim*
+
+* Make session stores rely on request.cookie_jar and change set_session semantics to return the cookie value instead of a boolean. *José Valim*
+
+* OAuth 2: HTTP Token Authorization support to complement Basic and Digest Authorization. *Rick Olson*
+
+* Fixed inconsistencies in form builder and view helpers #4432 *Neeraj Singh*
+
+* Both :xml and :json renderers now forwards the given options to the model, allowing you to invoke them as render :xml => @projects, :include => :tasks *José Valim, Yehuda Katz*
+
+* Renamed the field error CSS class from fieldWithErrors to field_with_errors for consistency. *Jeremy Kemper*
+
+* Add support for shorthand routes like /projects/status(.:format) #4423 *Diego Carrion*
+
+* Changed translate helper so that it doesn’t mark every translation as safe HTML. Only keys with a "_html" suffix and keys named "html" are considered to be safe HTML. All other translations are left untouched. *Craig Davey*
+
+* New option :as added to form_for allows to change the object name. The old <% form_for :client, @post %> becomes <% form_for @post, :as => :client %> *spastorino*
+
+* Removed verify method in controllers. *JV*
+ It's now available as a plugin at http://github.com/rails/verification
+
+* Removed input, form, error_messages_for and error_message_on from views. *JV*
+ It's now available as a plugin at http://github.com/rails/dynamic_form
+
+* Routes can be scoped by controller module. *Jeremy Kemper*
+
+ # /session => Auth::SessionsController
+ scope :module => 'auth' do
+ resource :session
+ end
+
+* Added #favicon_link_tag, it uses #image_path so in particular the favicon gets an asset ID *fxn*
+
+* Fixed that default locale templates should be used if the current locale template is missing *DHH*
+
+* Added all the new HTML5 form types as individual form tag methods (search, url, number, etc) #3646 *Stephen Celis*
+
+* Changed the object used in routing constraints to be an instance of
+ ActionDispatch::Request rather than Rack::Request *YK*
+
+* Changed ActionDispatch::Request#method to return a String, to be compatible
+ with Rack::Request. Added ActionDispatch::Request#method_symbol to
+ return a symbol form of the request method. *YK*
+
+* Changed ActionDispatch::Request#method to return the original
+ method and #request_method to return the overridden method in the
+ case of methodoverride being used (this means that #method returns
+ "HEAD" and #request_method returns "GET" in HEAD requests). This
+ is for compatibility with Rack::Request *YK*
+
+* #concat is now deprecated in favor of using <%= %> helpers *YK*
+
+* Block helpers now return Strings, so you can use <%= form_for @foo do |f| %>.
+ <% form_for do |f| %> still works with deprecation notices *YK*
+
+* Add a new #mount method on the router that does not anchor the PATH_INFO
+ at the end *YK & CL*
+
+* Create a new LookupContext object that is responsible for performantly
+ finding a template for a given pattern *JV*
+
+* Removed relative_url_for in favor of respecting SCRIPT_NAME *YK & CL*
+
+* Changed file streaming to use Rack::Sendfile middleware *YK*
+
+* ActionDispatch::Request#content_type returns a String to be compatible with
+ Rack::Request. Use #content_mime_type for the Mime::Type instance *YK*
+
+* Updated Prototype to 1.6.1 and Scriptaculous to 1.8.3 *ML*
+
+* Change the preferred way that URL helpers are included into a class*YK & CL*
+
+ # for all helpers including named routes
+ include Rails.application.router.url_helpers
+
+ # for just url_for
+ include Rails.application.router.url_for
+
+* Fixed that PrototypeHelper#update_page should return html_safe *DHH*
+
+* Fixed that much of DateHelper wouldn't return html_safe? strings *DHH*
+
+* Fixed that fragment caching should return a cache hit as html_safe (or it would all just get escaped) *DHH*
+
+* Added that ActionController::Base now does helper :all instead of relying on the default ApplicationController in Rails to do it *DHH*
+
+* Added ActionDispatch::Request#authorization to access the http authentication header regardless of its proxy hiding *DHH*
+
+* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection *DHH*. Examples:
+
+ flash[:notice] = 'Post was created'
+ redirect_to(@post)
+
+ ...becomes:
+
+ redirect_to(@post, :notice => 'Post was created')
+
+* Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= *DHH*
+
+* Introduce grouped_collection_select helper. #1249 *Dan Codeape, Erik Ostrom*
+
+* Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 *Matthew Rudy Jacobs*
+
+* Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 *Sava Chankov*
+
+* 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*
+
+
+## 2.3.2 Final (March 15, 2009) ##
+
+* Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") *DHH*
+
+* Don't check authenticity tokens for any AJAX requests *Ross Kaffenberger/Bryan Helmkamp*
+
+* Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 *Gregg Pollack*
+
+* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 *Eloy Duran/Nate Wiger*
+
+* Form option helpers now support disabled option tags and the use of lambdas for selecting/disabling option tags from collections #837 *Tekin*
+
+* Added partial scoping to TranslationHelper#translate, so if you call translate(".foo") from the people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo") *DHH*
+
+* Fix a syntax error in current_page?() that was prevent matches against URL's with multiple query parameters #1385, #1868 *chris finne/Andrew White*
+
+* Added localized rescue template when I18n.locale is set (ex: public/404.da.html) #1835 *José Valim*
+
+* Make the form_for and fields_for helpers support the new Active Record nested update options. #1202 *Eloy Duran*
+
+ <% form_for @person do |person_form| %>
+ ...
+ <% person_form.fields_for :projects do |project_fields| %>
+ <% if project_fields.object.active? %>
+ Name: <%= project_fields.text_field :name %>
+ <% end %>
+ <% end %>
+ <% end %>
+
+
+* Added grouped_options_for_select helper method for wrapping option tags in optgroups. #977 *Jon Crawford*
+
+* Implement HTTP Digest authentication. #1230 *Gregg Kellogg, Pratik Naik* Example :
+
+ class DummyDigestController < ActionController::Base
+ USERS = { "lifo" => 'world' }
+
+ before_filter :authenticate
+
+ def index
+ render :text => "Hello Secret"
+ end
+
+ private
+
+ def authenticate
+ authenticate_or_request_with_http_digest("Super Secret") do |username|
+ # Return the user's password
+ USERS[username]
+ end
+ end
+ end
+
+* Improved i18n support for the number_to_human_size helper. Changes the storage_units translation data; update your translations accordingly. #1634 *Yaroslav Markin*
+ storage_units:
+ # %u is the storage unit, %n is the number (default: 2 MB)
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+
+* Added :silence option to BenchmarkHelper#benchmark and turned log_level into a hash parameter and deprecated the old use *DHH*
+
+* Fixed the AssetTagHelper cache to use the computed asset host as part of the cache key instead of just assuming the its a string #1299 *DHH*
+
+* Make ActionController#render(string) work as a shortcut for render :file/:template/:action => string. #1435 *Pratik Naik* Examples:
+
+ \# Instead of render(:action => 'other_action')
+ render('other_action') # argument has no '/'
+ render(:other_action)
+
+ \# Instead of render(:template => 'controller/action')
+ render('controller/action') # argument must not begin with a '/', but contain a '/'
+
+ \# Instead of render(:file => '/Users/lifo/home.html.erb')
+ render('/Users/lifo/home.html.erb') # argument must begin with a '/'
+
+* Add :prompt option to date/time select helpers. #561 *Sam Oliver*
+
+* Fixed that send_file shouldn't set an etag #1578 *Hongli Lai*
+
+* Allow users to opt out of the spoofing checks in Request#remote_ip. Useful for sites whose traffic regularly triggers false positives. *Darren Boyd*
+
+* Deprecated formatted_polymorphic_url. *Jeremy Kemper*
+
+* Added the option to declare an asset_host as an object that responds to call (see http://github.com/dhh/asset-hosting-with-minimum-ssl for an example) *David Heinemeier Hansson*
+
+* Added support for multiple routes.rb files (useful for plugin engines). This also means that draw will no longer clear the route set, you have to do that by hand (shouldn't make a difference to you unless you're doing some funky stuff) *David Heinemeier Hansson*
+
+* Dropped formatted_* routes in favor of just passing in :format as an option. This cuts resource routes generation in half #1359 *aaronbatalion*
+
+* Remove support for old double-encoded cookies from the cookie store. These values haven't been generated since before 2.1.0, and any users who have visited the app in the intervening 6 months will have had their cookie upgraded. *Michael Koziarski*
+
+* Allow helpers directory to be overridden via ActionController::Base.helpers_dir #1424 *Sam Pohlenz*
+
+* Remove deprecated ActionController::Base#assign_default_content_type_and_charset
+
+* Changed the default of ActionView#render to assume partials instead of files when not given an options hash *DHH*. Examples:
+
+ # Instead of <%= render :partial => "account" %>
+ <%= render "account" %>
+
+ # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
+ <%= render "account", :account => @buyer %>
+
+ # @account is an Account instance, so it uses the RecordIdentifier to replace
+ # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
+ <%= render(@account) %>
+
+ # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # <%= render :partial => "posts/post", :collection => @posts %>
+ <%= render(@posts) %>
+
+* Remove deprecated render_component. Please use the plugin from http://github.com/rails/render_component/tree/master *Pratik Naik*
+
+* Fixed RedCloth and BlueCloth shouldn't preload. Instead just assume that they're available if you want to use textilize and markdown and let autoload require them *David Heinemeier Hansson*
+
+
+## 2.2.2 (November 21st, 2008) ##
+
+* I18n: translate number_to_human_size. Add storage_units: [Bytes, KB, MB, GB, TB] to your translations. #1448 *Yaroslav Markin*
+
+* Restore backwards compatible functionality for setting relative_url_root. Include deprecation
+
+* Switched the CSRF module to use the request content type to decide if the request is forgeable. #1145 *Jeff Cohen*
+
+* Added :only and :except to map.resources to let people cut down on the number of redundant routes in an application. Typically only useful for huge routesets. #1215 *Tom Stuart*
+
+ map.resources :products, :only => :show do |product|
+ product.resources :images, :except => :destroy
+ end
+
+* Added render :js for people who want to render inline JavaScript replies without using RJS *David Heinemeier Hansson*
+
+* Fixed that polymorphic_url should compact given array #1317 *hiroshi*
+
+* Fixed the sanitize helper to avoid double escaping already properly escaped entities #683 *antonmos/Ryan McGeary*
+
+* Fixed that FormTagHelper generated illegal html if name contained square brackets #1238 *Vladimir Dobriakov*
+
+* Fix regression bug that made date_select and datetime_select raise a Null Pointer Exception when a nil date/datetime was passed and only month and year were displayed #1289 *Bernardo Padua/Tor Erik*
+
+* Simplified the logging format for parameters (don't include controller, action, and format as duplicates) *David Heinemeier Hansson*
+
+* Remove the logging of the Session ID when the session store is CookieStore *David Heinemeier Hansson*
+
+* Fixed regex in redirect_to to fully support URI schemes #1247 *Seth Fitzsimmons*
+
+* Fixed bug with asset timestamping when using relative_url_root #1265 *Joe Goldwasser*
+
+
+## 2.2.0 RC1 (October 24th, 2008) ##
+
+* Fix incorrect closing CDATA delimiter and that HTML::Node.parse would blow up on unclosed CDATA sections *packagethief*
+
+* Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends *DHH*. Example:
+
+ class ArticlesController < ApplicationController
+ def show_with_respond_to_block
+ @article = Article.find(params[:id])
+
+
+ # If the request sends headers that differs from the options provided to stale?, then
+ # the request is indeed stale and the respond_to block is triggered (and the options
+ # to the stale? call is set on the response).
+ #
+ # If the request headers match, then the request is fresh and the respond_to block is
+ # not triggered. Instead the default render will occur, which will check the last-modified
+ # and etag headers and conclude that it only needs to send a "304 Not Modified" instead
+ # of rendering the template.
+ if stale?(:last_modified => @article.published_at.utc, :etag => @article)
+ respond_to do |wants|
+ # normal response processing
+ end
+ end
+ end
+
+ def show_with_implied_render
+ @article = Article.find(params[:id])
+
+ # Sets the response headers and checks them against the request, if the request is stale
+ # (i.e. no match of either etag or last-modified), then the default render of the template happens.
+ # If the request is fresh, then the default render will return a "304 Not Modified"
+ # instead of rendering the template.
+ fresh_when(:last_modified => @article.published_at.utc, :etag => @article)
+ end
+ end
+
+
+* Added inline builder yield to atom_feed_helper tags where appropriate *Sam Ruby*. Example:
+
+ entry.summary :type => 'xhtml' do |xhtml|
+ xhtml.p pluralize(order.line_items.count, "line item")
+ xhtml.p "Shipped to #{order.address}"
+ xhtml.p "Paid by #{order.pay_type}"
+ end
+
+* Make PrototypeHelper#submit_to_remote a wrapper around PrototypeHelper#button_to_remote. *Tarmo Tänav*
+
+* Set HttpOnly for the cookie session store's cookie. #1046
+
+* Added FormTagHelper#image_submit_tag confirm option #784 *Alastair Brunton*
+
+* Fixed FormTagHelper#submit_tag with :disable_with option wouldn't submit the button's value when was clicked #633 *Jose Fernandez*
+
+* Stopped logging template compiles as it only clogs up the log *David Heinemeier Hansson*
+
+* Changed the X-Runtime header to report in milliseconds *David Heinemeier Hansson*
+
+* Changed BenchmarkHelper#benchmark to report in milliseconds *David Heinemeier Hansson*
+
+* Changed logging format to be millisecond based and skip misleading stats *DHH*. Went from:
+
+ Completed in 0.10000 (4 reqs/sec) | Rendering: 0.04000 (40%) | DB: 0.00400 (4%) | 200 OK [http://example.com]
+
+ ...to:
+
+ Completed in 100ms (View: 40, DB: 4) | 200 OK [http://example.com]
+
+* Add support for shallow nesting of routes. #838 *S. Brent Faulkner*
+
+ Example :
+
+ map.resources :users, :shallow => true do |user|
+ user.resources :posts
+ end
+
+ - GET /users/1/posts (maps to PostsController#index action as usual)
+ named route "user_posts" is added as usual.
+
+ - GET /posts/2 (maps to PostsController#show action as if it were not nested)
+ Additionally, named route "post" is added too.
+
+* Added button_to_remote helper. #3641 *Donald Piret, Tarmo Tänav*
+
+* Deprecate render_component. Please use render_component plugin from http://github.com/rails/render_component/tree/master *Pratik Naik*
+
+* Routes may be restricted to lists of HTTP methods instead of a single method or :any. #407 *Brennan Dunn, Gaius Centus Novus*
+ map.resource :posts, :collection => { :search => [:get, :post] }
+ map.session 'session', :requirements => { :method => [:get, :post, :delete] }
+
+* Deprecated implicit local assignments when rendering partials *Josh Peek*
+
+* Introduce current_cycle helper method to return the current value without bumping the cycle. #417 *Ken Collins*
+
+* Allow polymorphic_url helper to take url options. #880 *Tarmo Tänav*
+
+* Switched integration test runner to use Rack processor instead of CGI *Josh Peek*
+
+* Made AbstractRequest.if_modified_sense return nil if the header could not be parsed *Jamis Buck*
+
+* Added back ActionController::Base.allow_concurrency flag *Josh Peek*
+
+* AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root *Josh Peek*
+
+* Update Prototype to 1.6.0.2 #599 *Patrick Joyce*
+
+* Conditional GET utility methods. *Jeremy Kemper*
+ response.last_modified = @post.updated_at
+ response.etag = [:admin, @post, current_user]
+
+ if request.fresh?(response)
+ head :not_modified
+ else
+ # render ...
+ end
+
+* All 2xx requests are considered successful *Josh Peek*
+
+* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run *David Heinemeier Hansson*
+
+* Removed config.action_view.cache_template_loading, use config.cache_classes instead *Josh Peek*
+
+* Get buffer for fragment cache from template's @output_buffer *Josh Peek*
+
+* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached *Josh Peek*
+
+* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class *Josh Peek*
+
+* Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {}) *Josh Peek*
+
+* Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent with link_to_remote (submit_to_remote still works as an alias) #8994 *clemens*
+
+* Add :recursive option to javascript_include_tag and stylesheet_link_tag to be used along with :all. #480 *Damian Janowski*
+
+* Allow users to disable the use of the Accept header *Michael Koziarski*
+
+ The accept header is poorly implemented by browsers and causes strange
+ errors when used on public sites where crawlers make requests too. You can use formatted urls (e.g. /people/1.xml) to support API clients in a much simpler way.
+ To disable the header you need to set:
+ config.action_controller.use_accept_header = false
+* Do not stat template files in production mode before rendering. You will no longer be able to modify templates in production mode without restarting the server *Josh Peek*
+
+* Deprecated TemplateHandler line offset *Josh Peek*
+
+* Allow caches_action to accept cache store options. #416. *José Valim*. Example:
+
+ caches_action :index, :redirected, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
+
+* Remove define_javascript_functions, javascript_include_tag and friends are far superior. *Michael Koziarski*
+
+* Deprecate :use_full_path render option. The supplying the option no longer has an effect *Josh Peek*
+
+* Add :as option to render a collection of partials with a custom local variable name. #509 *Simon Jefford, Pratik Naik*
+
+ render :partial => 'other_people', :collection => @people, :as => :person
+
+ This will let you access objects of @people as 'person' local variable inside 'other_people' partial template.
+
+* time_zone_select: support for regexp matching of priority zones. Resolves #195 *Ernie Miller*
+
+* Made ActionView::Base#render_file private *Josh Peek*
+
+* Refactor and simplify the implementation of assert_redirected_to. Arguments are now normalised relative to the controller being tested, not the root of the application. *Michael Koziarski*
+
+ This could cause some erroneous test failures if you were redirecting between controllers
+ in different namespaces and wrote your assertions relative to the root of the application.
+
+* Remove follow_redirect from controller functional tests.
+
+ If you want to follow redirects you can use integration tests. The functional test version was only useful if you were using redirect_to :id=>...
+* Fix polymorphic_url with singleton resources. #461 *Tammer Saleh*
+
+* Replaced TemplateFinder abstraction with ViewLoadPaths *Josh Peek*
+
+* Added block-call style to link_to *Sam Stephenson/David Heinemeier Hansson*. Example:
+
+ <% link_to(@profile) do %>
+ <strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
+ <% end %>
+
+* Performance: integration test benchmarking and profiling. *Jeremy Kemper*
+
+* Make caching more aware of mime types. Ensure request format is not considered while expiring cache. *Jonathan del Strother*
+
+* Drop ActionController::Base.allow_concurrency flag *Josh Peek*
+
+* More efficient concat and capture helpers. Remove ActionView::Base.erb_variable. *Jeremy Kemper*
+
+* Added page.reload functionality. Resolves #277. *Sean Huber*
+
+* Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR doesn't match (not just if they're both present) *Mark Imbriaco, Bradford Folkens*
+
+* Allow caches_action to accept a layout option *José Valim*
+
+* Added Rack processor *Ezra Zygmuntowicz, Josh Peek*
+
+
+## 2.1.0 (May 31st, 2008) ##
+
+* InstanceTag#default_time_from_options overflows to DateTime *Geoff Buesing*
+
+* Fixed that forgery protection can be used without session tracking (Peter Jones) *#139*
+
+* Added session(:on) to turn session management back on in a controller subclass if the superclass turned it off (Peter Jones) *#136*
+
+* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" *Rick Olson*
+* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap *Geoff Buesing*
+
+* select_date defaults to Time.zone.today when config.time_zone is set *Geoff Buesing*
+
+* Fixed that TextHelper#text_field would corrypt when raw HTML was used as the value (mchenryc, Kevin Glowacz) *#80*
+
+* Added ActionController::TestCase#rescue_action_in_public! to control whether the action under test should use the regular rescue_action path instead of simply raising the exception inline (great for error testing) *David Heinemeier Hansson*
+
+* Reduce number of instance variables being copied from controller to view. *Pratik Naik*
+
+* select_datetime and select_time default to Time.zone.now when config.time_zone is set *Geoff Buesing*
+
+* datetime_select defaults to Time.zone.now when config.time_zone is set *Geoff Buesing*
+
+* Remove ActionController::Base#view_controller_internals flag. *Pratik Naik*
+
+* Add conditional options to caches_page method. *Paul Horsfall*
+
+* Move missing template logic to ActionView. *Pratik Naik*
+
+* Introduce ActionView::InlineTemplate class. *Pratik Naik*
+
+* Automatically parse posted JSON content for Mime::JSON requests. *Rick Olson*
+
+ POST /posts
+ {"post": {"title": "Breaking News"}}
+
+ def create
+ @post = Post.create params[:post]
+ # ...
+ end
+
+* add json_escape ERB util to escape html entities in json strings that are output in HTML pages. *Rick Olson*
+
+* Provide a helper proxy to access helper methods from outside views. Closes #10839 *Josh Peek*
+ e.g. ApplicationController.helpers.simple_format(text)
+
+* Improve documentation. *Xavier Noria, leethal, jerome*
+
+* Ensure RJS redirect_to doesn't html-escapes string argument. Closes #8546 *Josh Peek, eventualbuddha, Pratik Naik*
+
+* Support render :partial => collection of heterogeneous elements. #11491 *Zach Dennis*
+
+* Avoid remote_ip spoofing. *Brian Candler*
+
+* Added support for regexp flags like ignoring case in the :requirements part of routes declarations #11421 *NeilW*
+
+* Fixed that ActionController::Base#read_multipart would fail if boundary was exactly 10240 bytes #10886 *ariejan*
+
+* Fixed HTML::Tokenizer (used in sanitize helper) didn't handle unclosed CDATA tags #10071 *esad, packagethief*
+
+* Improve documentation. *Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert*
+
+* Fixed that FormHelper#radio_button would produce invalid ids #11298 *harlancrystal*
+
+* Added :confirm option to submit_tag #11415 *Emilio Tagua*
+
+* Fixed NumberHelper#number_with_precision to properly round in a way that works equally on Mac, Windows, Linux (closes #11409, #8275, #10090, #8027) *zhangyuanyi*
+
+* Allow the #simple_format text_helper to take an html_options hash for each paragraph. #2448 *François Beausoleil, Chris O'Sullivan*
+
+* Fix regression from filter refactoring where re-adding a skipped filter resulted in it being called twice. *Rick Olson*
+
+* Refactor filters to use Active Support callbacks. #11235 *Josh Peek*
+
+* Fixed that polymorphic routes would modify the input array #11363 *thomas.lee*
+
+* Added :format option to NumberHelper#number_to_currency to enable better localization support #11149 *lylo*
+
+* Fixed that TextHelper#excerpt would include one character too many #11268 *Irfy*
+
+* Fix more obscure nested parameter hash parsing bug. #10797 *thomas.lee*
+
+* Added ActionView::Helpers::register_javascript/stylesheet_expansion to make it easier for plugin developers to inject multiple assets. #10350 *lotswholetime*
+
+* Fix nested parameter hash parsing bug. #10797 *thomas.lee*
+
+* Allow using named routes in ActionController::TestCase before any request has been made. Closes #11273 *Eloy Duran*
+
+* Fixed that sweepers defined by cache_sweeper will be added regardless of the perform_caching setting. Instead, control whether the sweeper should be run with the perform_caching setting. This makes testing easier when you want to turn perform_caching on/off *David Heinemeier Hansson*
+
+* Make MimeResponds::Responder#any work without explicit types. Closes #11140 *jaw6*
+
+* Better error message for type conflicts when parsing params. Closes #7962 *spicycode, matt*
+
+* Remove unused ActionController::Base.template_class. Closes #10787 *Pratik Naik*
+
+* Moved template handlers related code from ActionView::Base to ActionView::Template. *Pratik Naik*
+
+* Tests for div_for and content_tag_for helpers. Closes #11223 *Chris O'Sullivan*
+
+* Allow file uploads in Integration Tests. Closes #11091 *RubyRedRick*
+
+* Refactor partial rendering into a PartialTemplate class. *Pratik Naik*
+
+* Added that requests with JavaScript as the priority mime type in the accept header and no format extension in the parameters will be treated as though their format was :js when it comes to determining which template to render. This makes it possible for JS requests to automatically render action.js.rjs files without an explicit respond_to block *David Heinemeier Hansson*
+
+* Tests for distance_of_time_in_words with TimeWithZone instances. Closes #10914 *Ernesto Jimenez*
+
+* Remove support for multivalued (e.g., '&'-delimited) cookies. *Jamis Buck*
+
+* Fix problem with render :partial collections, records, and locals. #11057 *lotswholetime*
+
+* Added support for naming concrete classes in sweeper declarations *David Heinemeier Hansson*
+
+* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application. #10098 *Tim Pope, Chris Kampmeier*
+
+* Fix typo in form_helper documentation. #10650 *Xavier Shay, Chris Kampmeier*
+
+* Fix bug with setting Request#format= after the getter has cached the value. #10889 *cch1*
+
+* Correct inconsistencies in RequestForgeryProtection docs. #11032 *Mislav Marohnić*
+
+* Introduce a Template class to ActionView. #11024 *Pratik Naik*
+
+* Introduce the :index option for form_for and fields_for to simplify multi-model forms (see http://railscasts.com/episodes/75). #9883 *rmm5t*
+
+* Introduce map.resources :cards, :as => 'tarjetas' to use a custom resource name in the URL: cards_path == '/tarjetas'. #10578 *blj*
+
+* TestSession supports indifferent access. #7372 *tamc, Arsen7, mhackett, julik, jean.helou*
+
+* Make assert_routing aware of the HTTP method used. #8039 *mpalmer*
+ e.g. assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
+
+* Make map.root accept a single symbol as an argument to declare an alias. #10818 *bscofield*
+
+ e.g. map.dashboard '/dashboard', :controller=>'dashboard'
+ map.root :dashboard
+
+* Handle corner case with image_tag when passed 'messed up' image names. #9018 *Duncan Beevers, mpalmer*
+
+* Add label_tag helper for generating elements. #10802 *DefV*
+
+* Introduce TemplateFinder to handle view paths and lookups. #10800 *Pratik Naik*
+
+* Performance: optimize route recognition. Large speedup for apps with many resource routes. #10835 *oleganza*
+
+* Make render :partial recognise form builders and use the _form partial. #10814 *Damian Janowski*
+
+* Allow users to declare other namespaces when using the atom feed helpers. #10304 *david.calavera*
+
+* Introduce send_file :x_sendfile => true to send an X-Sendfile response header. *Jeremy Kemper*
+
+* Fixed ActionView::Helpers::ActiveRecordHelper::form for when protect_from_forgery is used #10739 *Jeremy Evans*
+
+* Provide nicer access to HTTP Headers. Instead of request.env["HTTP_REFERRER"] you can now use request.headers["Referrer"]. *Michael Koziarski*
+
+* UrlWriter respects relative_url_root. #10748 *Cheah Chu Yeow*
+
+* The asset_host block takes the controller request as an optional second argument. Example: use a single asset host for SSL requests. #10549 *Cheah Chu Yeow, Peter B, Tom Taylor*
+
+* Support render :text => nil. #6684 *tjennings, PotatoSalad, Cheah Chu Yeow*
+
+* assert_response failures include the exception message. #10688 *Seth Rasmussen*
+
+* All fragment cache keys are now by default prefixed with the "views/" namespace *David Heinemeier Hansson*
+
+* Moved the caching stores from ActionController::Caching::Fragments::* to ActiveSupport::Cache::*. If you're explicitly referring to a store, like ActionController::Caching::Fragments::MemoryStore, you need to update that reference with ActiveSupport::Cache::MemoryStore *David Heinemeier Hansson*
+
+* Deprecated ActionController::Base.fragment_cache_store for ActionController::Base.cache_store *David Heinemeier Hansson*
+
+* Made fragment caching in views work for rjs and builder as well #6642 *Dee Zsombor*
+
+* Fixed rendering of partials with layout when done from site layout #9209 *antramm*
+
+* Fix atom_feed_helper to comply with the atom spec. Closes #10672 *Xavier Shay*
+
+ * The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
+ * IDs are not guaranteed unique
+ * A default self link was not provided, contrary to the documentation
+ * NOTE: This changes tags for existing atom entries, but at least they validate now.
+
+* Correct indentation in tests. Closes #10671 *Luca Guidi*
+
+* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 *Brad Greenlee*
+
+* Ensure that test case setup is run even if overridden. #10382 *Josh Peek*
+
+* Fix HTML Sanitizer to allow trailing spaces in CSS style attributes. Closes #10566 *wesley.moxam*
+
+* Add :default option to time_zone_select. #10590 *Matt Aimonetti*
+
+
+## 2.0.2 (December 16th, 2007) ##
+
+* Added delete_via_redirect and put_via_redirect to integration testing #10497 *philodespotos*
+
+* Allow headers['Accept'] to be set by hand when calling xml_http_request #10461 *BMorearty*
+
+* Added OPTIONS to list of default accepted HTTP methods #10449 *holoway*
+
+* Added option to pass proc to ActionController::Base.asset_host for maximum configurability #10521 *Cheah Chu Yeow*. Example:
+
+ ActionController::Base.asset_host = Proc.new { |source|
+ if source.starts_with?('/images')
+ "http://images.example.com"
+ else
+ "http://assets.example.com"
+ end
+ }
+
+* Fixed that ActionView#file_exists? would be incorrect if @first_render is set #10569 *dbussink*
+
+* Added that Array#to_param calls to_param on all it's elements #10473 *brandon*
+
+* Ensure asset cache directories are automatically created. #10337 *Josh Peek, Cheah Chu Yeow*
+
+* render :xml and :json preserve custom content types. #10388 *jmettraux, Cheah Chu Yeow*
+
+* Refactor Action View template handlers. #10437, #10455 *Josh Peek*
+
+* Fix DoubleRenderError message and leave out mention of returning false from filters. Closes #10380 *Frederick Cheung*
+
+* Clean up some cruft around ActionController::Base#head. Closes #10417 *ssoroka*
+
+
+## 2.0.1 (December 7th, 2007) ##
+
+* Fixed send_file/binary_content for testing #8044 *tolsen*
+
+* When a NonInferrableControllerError is raised, make the proposed fix clearer in the error message. Closes #10199 *Jack Danger Canty*
+
+* Update Prototype to 1.6.0.1. *sam*
+
+* Update script.aculo.us to 1.8.0.1. *madrobby*
+
+* Add 'disabled' attribute to <OPTION> separators used in time zone and country selects. Closes #10354 *Josh Susser*
+
+* Added the same record identification guessing rules to fields_for as form_for has *David Heinemeier Hansson*
+
+* Fixed that verification violations with no specified action didn't halt the chain (now they do with a 400 Bad Request) *David Heinemeier Hansson*
+
+* Raise UnknownHttpMethod exception for unknown HTTP methods. Closes #10303 *Tarmo Tänav*
+
+* Update to Prototype -r8232. *sam*
+
+* Make sure the optimisation code for routes doesn't get used if :host, :anchor or :port are provided in the hash arguments. *pager, Michael Koziarski* #10292
+
+* Added protection from trailing slashes on page caching #10229 *devrieda*
+
+* Asset timestamps are appended, not prepended. Closes #10276 *Mike Naberezny*
+
+* Minor inconsistency in description of render example. Closes #10029 *ScottSchram*
+
+* Add #prepend_view_path and #append_view_path instance methods on ActionController::Base for consistency with the class methods. *Rick Olson*
+
+* Refactor sanitizer helpers into HTML classes and make it easy to swap them out with custom implementations. Closes #10129. *Rick Olson*
+
+* Add deprecation for old subtemplate syntax for ActionMailer templates, use render :partial *Rick Olson*
+
+* Fix TemplateError so it doesn't bomb on exceptions while running tests *Rick Olson*
+
+* Fixed that named routes living under resources shouldn't have double slashes #10198 *Isaac Feliu*
+
+* Make sure that cookie sessions use a secret that is at least 30 chars in length. *Michael Koziarski*
+
+* Fixed that partial rendering should look at the type of the first render to determine its own type if no other clues are available (like when using text.plain.erb as the extension in AM) #10130 *java*
+
+* Fixed that has_many :through associations should render as collections too #9051 *mathie/Jack Danger Canty*
+
+* Added :mouseover short-cut to AssetTagHelper#image_tag for doing easy image swaps #6893 *joost*
+
+* Fixed handling of non-domain hosts #9479 *purp*
+
+* Fix syntax error in documentation example for cycle method. Closes #8735 *foca*
+
+* Document :with option for link_to_remote. Closes #8765 *Ryan Bates*
+
+* Document :minute_step option for time_select. Closes #8814 *brupm*
+
+* Explain how to use the :href option for link_to_remote to degrade gracefully in the absence of JavaScript. Closes #8911 *vlad*
+
+* Disambiguate :size option for text area tag. Closes #8955 *redbeard*
+
+* Fix broken tag in assert_tag documentation. Closes #9037 *mfazekas*
+
+* Add documentation for route conditions. Closes #9041 *innu, Manfred Stienstra*
+
+* Fix typo left over from previous typo fix in url helper. Closes #9414 *Henrik N*
+
+* Fixed that ActionController::CgiRequest#host_with_port() should handle standard port #10082 *moro*
+
+* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. *sam, madrobby*
+
+* Expose the cookie jar as a helper method (before the view would just get the raw cookie hash) *David Heinemeier Hansson*
+
+* Integration tests: get_ and post_via_redirect take a headers hash. #9130 *simonjefford*
+
+* Simplfy #view_paths implementation. ActionView templates get the exact object, not a dup. *Rick Olson*
+
+* Update tests for ActiveSupport's JSON escaping change. *Rick Olson*
+
+* FormHelper's auto_index should use #to_param instead of #id_before_type_cast. Closes #9994 *mattly*
+
+* Doc typo fixes for ActiveRecordHelper. Closes #9973 *mikong*
+
+* Make example parameters in restful routing docs idiomatic. Closes #9993 *Jack Danger Canty*
+
+* Make documentation comment for mime responders match documentation example. Closes #9357 *yon*
+
+* Introduce a new test case class for functional tests. ActionController::TestCase. *Michael Koziarski*
+
+* Fix incorrect path in helper rdoc. Closes #9926 *viktor tron*
+
+* Partials also set 'object' to the default partial variable. #8823 *Nick Retallack, Jeremy Kemper*
+
+* Request profiler. *Jeremy Kemper*
+ $ cat login_session.rb
+ get_with_redirect '/'
+ say "GET / => #{path}"
+ post_with_redirect '/sessions', :username => 'john', :password => 'doe'
+ say "POST /sessions => #{path}"
+ $ ./script/performance/request -n 10 login_session.rb
+
+* Disabled checkboxes don't submit a form value. #9301 *vladr, robinjfisher*
+
+* Added tests for options to ActiveRecordHelper#form. Closes #7213 *richcollins, mikong, Mislav Marohnić*
+
+* Changed before_filter halting to happen automatically on render or redirect but no longer on simply returning false *David Heinemeier Hansson*
+
+* Ensure that cookies handle array values correctly. Closes #9937 *queso*
+
+* Make sure resource routes don't clash with internal helpers like javascript_path, image_path etc. #9928 *Geoff Buesing*
+
+* caches_page uses a single after_filter instead of one per action. #9891 *Pratik Naik*
+
+* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0. *sam, madrobby*
+
+* Dispatcher: fix that to_prepare should only run once in production. #9889 *Nathaniel Talbott*
+
+* Memcached sessions: add session data on initialization; don't silently discard exceptions; add unit tests. #9823 *kamk*
+
+* error_messages_for also takes :message and :header_message options which defaults to the old "There were problems with the following fields:" and "<count> errors prohibited this <object_name> from being saved". #8270 *rmm5t, zach-inglis-lt3*
+
+* Make sure that custom inflections are picked up by map.resources. #9815 *Mislav Marohnić*
+
+* Changed SanitizeHelper#sanitize to only allow the custom attributes and tags when specified in the call *David Heinemeier Hansson*
+
+* Extracted sanitization methods from TextHelper to SanitizeHelper *David Heinemeier Hansson*
+
+* rescue_from accepts :with => lambda { |exception| ... } or a normal block. #9827 *Pratik Naik*
+
+* Add :status to redirect_to allowing users to choose their own response code without manually setting headers. #8297 *Coda Hale, chasgrundy*
+
+* Add link_to :back which uses your referrer with a fallback to a javascript link. #7366 *eventualbuddha, Tarmo Tänav*
+
+* error_messages_for and friends also work with local variables. #9699 *Frederick Cheung*
+
+* Fix url_for, redirect_to, etc. with :controller => :symbol instead of 'string'. #8562, #9525 *Justin Lynn, Tarmo Tänav, shoe*
+
+* Use #require_library_or_gem to load the memcache library for the MemCache session and fragment cache stores. Closes #8662. *Rick Olson*
+
+* Move ActionController::Routing.optimise_named_routes to ActionController::Base.optimise_named_routes. Now you can set it in the config. *Rick Olson*
+
+ config.action_controller.optimise_named_routes = false
+
+* ActionController::Routing::DynamicSegment#interpolation_chunk should call #to_s on all values before calling URI.escape. *Rick Olson*
+
+* Only accept session ids from cookies, prevents session fixation attacks. *bradediger*
+
+
+## 2.0.0 Preview Release (September 29th, 2007) Includes duplicates of changes from 1.12.2 - 1.13.3 ##
+
+* Fixed that render template did not honor exempt_from_layout #9698 *pezra*
+
+* Better error messages if you leave out the :secret option for request forgery protection. Closes #9670 *Rick Olson*
+
+* Allow ability to disable request forgery protection, disable it in test mode by default. Closes #9693 *Pratik Naik*
+
+* Avoid calling is_missing on LoadErrors. Closes #7460. *ntalbott*
+
+* Move Railties' Dispatcher to ActionController::Dispatcher, introduce before_ and after_dispatch callbacks, and warm up to non-CGI requests. *Jeremy Kemper*
+
+* The tag helper may bypass escaping. *Jeremy Kemper*
+
+* Cache asset ids. *Jeremy Kemper*
+
+* Optimized named routes respect AbstractRequest.relative_url_root. #9612 *Daniel Morrison, Jeremy Kemper*
+
+* Introduce ActionController::Base.rescue_from to declare exception-handling methods. Cleaner style than the case-heavy rescue_action_in_public. #9449 *Norbert Crombach*
+
+* Rename some RequestForgeryProtection methods. The class method is now #protect_from_forgery, and the default parameter is now 'authenticity_token'. *Rick Olson*
+
+* Merge csrf_killer plugin into rails. Adds RequestForgeryProtection model that verifies session-specific _tokens for non-GET requests. *Rick Olson*
+
+* Secure #sanitize, #strip_tags, and #strip_links helpers against xss attacks. Closes #8877. *Rick Olson, Pratik Naik, Jacques Distler*
+
+ This merges and renames the popular white_list helper (along with some css sanitizing from Jacques Distler version of the same plugin).
+ Also applied updated versions of #strip_tags and #strip_links from #8877.
+
+* Remove use of & logic operator. Closes #8114. *watson*
+
+* Fixed JavaScriptHelper#escape_javascript to also escape closing tags #8023 *Ruy Asan*
+
+* Fixed TextHelper#word_wrap for multiline strings with extra carrier returns #8663 *seth*
+
+* Fixed that setting the :host option in url_for would automatically turn off :only_path (since :host would otherwise not be shown) #9586 *Bounga*
+
+* Added FormHelper#label. #8641, #9850 *jcoglan, Jarkko Laine*
+
+* Added AtomFeedHelper (slightly improved from the atom_feed_helper plugin) *David Heinemeier Hansson*
+
+* Prevent errors when generating routes for uncountable resources, (i.e. sheep where plural == singluar). map.resources :sheep now creates sheep_index_url for the collection and sheep_url for the specific item. *Michael Koziarski*
+
+* Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks #8895 *Pratik Naik, Mark Somerville*
+
+* Don't warn when a path segment precedes a required segment. Closes #9615. *Nicholas Seckar*
+
+* Fixed CaptureHelper#content_for to work with the optional content parameter instead of just the block #9434 *sandofsky/wildchild*.
+
+* Added Mime::Type.register_alias for dealing with different formats using the same mime type *DHH*. Example:
+
+ class PostsController < ApplicationController
+ before_filter :adjust_format_for_iphone
+
+ def index
+ @posts = Post.find(:all)
+
+ respond_to do |format|
+ format.html # => renders index.html.erb and uses "text/html" as the content type
+ format.iphone # => renders index.iphone.erb and uses "text/html" as the content type
+ end
+ end
+
+
+ private
+ def adjust_format_for_iphone
+ if request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/iPhone/]
+ request.format = :iphone
+ end
+ end
+ end
+
+* Added that render :json will automatically call .to_json unless it's being passed a string *DHH*.
+
+* Autolink behaves well with emails embedded in URLs. #7313 *Jeremy McAnally, Tarmo Tänav*
+
+* Fixed that default layouts did not take the format into account #9564 *Pratik Naik*
+
+* Fixed optimized route segment escaping. #9562 *wildchild, Jeremy Kemper*
+
+* Added block acceptance to JavaScriptHelper#javascript_tag. #7527 *Bob Silva, Tarmo Tänav, rmm5t*
+
+* root_path returns '/' not ''. #9563 *Pratik Naik*
+
+* Fixed that setting request.format should also affect respond_to blocks *David Heinemeier Hansson*
+
+* Add option to force binary mode on tempfile used for fixture_file_upload. #6380 *Jonathan Viney*
+
+* Fixed that resource namespaces wouldn't stick to all nested resources #9399 *pixeltrix*
+
+* Moved ActionController::Macros::AutoComplete into the auto_complete plugin on the official Rails svn. #9512 *Pratik Naik*
+
+* Moved ActionController::Macros::InPlaceEditing into the in_place_editor plugin on the official Rails svn. #9513 *Pratik Naik*
+
+* Removed deprecated form of calling xml_http_request/xhr without the first argument being the http verb *David Heinemeier Hansson*
+
+* Removed deprecated methods *DHH*:
+
+ - ActionController::Base#keep_flash (use flash.keep instead)
+ - ActionController::Base#expire_matched_fragments (just call expire_fragment with a regular expression)
+ - ActionController::Base.template_root/= methods (use ActionController#Base.view_paths/= instead)
+ - ActionController::Base.cookie (use ActionController#Base.cookies[]= instead)
+
+* Removed the deprecated behavior of appending ".png" to image_tag/image_path calls without an existing extension *David Heinemeier Hansson*
+
+* Removed ActionController::Base.scaffold -- it went through the whole idea of scaffolding (card board walls you remove and tweak one by one). Use the scaffold generator instead (it does resources too now!) *David Heinemeier Hansson*
+
+* Optimise named route generation when using positional arguments. *Michael Koziarski*
+
+ This change delivers significant performance benefits for the most
+ common usage scenarios for modern rails applications by avoiding the
+ costly trip through url_for. Initial benchmarks indicate this is
+ between 6 and 20 times as fast.
+
+* Explicitly require active_record/query_cache before using it. *Jeremy Kemper*
+
+* Fix layout overriding response status. #9476 *lotswholetime*
+
+* Add field_set_tag for generating field_sets, closes #9477. *Damian Janowski*
+
+* Allow additional parameters to be passed to named route helpers when using positional arguments. Closes #8930 *Ian White*
+
+* Make render :partial work with a :collection of Hashes, previously this wasn't possible due to backwards compatibility restrictions. *Pratik Naik*
+
+* request.host works with IPv6 addresses. #9458 *yuya*
+
+* Fix bug where action caching sets the content type to the ActionCachePath object. Closes #9282 *mindforge*
+
+* Find layouts even if they're not in the first view_paths directory. Closes #9258 *caio*
+
+* Major improvement to the documentation for the options / select form helpers. Closes #9038 *Chris Kampmeier, jardeon, wesg*
+
+* Fix number_to_human_size when using different precisions. Closes #7536. *RichardStrand, mpalmer*
+
+* Added partial layouts (see example in action_view/lib/partials.rb) *David Heinemeier Hansson*
+
+* Allow you to set custom :conditions on resource routes. *Rick Olson*
+
+* Fixed that file.content_type for uploaded files would include a trailing \r #9053 *Brad Greenlee*
+
+* url_for now accepts a series of symbols representing the namespace of the record *Josh Knowles*
+
+* Make :trailing_slash work with query parameters for url_for. Closes #4004 *nov*
+
+* Make sure missing template exceptions actually say which template they were looking for. Closes #8683 *dasil003*
+
+* Fix errors with around_filters which do not yield, restore 1.1 behaviour with after filters. Closes #8891 *Stefan Kaes*
+
+ After filters will *no longer* be run if an around_filter fails to yield, users relying on
+ this behaviour are advised to put the code in question after a yield statement in an around filter.
+
+
+* Allow you to delete cookies with options. Closes #3685 *Josh Peek, Chris Wanstrath*
+
+* Allow you to render views with periods in the name. Closes #8076 *Norbert Crombach*
+
+ render :partial => 'show.html.erb'
+
+* Improve capture helper documentation. #8796 *Chris Kampmeier*
+
+* Prefix nested resource named routes with their action name, e.g. new_group_user_path(@group) instead of group_new_user_path(@group). The old nested action named route is deprecated in Rails 1.2.4. #8558 *David Chelimsky*
+
+* Allow sweepers to be created solely for expiring after controller actions, not model changes *David Heinemeier Hansson*
+
+* Added assigns method to ActionController::Caching::Sweeper to easily access instance variables on the controller *David Heinemeier Hansson*
+
+* Give the legacy X-POST_DATA_FORMAT header greater precedence during params parsing for backward compatibility. *Jeremy Kemper*
+
+* Fixed that link_to with an href of # when using :method will not allow for click-through without JavaScript #7037 *Steven Bristol, Josh Peek*
+
+* Fixed that radio_button_tag should generate unique ids #3353 *Bob Silva, Rebecca, Josh Peek*
+
+* Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well #6754 *Mislav Marohnić*
+
+* Don't mistakenly interpret the request uri as the query string. #8731 *Pratik Naik, Jeremy Kemper*
+
+* Make ActionView#view_paths an attr_accessor for real this time. Also, don't perform an unnecessary #compact on the @view_paths array in #initialize. Closes #8582 *dasil003, julik, Rick Olson*
+
+* Tolerate missing content type on multipart file uploads. Fix for Safari 3. *Jeremy Kemper*
+
+* Deprecation: remove pagination. Install the classic_pagination plugin for forward compatibility, or move to the superior will_paginate plugin. #8157 *Josh Peek*
+
+* Action caching is limited to GET requests returning 200 OK status. #3335 *tom@craz8.com, halfbyte, Dan Kubb, Josh Peek*
+
+* Improve Text Helper test coverage. #7274 *Rob Sanheim, Josh Peek*
+
+* Improve helper test coverage. #7208, #7212, #7215, #7233, #7234, #7235, #7236, #7237, #7238, #7241, #7243, #7244 *Rich Collins, Josh Peek*
+
+* Improve UrlRewriter tests. #7207 *Rich Collins*
+
+* Resources: url_for([parent, child]) generates /parents/1/children/2 for the nested resource. Likewise with the other simply helpful methods like form_for and link_to. #6432 *mhw, Jonathan Vaught, lotswholetime*
+
+* Assume html format when rendering partials in RJS. #8076 *Rick Olson*
+
+* Don't double-escape url_for in views. #8144 *Rich Collins, Josh Peek*
+
+* Allow JSON-style values for the :with option of observe_field. Closes #8557 *kommen*
+
+* Remove RAILS_ROOT from backtrace paths. #8540 *Tim Pope*
+
+* Routing: map.resource :logo routes to LogosController so the controller may be reused for multiple nestings or namespaces. *Jeremy Kemper*
+
+* render :partial recognizes Active Record associations as Arrays. #8538 *Kamal Fariz Mahyuddin*
+
+* Routing: drop semicolon and comma as route separators. *Jeremy Kemper*
+
+* request.remote_ip understands X-Forwarded-For addresses with nonstandard whitespace. #7386 *moses*
+
+* Don't prepare response when rendering a component. #8493 *jsierles*
+
+* Reduce file stat calls when checking for template changes. #7736 *alex*
+
+* Added custom path cache_page/expire_page parameters in addition to the options hashes *DHH*. Example:
+
+ def index
+ caches_page(response.body, "/index.html")
+ end
+
+* Action Caching speedup. #8231 *Stefan Kaes*
+
+* Wordsmith resources documentation. #8484 *marclove*
+
+* Fix syntax error in code example for routing documentation. #8377. *Norbert Crombach*
+
+* Routing: respond with 405 Method Not Allowed status when the route path matches but the HTTP method does not. #6953 *Josh Peek, defeated, Dan Kubb, Coda Hale*
+
+* Add support for assert_select_rjs with :show and :hide. #7780 *dchelimsky*
+
+* Make assert_select's failure messages clearer about what failed. #7779 *dchelimsky*
+
+* Introduce a default respond_to block for custom types. #8174 *Josh Peek*
+
+* auto_complete_field takes a :method option so you can GET or POST. #8120 *zapnap*
+
+* Added option to suppress :size when using :maxlength for FormTagHelper#text_field #3112 *Tim Pope*
+
+* catch possible WSOD when trying to render a missing partial. Closes #8454 *Jonathan del Strother*
+
+* Rewind request body after reading it, if possible. #8438 *s450r1*
+
+* Resource namespaces are inherited by their has_many subresources. #8280 *marclove, Geoff Garside*
+
+* Fix filtered parameter logging with nil parameter values. #8422 *choonkeat*
+
+* Integration tests: alias xhr to xml_http_request and add a request_method argument instead of always using POST. #7124 *Nik Wakelin, François Beausoleil, Wizard*
+
+* Document caches_action. #5419 *Jarkko Laine*
+
+* Update to Prototype 1.5.1. *Sam Stephenson*
+
+* Allow routes to be decalred under namespaces *Tobias Lütke*:
+
+ map.namespace :admin do |admin|
+ admin.root :controller => "products"
+ admin.feed 'feed.xml', :controller => 'products', :action => 'feed', :format => 'xml'
+ end
+
+* Update to script.aculo.us 1.7.1_beta3. *Thomas Fuchs*
+
+* observe_form always sends the serialized form. #5271 *Manfred Stienstra, normelton@gmail.com*
+
+* Parse url-encoded and multipart requests ourselves instead of delegating to CGI. *Jeremy Kemper*
+
+* select :include_blank option can be set to a string instead of true, which just uses an empty string. #7664 *Wizard*
+
+* Added url_for usage on render :location, which allows for record identification *DHH*. Example:
+
+ render :xml => person, :status => :created, :location => person
+
+ ...expands the location to person_url(person).
+
+* Introduce the request.body stream. Lazy-read to parse parameters rather than always setting RAW_POST_DATA. Reduces the memory footprint of large binary PUT requests. *Jeremy Kemper*
+
+* Add some performance enhancements to ActionView.
+
+ * Cache base_paths in @@cached_base_paths
+ * Cache template extensions in @@cached_template_extension
+ * Remove unnecessary rescues
+
+* Assume that rendered partials go by the HTML format by default
+
+ def my_partial
+ render :update do |page|
+ # in this order
+ # _foo.html.erb
+ # _foo.erb
+ # _foo.rhtml
+ page.replace :foo, :partial => 'foo'
+ end
+ end
+
+* Added record identifications to FormHelper#form_for and PrototypeHelper#remote_form_for *DHH*. Examples:
+
+ <% form_for(@post) do |f| %>
+ ...
+ <% end %>
+
+ This will expand to be the same as:
+
+ <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
+ ...
+ <% end %>
+
+ And for new records:
+
+ <% form_for(Post.new) do |f| %>
+ ...
+ <% end %>
+
+ This will expand to be the same as:
+
+ <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+ ...
+ <% end %>
+
+* Rationalize route path escaping according to RFC 2396 section 3.3. #7544, #8307. *Jeremy Kemper, Chris Roos, begemot, jugend*
+
+* Added record identification with polymorphic routes for ActionController::Base#url_for and ActionView::Base#url_for *DHH*. Examples:
+
+ redirect_to(post) # => redirect_to(posts_url(post)) => Location: http://example.com/posts/1
+ link_to(post.title, post) # => link_to(post.title, posts_url(post)) => <a href="/posts/1">Hello world</a>
+
+ Any method that calls url_for on its parameters will automatically benefit from this.
+
+* Removed deprecated parameters_for_method_reference concept (legacy from before named routes) *David Heinemeier Hansson*
+
+* Add ActionController::Routing::Helpers, a module to contain common URL helpers such as polymorphic_url. *Nicholas Seckar*
+
+* Included the HttpAuthentication plugin as part of core (ActionController::HttpAuthentication::Basic) *David Heinemeier Hansson*
+
+* Modernize documentation for form helpers. *Jeremy McAnally*
+
+* Add brief introduction to REST to the resources documentation. *fearoffish*
+
+* Fix various documentation typos throughout ActionPack. *Henrik N*
+
+* Enhance documentation and add examples for url_for. *Jeremy McAnally*
+
+* Fix documentation typo in routes. *Norbert Crombach, pam*
+
+* Sweep flash when filter chain is halted. *Caio Chassot <lists@v2studio.com>*
+
+* Fixed that content_tag with a block will just return the result instead of concate it if not used in a ERb view #7857, #7432 *michael.niessner*
+
+* Replace the current block/continuation filter chain handling by an implementation based on a simple loop. #8226 *Stefan Kaes*
+
+* Update UrlWriter to accept :anchor parameter. Closes #6771. *Chris McGrath*
+
+* Added RecordTagHelper for using RecordIdentifier conventions on divs and other container elements *DHH*. Example:
+
+ <% div_for(post) do %> <div id="post_45" class="post">
+ <%= post.body %> What a wonderful world!
+ <% end %> </div>
+
+* Added page[record] accessor to JavaScriptGenerator that relies on RecordIdentifier to find the right dom id *DHH*. Example:
+
+ format.js do
+ # Calls: new Effect.fade('post_45');
+ render(:update) { |page| page[post].visual_effect(:fade) }
+ end
+
+* Added RecordIdentifier to enforce view conventions on records for dom ids, classes, and partial paths *David Heinemeier Hansson*
+
+* Added map.namespace to deal with the common situation of admin sections and the like *David Heinemeier Hansson*
+
+ Before:
+
+ map.resources :products, :path_prefix => "admin", :controller => "admin/products", :collection => { :inventory => :get }, :member => { :duplicate => :post }
+ map.resources :tags, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_tags"
+ map.resources :images, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_images"
+ map.resources :variants, :name_prefix => 'admin_product_', :path_prefix => "admin/products/:product_id", :controller => "admin/product_variants"
+
+ After:
+
+ map.namespace(:admin) do |admin|
+ admin.resources :products,
+ :collection => { :inventory => :get },
+ :member => { :duplicate => :post },
+ :has_many => [ :tags, :images, :variants ]
+ end
+
+* Added :name_prefix as standard for nested resources *DHH*. WARNING: May be backwards incompatible with your app
+
+ Before:
+
+ map.resources :emails do |emails|
+ emails.resources :comments, :name_prefix => "email_"
+ emails.resources :attachments, :name_prefix => "email_"
+ end
+
+ After:
+
+ map.resources :emails do |emails|
+ emails.resources :comments
+ emails.resources :attachments
+ end
+
+ This does mean that if you intended to have comments_url go to /emails/5/comments, then you'll have to set :name_prefix to nil explicitly.
+
+* Added :has_many and :has_one for declaring plural and singular resources beneath the current *David Heinemeier Hansson*
+
+ Before:
+
+ map.resources :notes do |notes|
+ notes.resources :comments
+ notes.resources :attachments
+ notes.resource :author
+ end
+
+ After:
+
+ map.resources :notes, :has_many => [ :comments, :attachments ], :has_one => :author
+
+* Added that render :xml will try to call to_xml if it can *DHH*. Makes these work:
+
+ render :xml => post
+ render :xml => comments
+
+* Added :location option to render so that the common pattern of rendering a response after creating a new resource is now a 1-liner *David Heinemeier Hansson*
+
+ render :xml => post.to_xml, :status => :created, :location => post_url(post)
+
+* Ensure that render_text only adds string content to the body of the response *David Heinemeier Hansson*
+
+* Return the string representation from an Xml Builder when rendering a partial. Closes #5044 *Tim Pope*
+
+* Fixed that parameters from XML should also be presented in a hash with indifferent access *David Heinemeier Hansson*
+
+* Tweak template format rules so that the ACCEPT header is only used if it's text/javascript. This is so ajax actions without a :format param get recognized as Mime::JS. *Rick Olson*
+
+* The default respond_to blocks don't set a specific extension anymore, so that both 'show.rjs' and 'show.js.rjs' will work. *Rick Olson*
+
+* Allow layouts with extension of .html.erb. Closes #8032 *Josh Knowles*
+
+* Change default respond_to templates for xml and rjs formats. *Rick Olson*
+
+ * Default xml template goes from #{action_name}.rxml => #{action_name}.xml.builder.
+ * Default rjs template goes from #{action_name}.rjs => #{action_name}.js.rjs.
+
+ You can still specify your old templates:
+
+ respond_to do |format|
+ format.xml do
+ render :action => "#{action_name}.rxml"
+ end
+ end
+
+* Fix WSOD due to modification of a formatted template extension so that requests to templates like 'foo.html.erb' fail on the second hit. *Rick Olson*
+
+* Fix WSOD when template compilation fails *Rick Olson*
+
+* Change ActionView template defaults. Look for templates using the request format first, such as "show.html.erb" or "show.xml.builder", before looking for the old defaults like "show.erb" or "show.builder" *Rick Olson*
+
+* Highlight helper highlights one or many terms in a single pass. *Jeremy Kemper*
+
+* Dropped the use of ; as a separator of non-crud actions on resources and went back to the vanilla slash. It was a neat idea, but lots of the non-crud actions turned out not to be RPC (as the ; was primarily intended to discourage), but legitimate sub-resources, like /parties/recent, which didn't deserve the uglification of /parties;recent. Further more, the semicolon caused issues with caching and HTTP authentication in Safari. Just Not Worth It *David Heinemeier Hansson*
+
+* Added that FormTagHelper#submit_tag will return to its original state if the submit fails and you're using :disable_with *David Heinemeier Hansson*
+
+* Cleaned up, corrected, and mildly expanded ActionPack documentation. Closes #7190 *Jeremy McAnally*
+
+* Small collection of ActionController documentation cleanups. Closes #7319 *Jeremy McAnally*
+
+* Make sure the route expiry hash is constructed by comparing the to_param-ized values of each hash. *Jamis Buck*
+
+* Allow configuration of the default action cache path for #caches_action calls. *Rick Olson*
+
+ class ListsController < ApplicationController
+ caches_action :index, :cache_path => Proc.new { |controller|
+ controller.params[:user_id] ?
+ controller.send(:user_lists_url, c.params[:user_id]) :
+ controller.send(:lists_url) }
+ end
+
+* Performance: patch cgi/session/pstore to require digest/md5 once rather than per #initialize. #7583 *Stefan Kaes*
+
+* Cookie session store: ensure that new sessions doesn't reuse data from a deleted session in the same request. *Jeremy Kemper*
+
+* Deprecation: verification with :redirect_to => :named_route shouldn't be deprecated. #7525 *Justin French*
+
+* Cookie session store: raise ArgumentError when :session_key is blank. *Jeremy Kemper*
+
+* Deprecation: remove deprecated request, redirect, and dependency methods. Remove deprecated instance variables. Remove deprecated url_for(:symbol, *args) and redirect_to(:symbol, *args) in favor of named routes. Remove uses_component_template_root for toplevel components directory. Privatize deprecated render_partial and render_partial_collection view methods. Remove deprecated link_to_image, link_image_to, update_element_function, start_form_tag, and end_form_tag helper methods. Remove deprecated human_size helper alias. *Jeremy Kemper*
+
+* Consistent public/protected/private visibility for chained methods. #7813 *Dan Manges*
+
+* Prefer MIME constants to strings. #7707 *Dan Kubb*
+
+* Allow array and hash query parameters. Array route parameters are converted/to/a/path as before. #6765, #7047, #7462 *bgipsy, Jeremy McAnally, Dan Kubb, brendan*
+
+ \# Add a #dbman attr_reader for CGI::Session and make CGI::Session::CookieStore#generate_digest public so it's easy to generate digests using the cookie store's secret. *Rick Olson*
+* Added Request#url that returns the complete URL used for the request *David Heinemeier Hansson*
+
+* Extract dynamic scaffolding into a plugin. #7700 *Josh Peek*
+
+* Added user/password options for url_for to add http authentication in a URL *David Heinemeier Hansson*
+
+* Fixed that FormTagHelper#text_area_tag should disregard :size option if it's not a string *Brendon Davidson*
+
+* Set the original button value in an attribute of the button when using the :disable_with key with submit_tag, so that the original can be restored later. *Jamis Buck*
+
+* session_enabled? works with session :off. #6680 *Jonathan del Strother*
+
+* Added :port and :host handling to UrlRewriter (which unified url_for usage, regardless of whether it's called in view or controller) #7616 *alancfrancis*
+
+* Allow send_file/send_data to use a registered mime type as the :type parameter #7620 *jonathan*
+
+* Allow routing requirements on map.resource(s) #7633 *quixoten*. Example:
+
+ map.resources :network_interfaces, :requirements => { :id => /^\d+\.\d+\.\d+\.\d+$/ }
+
+* Cookie session store: empty and unchanged sessions don't write a cookie. *Jeremy Kemper*
+
+* Added helper(:all) as a way to include all helpers from app/helpers/**/*.rb in ApplicationController *David Heinemeier Hansson*
+
+* Integration tests: introduce methods for other HTTP methods. #6353 *caboose*
+
+* Routing: better support for escaped values in route segments. #7544 [Chris
+ Roos]
+* Introduce a cookie-based session store as the Rails default. Sessions typically contain at most a user_id and flash message; both fit within the 4K cookie size limit. A secure message digest is included with the cookie to ensure data integrity (a user cannot alter his user_id without knowing the secret key included in the digest). If you have more than 4K of session data or don't want your data to be visible to the user, pick another session store. Cookie-based sessions are dramatically faster than the alternatives. *Jeremy Kemper*
+
+ Example config/environment.rb:
+ # Use an application-wide secret key and the default SHA1 message digest.
+ config.action_controller.session = { :secret => "can't touch this" }
+
+ # Store a secret key per user and employ a stronger message digest.
+ config.action_controller.session = {
+ :digest => 'SHA512',
+ :secret => Proc.new { User.current.secret_key }
+ }
+
+* Added .erb and .builder as preferred aliases to the now deprecated .rhtml and .rxml extensions *Chad Fowler*. This is done to separate the renderer from the mime type. .erb templates are often used to render emails, atom, csv, whatever. So labeling them .rhtml doesn't make too much sense. The same goes for .rxml, which can be used to build everything from HTML to Atom to whatever. .rhtml and .rxml will continue to work until Rails 3.0, though. So this is a slow phasing out. All generators and examples will start using the new aliases, though.
+
+* Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag *DHH*. Examples:
+
+ stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
+ <link href="/stylesheets/style1.css" media="screen" rel="Stylesheet" type="text/css" />
+ <link href="/stylesheets/styleB.css" media="screen" rel="Stylesheet" type="text/css" />
+ <link href="/stylesheets/styleX2.css" media="screen" rel="Stylesheet" type="text/css" />
+
+ stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is true =>
+ <link href="/stylesheets/all.css" media="screen" rel="Stylesheet" type="text/css" />
+
+ ...when caching is on, all.css is the concatenation of style1.css, styleB.css, and styleX2.css.
+ Same deal for JavaScripts.
+
+* Work around the two connection per host browser limit: use asset%d.myapp.com to distribute asset requests among asset[0123].myapp.com. Use a DNS wildcard or CNAMEs to map these hosts to your asset server. See http://www.die.net/musings/page_load_time/ for background. *Jeremy Kemper*
+
+* Added default mime type for CSS (Mime::CSS) *David Heinemeier Hansson*
+
+* Added that rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified and the response body will be set to an empty string. *David Heinemeier Hansson*
+
+* Added X-Runtime to all responses with the request run time *David Heinemeier Hansson*
+
+* Add Mime::Type convenience methods to check the current mime type. *Rick Olson*
+
+ request.format.html? # => true if Mime::HTML
+ request.format.jpg? # => true if Mime::JPG
+
+ \# ActionController sample usage:
+ \# the session will be disabled for non html/ajax requests
+ session :off, :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
+
+* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id. *Stefan Kaes*
+
+* Add a :url_based_filename => true option to ActionController::Streaming::send_file, which allows URL-based filenames. *Thomas Fuchs*
+
+* Fix that FormTagHelper#submit_tag using :disable_with should trigger the onsubmit handler of its form if available *David Heinemeier Hansson*
+
+* Fix #render_file so that TemplateError is called with the correct params and you don't get the WSOD. *Rick Olson*
+
+* Fix issue with deprecation messing up #template_root= usage. Add #prepend_view_path and #append_view_path to allow modification of a copy of the
+ superclass' view_paths. *Rick Olson*
+* Allow Controllers to have multiple view_paths instead of a single template_root. Closes #2754 *John Long*
+
+* Add much-needed html-scanner tests. Fixed CDATA parsing bug. *Rick Olson*
+
+* improve error message for Routing for named routes. Closes #7346 *Rob Sanheim*
+
+* Added enhanced docs to routing assertions. Closes #7359 *Rob Sanheim*
+
+* fix form_for example in ActionController::Resources documentation. Closes #7362 *gnarg*
+
+* Make sure that the string returned by TextHelper#truncate is actually a string, not a char proxy -- that should only be used internally while working on a multibyte-safe way of truncating *David Heinemeier Hansson*
+
+* Added FormBuilder#submit as a delegate for FormTagHelper#submit_tag *David Heinemeier Hansson*
+
+* Allow Routes to generate all urls for a set of options by specifying :generate_all => true. Allows caching to properly set or expire all paths for a resource. References #1739. *Nicholas Seckar*
+
+* Change the query parser to map empty GET params to "" rather than nil. Closes #5694. *Nicholas Seckar*
+
+* date_select and datetime_select take a :default option. #7052 *Nik Wakelin*
+ date_select "post", "written_on", :default => 3.days.from_now
+ date_select "credit_card", "bill_due", :default => { :day => 20 }
+
+* select :multiple => true suffixes the attribute name with [] unless already suffixed. #6977 *nik.kakelin, ben, julik*
+
+* Improve routes documentation. #7095 *zackchandler*
+
+* mail_to :encode => 'hex' also encodes the mailto: part of the href attribute as well as the linked email when no name is given. #2061 *Jarkko Laine, pfc.pille@gmx.net*
+
+* Resource member routes require :id, eliminating the ambiguous overlap with collection routes. #7229 *dkubb*
+
+* Remove deprecated assertions. *Jeremy Kemper*
+
+* Change session restoration to allow namespaced models to be autoloaded. Closes #6348. *Nicholas Seckar*
+
+* Fix doubly appearing parameters due to string and symbol mixups. Closes #2551. *Anthony Eden*
+
+* Fix overly greedy rescues when loading helpers. Fixes #6268. *Nicholas Seckar*
+
+* Fixed NumberHelper#number_with_delimiter to use "." always for splitting the original number, not the delimiter parameter #7389 *ceefour*
+
+* Autolinking recognizes trailing and embedded . , : ; #7354 *Jarkko Laine*
+
+* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268. *imajes*
+
+* Update to script.aculo.us 1.7.0. *Thomas Fuchs*
+
+* Modernize cookie testing code, and increase coverage (Heckle++) #7101 *Kevin Clark*
+
+* Improve Test Coverage for ActionController::Routing::Route#matches_controller_and_action? (Heckle++) #7115 *Kevin Clark*
+
+* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 *Kevin Clark*
+
+* Fix #distance_of_time_in_words to report accurately against the Duration class. #7114 *eventualbuddha*
+
+* Refactor #form_tag to allow easy extending. *Rick Olson*
+
+* Update to Prototype 1.5.0. *Sam Stephenson*
+
+* RecordInvalid, RecordNotSaved => 422 Unprocessable Entity, StaleObjectError => 409 Conflict. #7097 *dkubb*
+
+* Allow fields_for to be nested inside form_for, so that the name and id get properly constructed *Jamis Buck*
+
+* Allow inGroupsOf and eachSlice to be called through rjs. #7046 *Cody Fauser*
+
+* Allow exempt_from_layout :rhtml. #6742, #7026 *Dan Manges, Squeegy*
+
+* Recognize the .txt extension as Mime::TEXT *Rick Olson*
+
+* Fix parsing of array[] CGI parameters so extra empty values aren't included. #6252 *Nicholas Seckar, aiwilliams, brentrowland*
+
+* link_to_unless_current works with full URLs as well as paths. #6891 *Jarkko Laine, Manfred Stienstra, idrifter*
+
+* Lookup the mime type for #auto_discovery_link_tag in the Mime::Type class. Closes #6941 *Josh Peek*
+
+* Fix bug where nested resources ignore a parent singleton parent's path prefix. Closes #6940 *Dan Kubb*
+
+* Fix no method error with error_messages_on. Closes #6935 *nik.wakelin Koz*
+
+* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs. Closes #6922 *Steven Bristol*
+
+* Slight doc tweak to #prepend_filter. Closes #6493 *Jeremy Voorhis*
+
+* Add more extensive documentation to the AssetTagHelper. Closes #6452 *Bob Silva*
+
+* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper. Closes #6394 *Bob Silva*
+
+* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 *Jan Prill*
+
+* Make sure html_document is reset between integration test requests. *ctm*
+
+* Set session to an empty hash if :new_session => false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError. *Josh Susser*
+
+* Routing uses URI escaping for path components and CGI escaping for query parameters. *darix, Jeremy Kemper*
+
+* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting. Closes #6128. *Rick Olson*
+
+* Singleton resources: POST /singleton => create, GET /singleton/new => new. *Jeremy Kemper*
+
+* Use 400 Bad Request status for unrescued ActiveRecord::RecordInvalid exceptions. *Jeremy Kemper*
+
+* Silence log_error deprecation warnings from inspecting deprecated instance variables. *Nate Wiger*
+
+* Only cache GET requests with a 200 OK response. #6514, #6743 *RSL, anamba*
+
+* Add a 'referer' attribute to TestRequest. *Jamis Buck*
+
+* Ensure render :json => ... skips the layout. Closes #6808 *Josh Peek*
+
+* Fix HTML::Node to output double quotes instead of single quotes. Closes #6845 *mitreandy*
+
+* Correctly report which filter halted the chain. #6699 *Martin Emde*
+
+* Fix a bug in Routing where a parameter taken from the path of the current request could not be used as a query parameter for the next. Closes #6752. *Nicholas Seckar*
+
+* Unrescued ActiveRecord::RecordNotFound responds with 404 instead of 500. *Jeremy Kemper*
+
+* Improved auto_link to match more valid urls correctly *Tobias Lütke*
+
+* Add singleton resources. *Rick Olson*
+
+ map.resource :account
+
+ GET /account
+ GET /account;edit
+ UPDATE /account
+ DELETE /account
+
+* respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument. #4185 *Scott Raymond, eventualbuddha*
+ # application/json response with body 'Element.show({:name: "David"})'
+ respond_to do |format|
+ format.json { render :json => { :name => "David" }.to_json, :callback => 'Element.show' }
+ end
+
+* Makes :discard_year work without breaking multi-attribute parsing in AR. #1260, #3800 *sean@ardismg.com, jmartin@desertflood.com, stephen@touset.org, Bob Silva*
+
+* Adds html id attribute to date helper elements. #1050, #1382 *mortonda@dgrmm.net, David North, Bob Silva*
+
+* Add :index and @auto_index capability to model driven date/time selects. #847, #2655 *moriq, Doug Fales, Bob Silva*
+
+* Add :order to datetime_select, select_datetime, and select_date. #1427 *Timothee Peignier, Patrick Lenz, Bob Silva*
+
+* Added time_select to work with time values in models. Update scaffolding. #2489, #2833 *Justin Palmer, Andre Caum, Bob Silva*
+
+* Added :include_seconds to select_datetime, datetime_select and time_select. #2998 *csn, Bob Silva*
+
+* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization. #363 *tomasj, Bob Silva*
+
+* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC. #3811 *Bob Silva*
+
+* Added map.root as an alias for map.connect '' *David Heinemeier Hansson*
+
+* Added Request#format to return the format used for the request as a mime type. If no format is specified, the first Request#accepts type is used. This means you can stop using respond_to for anything else than responses *DHH*. Examples:
+
+ GET /posts/5.xml | request.format => Mime::XML
+ GET /posts/5.xhtml | request.format => Mime::HTML
+ GET /posts/5 | request.format => request.accepts.first (usually Mime::HTML for browsers)
+
+* Added the option for extension aliases to mime type registration *DHH*. Example (already in the default routes):
+
+ Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
+
+ ...will respond on both .html and .xhtml.
+
+* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status. *Jeremy Kemper*
+
+* Added CSV to Mime::SET so that respond_to csv will work *Cody Fauser*
+
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) *David Heinemeier Hansson*
+
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 *David Heinemeier Hansson*
+
+* Update Routing to complain when :controller is not specified by a route. Closes #6669. *Nicholas Seckar*
+
+* Ensure render_to_string cleans up after itself when an exception is raised. #6658 *Rob Sanheim*
+
+* Extract template_changed_since? from compile_template? so plugins may override its behavior for non-file-based templates. #6651 *Jeff Barczewski*
+
+* Update to Prototype and script.aculo.us [5579]. *Thomas Fuchs*
+
+* simple_format helper doesn't choke on nil. #6644 *jerry426*
+
+* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again *Thomas Fuchs*
+
+* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. *Nicholas Seckar*
+
+* ActionView::Base.erb_variable accessor names the buffer variable used to render templates. Defaults to _erbout; use _buf for erubis. *Rick Olson*
+
+* assert_select_rjs :remove. *Dylan Egan*
+
+* Always clear model associations from session. #4795 *sd@notso.net, andylien@gmail.com*
+
+* Update to Prototype 1.5.0_rc2. *Sam Stephenson*
+
+* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. *Sam Stephenson*
+
+* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes. #6586 *dkubb*
+
+* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename. #6401 *Andreas Schwarz, Jeremy Kemper*
+
+* assert_response supports symbolic status codes. #6569 *Kevin Clark*
+ assert_response :ok
+ assert_response :not_found
+ assert_response :forbidden
+
+* Cache parsed query parameters. #6559 *Stefan Kaes*
+
+* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS *Thomas Fuchs*
+
+* pluralize helper interprets nil as zero. #6474 *Tim Pope*
+
+* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing. #6524 *Bob Silva*
+
+* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods. *Jeremy Kemper*
+
+* Deprecate expire_matched_fragments. Use expire_fragment instead. #6535 *Bob Silva*
+
+* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions *Thomas Fuchs*
+
+* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. *Rick Olson*
+
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag *Rick Olson*
+
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. *Rick Olson*
+
+* Added block-usage to TagHelper#content_tag *DHH*. Example:
+
+ <% content_tag :div, :class => "strong" %>
+ Hello world!
+ <% end %>
+
+ Will output:
+ <div class="strong">Hello world!</div>
+
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 *Bob Silva*
+
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 *Bob Silva*
+
+* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 *Bob Silva/chrismear*
+
+* Force *_url named routes to show the host in ActionView *Rick Olson*
+
+ <%= url_for ... %> # no host
+ <%= foo_path %> # no host
+ <%= foo_url %> # host!
+
+* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. *Sam Stephenson*
+
+* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. *Sam Stephenson*
+
+* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. *Rick Olson*
+
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. *Rick Olson*
+
+* Fix deprecation warnings when rendering the template error template. *Nicholas Seckar*
+
+* Fix routing to correctly determine when generation fails. Closes #6300. *psross*.
+
+* Fix broken assert_generates when extra keys are being checked. *Jamis Buck*
+
+* Replace KCODE checks with String#chars for truncate. Closes #6385 *Manfred Stienstra*
+
+* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'. *Marcel Molina Jr.*
+
+* Use String#chars in TextHelper::excerpt. Closes #6386 *Manfred Stienstra*
+
+* Install named routes into ActionView::Base instead of proxying them to the view via helper_method. Closes #5932. *Nicholas Seckar*
+
+* Update to latest Prototype and script.aculo.us trunk versions *Thomas Fuchs*
+
+* Fix relative URL root matching problems. *Mark Imbriaco*
+
+* Fix filter skipping in controller subclasses. #5949, #6297, #6299 *Martin Emde*
+
+* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API). *Jeremy Kemper*
+
+* Rename test assertion to prevent shadowing. Closes #6306. *psross*
+
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 *Philip Hallstrom*
+
+* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 *evansj*
+
+* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 *tzaharia*. Example:
+
+ update_page_tag :defer => 'true' { |page| ... }
+
+ Gives:
+
+ <script defer="true" type="text/javascript">...</script>
+
+ Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
+
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 *Mike Naberezny*
+
+* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 *sven@c3d2.de*
+
+* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 *cmselmer*
+
+* respond_to :html doesn't assume .rhtml. #6281 *Hampton Catlin*
+
+* Fixed some deprecation warnings in ActionPack *Rick Olson*
+
+* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them. #6240 *japgolly*
+
+* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead. *Jeremy Kemper*
+
+* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. *Jamis Buck*. Examples:
+
+ head :status => 404 # expands to "404 Not Found"
+ head :status => :not_found # expands to "404 Not Found"
+ head :status => :created # expands to "201 Created"
+
+* Add head(options = {}) for responses that have no body. *Jamis Buck*. Examples:
+
+ head :status => 404 # return an empty response with a 404 status
+ head :location => person_path(@person), :status => 201
+
+* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain. *Rick Olson*
+
+* strip_links is case-insensitive. #6285 *tagoh, Bob Silva*
+
+* Clear the cache of possible controllers whenever Routes are reloaded. *Nicholas Seckar*
+
+* Filters overhaul including meantime filter support using around filters + blocks. #5949 *Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper*
+
+* Update RJS render tests. *sam*
+
+* Update CGI process to allow sessions to contain namespaced models. Closes #4638. *dfelstead@site5.com*
+
+* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action => 'index'). Closes #5950. *Nicholas Seckar*
+
+* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability. #3368, #6226 *sdsykes, fhanshaw@vesaria.com*
+
+* Added that respond_to blocks will automatically set the content type to be the same as is requested *DHH*. Examples:
+
+ respond_to do |format|
+ format.html { render :text => "I'm being sent as text/html" }
+ format.rss { render :text => "I'm being sent as application/rss+xml" }
+ format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
+ end
+
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) *David Heinemeier Hansson*
+
+* Added proper getters and setters for content type and charset *DHH*. Example of what we used to do:
+
+ response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
+
+ ...now:
+
+ response.content_type = Mime::ATOM
+ response.charset = "utf-8"
+
+* Updated prototype.js to 1.5.0_rc1 with latest fixes. *Rick Olson*
+
+ - XPATH support
+ - Make Form.getElements() return elements in the correct order
+ - fix broken Form.serialize return
+
+* Declare file extensions exempt from layouts. #6219 *brandon*
+ Example: ActionController::Base.exempt_from_layout 'rpdf'
+
+* Add chained replace/update support for assert_select_rjs *Rick Olson*
+
+ Given RJS like...
+
+ page['test1'].replace "<div id=\"1\">foo</div>"
+ page['test2'].replace_html "<div id=\"2\">foo</div>"
+
+ Test it with...
+
+ assert_select_rjs :chained_replace
+ assert_select_rjs :chained_replace, "test1"
+
+ assert_select_rjs :chained_replace_html
+ assert_select_rjs :chained_replace_html, "test2"
+
+* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency. #6132, #6178 *choonkeat@gmail.com*
+
+* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 *Manfred Stienstra, raphinou@yahoo.com*
+
+* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 *Eric Hodel*
+
+* Add descriptive messages to the exceptions thrown by cgi_methods. #6091, #6103 *Nicholas Seckar, Bob Silva*
+
+* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids, #6068 *petermichaux@gmail.com*
+
+* Update UrlWriter to support :only_path. *Nicholas Seckar, Dave Thomas*
+
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional *DHH*. So what used to require a nil, like this:
+
+ link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
+
+ ...can be written like this:
+
+ link_to("Hider", :class => "hider_link") { |p| p[:something].hide }
+
+* Update to script.aculo.us 1.6.3 *Thomas Fuchs*
+
+* Update to Prototype 1.5.0_rc1 *sam*
+
+* Added access to nested attributes in RJS #4548 *richcollins@gmail.com*. Examples:
+
+ page['foo']['style'] # => $('foo').style;
+ page['foo']['style']['color'] # => $('blank_slate').style.color;
+ page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
+ page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
+
+* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) *eule@space.ch*
+
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension *David Heinemeier Hansson*
+
+* Fixed FormOptionsHelper#select to respect :selected value #5813
+
+* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 *moriq@moriq.com*
+
+* Fixed TextHelper#pluralize to handle 1 as a string #5909 *rails@bencurtis.com*
+
+* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 *Bob Silva*
+
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" *David Heinemeier Hansson*
+
+* Integration tests: thoroughly test ActionController::Integration::Session. #6022 *Kevin Clark*
+ (tests skipped unless you `gem install mocha`)
+
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 *David Heinemeier Hansson*
+
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 *David Heinemeier Hansson*
+
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support *David Heinemeier Hansson*
+
+* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 *assaf.arkin@gmail.com*
+
+* radio_button_tag generates unique id attributes. #3353 *Bob Silva, somekool@gmail.com*
+
+* strip_tags passes through blank args such as nil or "". #2229, #6702 *duncan@whomwah.com, dharana*
+
+* Cleanup assert_tag :children counting. #2181 *jamie@bravenet.com*
+
+* button_to accepts :method so you can PUT and DELETE with it. #6005 *Dan Webb*
+
+* Update sanitize text helper to strip plaintext tags, and <img src="javascript:bang">. *Rick Olson*
+
+* Update routing documentation. Closes #6017 *Nathan Witmer*
+
+* Add routing tests to assert that RoutingError is raised when conditions aren't met. Closes #6016 *Nathan Witmer*
+
+* Deprecation: update docs. #5998 *Jakob Skjerning, Kevin Clark*
+
+* Make auto_link parse a greater subset of valid url formats. *Jamis Buck*
+
+* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. *Mike Clark*
+
+* Tighten rescue clauses. #5985 *james@grayproductions.net*
+
+* Fix send_data documentation typo. #5982 *brad@madriska.com*
+
+* Switch to using FormEncodedPairParser for parsing request parameters. *Nicholas Seckar, David Heinemeier Hansson*
+
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. *Tobias Lütke*
+
+* Deprecation: test deprecated instance vars in partials. *Jeremy Kemper*
+
+* Add UrlWriter to allow writing urls from Mailers and scripts. *Nicholas Seckar*
+
+* Clean up and run the Active Record integration tests by default. #5854 *Kevin Clark, Jeremy Kemper*
+
+* Correct example in cookies docs. #5832 *jessemerriman@warpmail.net*
+
+* Updated to script.aculo.us 1.6.2 *Thomas Fuchs*
+
+* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. *Nicholas Seckar*
+
+* Add controller_paths variable to Routing. *Nicholas Seckar*
+
+* Fix assert_redirected_to issue with named routes for module controllers. *Rick Olson*
+
+* Tweak RoutingError message to show option diffs, not just missing named route significant keys. *Rick Olson*
+
+* Invoke method_missing directly on hidden actions. Closes #3030. *Nicholas Seckar*
+
+* Require Tempfile explicitly for TestUploadedFile due to changes in class auto loading. *Rick Olson*
+
+* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. *Rick Olson*
+
+* Replace Reloadable with Reloadable::Deprecated. *Nicholas Seckar*
+
+* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. *Jeremy Kemper*
+
+* Add support for the param_name parameter to the auto_complete_field helper. #5026 *david.a.williams@gmail.com*
+
+* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. *Jeremy Kemper*
+
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 *François Beausoleil*
+
+* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 *pjhyett@gmail.com*
+
+* Short documentation to mention use of Mime::Type.register. #5710 *choonkeat@gmail.com*
+
+* Make controller_path available as an instance method. #5724 *jmckible@gmail.com*
+
+* Update query parser to support adjacent hashes. *Nicholas Seckar*
+
+* Make action caching aware of different formats for the same action so that, e.g. foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. *Marcel Molina Jr.*
+
+* Restrict Request Method hacking with ?_method to POST requests. *Rick Olson*
+
+* Fix bug when passing multiple options to SimplyRestful, like :new => { :preview => :get, :draft => :get }. *Rick Olson, Josh Susser, Lars Pind*
+
+* Dup the options passed to map.resources so that multiple resources get the same options. *Rick Olson*
+
+* Fixed the new_#{resource}_url route and added named route tests for Simply Restful. *Rick Olson*
+
+* Added map.resources from the Simply Restful plugin *DHH*. Examples (the API has changed to use plurals!):
+
+ map.resources :messages
+ map.resources :messages, :comments
+ map.resources :messages, :new => { :preview => :post }
+
+* Fixed that integration simulation of XHRs should set Accept header as well *Edward Frederick*
+
+* TestRequest#reset_session should restore a TestSession, not a hash *Michael Koziarski*
+
+* Don't search a load-path of '.' for controller files *Jamis Buck*
+
+* Update integration.rb to require test_process explicitly instead of via Dependencies. *Nicholas Seckar*
+
+* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 *lmarlow*
+
+* Allow form_for and fields_for to work with indexed form inputs. *Jeremy Kemper, Matt Lyon*
+
+ <% form_for 'post[]', @post do |f| -%>
+ <% end -%>
+
+* Remove leak in development mode by replacing define_method with module_eval. *Nicholas Seckar*
+
+* Provide support for decimal columns to form helpers. Closes #5672. *Dave Thomas*
+
+* Update documentation for erb trim syntax. #5651 *matt@mattmargolis.net*
+
+* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 *olivier_ansaldi@yahoo.com*
+
+* Reset @html_document between requests so assert_tag works. #4810 *Jarkko Laine, easleydp@gmail.com*
+
+* Update render :partial documentation. #5646 *matt@mattmargolis.net*
+
+* Integration tests behave well with render_component. #4632 *edward.frederick@revolution.com, dev.rubyonrails@maxdunn.com*
+
+* Added exception handling of missing layouts #5373 *chris@ozmm.org*
+
+* Fixed that real files and symlinks should be treated the same when compiling templates #5438 *zachary@panandscan.com*
+
+* Fixed that the flash should be reset when reset_session is called #5584 *Shugo Maeda*
+
+* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 *murpyh@rubychan.de*
+
+* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) *David Heinemeier Hansson*
+
+* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. *Nicholas Seckar*
+
+* Avoid naming collision among compiled view methods. *Jeremy Kemper*
+
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 *Mislav Marohnić*
+
+* Determine the correct template_root for deeply nested components. #2841 *s.brink@web.de*
+
+* Fix that routes with *path segments in the recall can generate URLs. *Rick Olson*
+
+* Fix strip_links so that it doesn't hang on multiline <acronym> tags *Jamis Buck*
+
+* Remove problematic control chars in rescue template. #5316 *Stefan Kaes*
+
+* Make sure passed routing options are not mutated by routing code. #5314 *Blair Zajac*
+
+* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. *Jamis Buck*
+
+* Escape the path before routing recognition. #3671
+
+* Make sure :id and friends are unescaped properly. #5275 *me@julik.nl*
+
+* Fix documentation for with_routing to reflect new reality. #5281 *rramdas@gmail.com*
+
+* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form). #210 *mklame@atxeu.com, matthew@walker.wattle.id.au*
+
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types *David Heinemeier Hansson*
+
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types *DHH*. Example: Mime::Type.register("image/gif", :gif)
+
+* Added support for Mime objects in render :content_type option *DHH*. Example: render :text => some_atom, :content_type => Mime::ATOM
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 *Manfred Stienstra <m.stienstra@fngtps.com>*
+
+* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. *Nicholas Seckar, Jamis Buck*
+
+ map.connect '/foo/:id', :controller => '...', :action => '...'
+ map.connect '/foo/:id.:format', :controller => '...', :action => '...'
+ map.connect '/foo/:id', ..., :conditions => { :method => :get }
+
+* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. *Jeremy Kemper*
+
+* Accept multipart PUT parameters. #5235 *guy.naor@famundo.com*
+
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output *DHH*. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find :all
+
+ respond_to do |format|
+ format.html
+ format.xml { render :xml => @posts.to_xml }
+ format.rss { render :action => "feed.rxml" }
+ end
+ end
+ end
+
+ \# returns HTML when requested by a browser, since the browser
+ \# has the HTML mimetype at the top of its priority list
+ Accept: text/html
+ GET /weblog
+
+ \# returns the XML
+ Accept: application/xml
+ GET /weblog
+
+ \# returns the HTML
+ Accept: application/xml
+ GET /weblog.html
+
+ \# returns the XML
+ Accept: text/html
+ GET /weblog.xml
+
+ All this relies on the fact that you have a route that includes .:format.
+
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post *David Heinemeier Hansson*
+
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete *David Heinemeier Hansson*
+
+* follow_redirect doesn't complain about being redirected to the same controller. #5153 *dymo@mk.ukrtelecom.ua*
+
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. *Kevin Clark*
+
+* Fix NoMethodError when parsing params like &&. *Adam Greenfield*
+
+* Fix flip flopped logic in docs for url_for's :only_path option. Closes #4998. *esad@esse.at*
+
+* form.text_area handles the :size option just like the original text_area (:size => '60x10' becomes cols="60" rows="10"). *Jeremy Kemper*
+
+* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 *anonymous*
+
+* Small fix in routing to allow dynamic routes (broken after [4242]) *Rick Olson*
+
+ map.connect '*path', :controller => 'files', :action => 'show'
+
+* Replace alias method chaining with Module#alias_method_chain. *Marcel Molina Jr.*
+
+* Replace Ruby's deprecated append_features in favor of included. *Marcel Molina Jr.*
+
+* Use #flush between switching from #write to #syswrite. Closes #4907. *Blair Zajac <blair@orcaware.com>*
+
+* Documentation fix: integration test scripts don't require integration_test. Closes #4914. *Frederick Ros <sl33p3r@free.fr>*
+
+* ActionController::Base Summary documentation rewrite. Closes #4900. *Kevin Clark*
+
+* Fix text_helper.rb documentation rendering. Closes #4725. *Frederick Ros*
+
+* Fixes bad rendering of JavaScriptMacrosHelper rdoc (closes #4910) *Frederick Ros*
+
+* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. *andrew@redlinesoftware.com, Marcel Molina Jr.*
+
+ error_messages_for :account, :user, :subscription, :object_name => :account
+
+* Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there. Closes #4079. *Rick Olson*
+
+* Documentation for AbstractRequest. Closes #4895. *Kevin Clark*
+
+* Refactor various InstanceTag instance method to class methods. Closes #4800. *Stefan Kaes*
+
+* Remove all remaining references to @params in the documentation. *Marcel Molina Jr.*
+
+* Add documentation for redirect_to :back's RedirectBackError exception. *Marcel Molina Jr.*
+
+* Update layout and content_for documentation to use yield rather than magic @content_for instance variables. *Marcel Molina Jr.*
+
+* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: *Rick Olson*
+
+ redirect_to :action => 'new'
+ assert_redirected_to :controller => 'monkeys', :action => 'new'
+
+* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. *Rick Olson*
+
+* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method *Rick Olson*
+
+ assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
+ assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
+
+* Diff compared options with #assert_redirected_to *Rick Olson*
+
+* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action *Jamis Buck*
+
+* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. *zraii@comcast.net, Sam Stephenson*
+
+* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for *David Heinemeier Hansson*
+
+* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. *Jamis Buck*
+
+* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. *Sam Stephenson*
+ ex. verify :only => :speak, :method => :post,
+ :render => { :status => 405, :text => "Must be post" },
+ :add_headers => { "Allow" => "POST" }
+
+* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled #1897 *jeremye@bsa.ca.gov*
+
+
+## 1.13.3 (March 12th, 2007) ##
+
+* Apply [5709] to stable.
+
+* session_enabled? works with session :off. #6680 *Jonathan del Strother*
+
+* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id. *Stefan Kaes*
+
+
+## 1.13.2 (February 5th, 2007) ##
+
+* Add much-needed html-scanner tests. Fixed CDATA parsing bug. *Rick Olson*
+
+* improve error message for Routing for named routes. *Rob Sanheim*
+
+* Added enhanced docs to routing assertions. *Rob Sanheim*
+
+* fix form_for example in ActionController::Resources documentation. *gnarg*
+
+* Add singleton resources from trunk *Rick Olson*
+
+* select :multiple => true suffixes the attribute name with [] unless already suffixed. #6977 *nik.kakelin, ben, julik*
+
+* Improve routes documentation. #7095 *zackchandler*
+
+* Resource member routes require :id, eliminating the ambiguous overlap with collection routes. #7229 *dkubb*
+
+* Fixed NumberHelper#number_with_delimiter to use "." always for splitting the original number, not the delimiter parameter #7389 *ceefour*
+
+* Autolinking recognizes trailing and embedded . , : ; #7354 *Jarkko Laine*
+
+* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268. *imajes*
+
+* Improved auto_link to match more valid urls correctly *Tobias Lütke*
+
+
+## 1.13.1 (January 18th, 2007) ##
+
+* Fixed content-type bug in Prototype *sam*
+
+
+## 1.13.0 (January 16th, 2007) ##
+
+* Modernize cookie testing code, and increase coverage (Heckle++) #7101 *Kevin Clark*
+
+* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 *Kevin Clark*
+
+* Update to Prototype 1.5.0. *Sam Stephenson*
+
+* Allow exempt_from_layout :rhtml. #6742, #7026 *Dan Manges, Squeegy*
+
+* Fix parsing of array[] CGI parameters so extra empty values aren't included. #6252 *Nicholas Seckar, aiwilliams, brentrowland*
+
+* link_to_unless_current works with full URLs as well as paths. #6891 *Jarkko Laine, Manfred Stienstra, idrifter*
+
+* Fix HTML::Node to output double quotes instead of single quotes. Closes #6845 *mitreandy*
+
+* Fix no method error with error_messages_on. Closes #6935 *nik.wakelin Koz*
+
+* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs. Closes #6922 *Steven Bristol*
+
+* Slight doc tweak to #prepend_filter. Closes #6493 *Jeremy Voorhis*
+
+* Add more extensive documentation to the AssetTagHelper. Closes #6452 *Bob Silva*
+
+* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper. Closes #6394 *Bob Silva*
+
+* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 *Jan Prill*
+
+* Make sure html_document is reset between integration test requests. *ctm*
+
+* Set session to an empty hash if :new_session => false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError. *Josh Susser*
+
+* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting. Closes #6128. *Rick Olson*
+
+* Ensure render :json => ... skips the layout. #6808 *Josh Peek*
+
+* Silence log_error deprecation warnings from inspecting deprecated instance variables. *Nate Wiger*
+
+* Only cache GET requests with a 200 OK response. #6514, #6743 *RSL, anamba*
+
+* Correctly report which filter halted the chain. #6699 *Martin Emde*
+
+* respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument. #4185 *Scott Raymond, eventualbuddha*
+ # application/json response with body 'Element.show({:name: "David"})'
+ respond_to do |format|
+ format.json { render :json => { :name => "David" }.to_json, :callback => 'Element.show' }
+ end
+
+* Makes :discard_year work without breaking multi-attribute parsing in AR. #1260, #3800 *sean@ardismg.com, jmartin@desertflood.com, stephen@touset.org, Bob Silva*
+
+* Adds html id attribute to date helper elements. #1050, #1382 *mortonda@dgrmm.net, David North, Bob Silva*
+
+* Add :index and @auto_index capability to model driven date/time selects. #847, #2655 *moriq, Doug Fales, Bob Silva*
+
+* Add :order to datetime_select, select_datetime, and select_date. #1427 *Timothee Peignier, Patrick Lenz, Bob Silva*
+
+* Added time_select to work with time values in models. Update scaffolding. #2489, #2833 *Justin Palmer, Andre Caum, Bob Silva*
+
+* Added :include_seconds to select_datetime, datetime_select and time_select. #2998 *csn, Bob Silva*
+
+* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization. #363 *tomasj, Bob Silva*
+
+* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC. #3811 *Bob Silva*
+
+* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status. *Jeremy Kemper*
+
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) *David Heinemeier Hansson*
+
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 *David Heinemeier Hansson*
+
+
+## 1.13.0 RC1 (r5619, November 22nd, 2006) ##
+
+* Update Routing to complain when :controller is not specified by a route. Closes #6669. *Nicholas Seckar*
+
+* Ensure render_to_string cleans up after itself when an exception is raised. #6658 *rsanheim*
+
+* Update to Prototype and script.aculo.us [5579]. *Sam Stephenson, Thomas Fuchs*
+
+* simple_format helper doesn't choke on nil. #6644 *jerry426*
+
+* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. *Nicholas Seckar*
+
+* Deprecate standalone components. *Jeremy Kemper*
+
+* Always clear model associations from session. #4795 *sd@notso.net, andylien@gmail.com*
+
+* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. *Sam Stephenson*
+
+* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes. #6586 *dkubb*
+
+* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename. #6401 *Andreas Schwarz, Jeremy Kemper*
+
+* assert_response supports symbolic status codes. #6569 *Kevin Clark*
+ assert_response :ok
+ assert_response :not_found
+ assert_response :forbidden
+
+* Cache parsed query parameters. #6559 *Stefan Kaes*
+
+* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS *Thomas Fuchs*
+
+* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing. #6524 *Bob Silva*
+
+* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods. *Jeremy Kemper*
+
+* Deprecate expire_matched_fragments. Use expire_fragment instead. #6535 *Bob Silva*
+
+* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. *Rick Olson*
+
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag *Rick Olson*
+
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. *Rick Olson*
+
+* Added block-usage to TagHelper#content_tag *DHH*. Example:
+
+ <% content_tag :div, :class => "strong" %>
+ Hello world!
+ <% end %>
+
+ Will output:
+ <div class="strong">Hello world!</div>
+
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 *Bob Silva*
+
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 *Bob Silva*
+
+* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 *Bob Silva/chrismear*
+
+* Force *_url named routes to show the host in ActionView *Rick Olson*
+
+ <%= url_for ... %> # no host
+ <%= foo_path %> # no host
+ <%= foo_url %> # host!
+
+* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. *Sam Stephenson*
+
+* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. *Sam Stephenson*
+
+* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. *Rick Olson*
+
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. *Rick Olson*
+
+* Fix routing to correctly determine when generation fails. Closes #6300. *psross*.
+
+* Fix broken assert_generates when extra keys are being checked. *Jamis Buck*
+
+* Replace KCODE checks with String#chars for truncate. Closes #6385 *Manfred Stienstra*
+
+* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'. *Marcel Molina Jr.*
+
+* Use String#chars in TextHelper::excerpt. Closes #6386 *Manfred Stienstra*
+
+* Fix relative URL root matching problems. *Mark Imbriaco*
+
+* Fix filter skipping in controller subclasses. #5949, #6297, #6299 *Martin Emde*
+
+* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API). *Jeremy Kemper*
+
+* Rename test assertion to prevent shadowing. Closes #6306. *psross*
+
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 *Philip Hallstrom*
+
+* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 *evansj*
+
+* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 *tzaharia*. Example:
+
+ update_page_tag :defer => 'true' { |page| ... }
+
+ Gives:
+
+ <script defer="true" type="text/javascript">...</script>
+
+ Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
+
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 *Mike Naberezny*
+
+* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 *sven@c3d2.de*
+
+* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 *cmselmer*
+
+* respond_to :html doesn't assume .rhtml. #6281 *Hampton Catlin*
+
+* Fixed some deprecation warnings in ActionPack *Rick Olson*
+
+* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them. #6240 *japgolly*
+
+* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead. *Jeremy Kemper*
+
+* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. *Jamis Buck*. Examples:
+
+ head :status => 404 # expands to "404 Not Found"
+ head :status => :not_found # expands to "404 Not Found"
+ head :status => :created # expands to "201 Created"
+
+* Add head(options = {}) for responses that have no body. *Jamis Buck*. Examples:
+
+ head :status => 404 # return an empty response with a 404 status
+ head :location => person_path(@person), :status => 201
+
+* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain. *Rick Olson*
+
+* strip_links is case-insensitive. #6285 *tagoh, Bob Silva*
+
+* Clear the cache of possible controllers whenever Routes are reloaded. *Nicholas Seckar*
+
+* Filters overhaul including meantime filter support using around filters + blocks. #5949 *Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper*
+
+* Update CGI process to allow sessions to contain namespaced models. Closes #4638. *dfelstead@site5.com*
+
+* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action => 'index'). Closes #5950. *Nicholas Seckar*
+
+* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability. #3368, #6226 *sdsykes, fhanshaw@vesaria.com*
+
+* Added that respond_to blocks will automatically set the content type to be the same as is requested *DHH*. Examples:
+
+ respond_to do |format|
+ format.html { render :text => "I'm being sent as text/html" }
+ format.rss { render :text => "I'm being sent as application/rss+xml" }
+ format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
+ end
+
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) *David Heinemeier Hansson*
+
+* Added proper getters and setters for content type and charset *DHH*. Example of what we used to do:
+
+ response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
+
+ ...now:
+
+ response.content_type = Mime::ATOM
+ response.charset = "utf-8"
+
+* Declare file extensions exempt from layouts. #6219 *brandon*
+ Example: ActionController::Base.exempt_from_layout 'rpdf'
+
+* Add chained replace/update support for assert_select_rjs *Rick Olson*
+
+ Given RJS like...
+
+ page['test1'].replace "<div id=\"1\">foo</div>"
+ page['test2'].replace_html "<div id=\"2\">foo</div>"
+
+ Test it with...
+
+ assert_select_rjs :chained_replace
+ assert_select_rjs :chained_replace, "test1"
+
+ assert_select_rjs :chained_replace_html
+ assert_select_rjs :chained_replace_html, "test2"
+
+* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency. #6132, #6178 *choonkeat@gmail.com*
+
+* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 *Manfred Stienstra, raphinou@yahoo.com*
+
+* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 *Eric Hodel*
+
+* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids, #6068 *petermichaux@gmail.com*
+
+* Update UrlWriter to support :only_path. *Nicholas Seckar, Dave Thomas*
+
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional *DHH*. So what used to require a nil, like this:
+
+ link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
+
+ ...can be written like this:
+
+ link_to("Hider", :class => "hider_link") { |p| p[:something].hide }
+
+* Added access to nested attributes in RJS #4548 *richcollins@gmail.com*. Examples:
+
+ page['foo']['style'] # => $('foo').style;
+ page['foo']['style']['color'] # => $('blank_slate').style.color;
+ page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
+ page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
+
+* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) *eule@space.ch*
+
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension *David Heinemeier Hansson*
+
+* Fixed FormOptionsHelper#select to respect :selected value #5813
+
+* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 *moriq@moriq.com*
+
+* Fixed TextHelper#pluralize to handle 1 as a string #5909 *rails@bencurtis.com*
+
+* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 *Bob Silva*
+
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" *David Heinemeier Hansson*
+
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 *David Heinemeier Hansson*
+
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 *David Heinemeier Hansson*
+
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support *David Heinemeier Hansson*
+
+* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 *assaf.arkin@gmail.com*
+
+* radio_button_tag generates unique id attributes. #3353 *Bob Silva, somekool@gmail.com*
+
+* strip_tags passes through blank args such as nil or "". #2229, #6702 *duncan@whomwah.com, dharana*
+
+* Cleanup assert_tag :children counting. #2181 *jamie@bravenet.com*
+
+* button_to accepts :method so you can PUT and DELETE with it. #6005 *Dan Webb*
+
+* Update sanitize text helper to strip plaintext tags, and <img src="javascript:bang">. *Rick Olson*
+
+* Add routing tests to assert that RoutingError is raised when conditions aren't met. Closes #6016 *Nathan Witmer*
+
+* Make auto_link parse a greater subset of valid url formats. *Jamis Buck*
+
+* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. *Mike Clark*
+
+* Switch to using FormEncodedPairParser for parsing request parameters. *Nicholas Seckar, David Heinemeier Hansson*
+
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. *Tobias Lütke*
+
+* Deprecation: test deprecated instance vars in partials. *Jeremy Kemper*
+
+* Add UrlWriter to allow writing urls from Mailers and scripts. *Nicholas Seckar*
+
+* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. *Nicholas Seckar*
+
+* Add controller_paths variable to Routing. *Nicholas Seckar*
+
+* Fix assert_redirected_to issue with named routes for module controllers. *Rick Olson*
+
+* Tweak RoutingError message to show option diffs, not just missing named route significant keys. *Rick Olson*
+
+* Invoke method_missing directly on hidden actions. Closes #3030. *Nicholas Seckar*
+
+* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. *Rick Olson*
+
+* Replace Reloadable with Reloadable::Deprecated. *Nicholas Seckar*
+
+* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. *Jeremy Kemper*
+
+* Add support for the param_name parameter to the auto_complete_field helper. #5026 *david.a.williams@gmail.com*
+
+* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. *Jeremy Kemper*
+
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 *François Beausoleil*
+
+* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 *pjhyett@gmail.com*
+
+* Make controller_path available as an instance method. #5724 *jmckible@gmail.com*
+
+* Update query parser to support adjacent hashes. *Nicholas Seckar*
+
+* Make action caching aware of different formats for the same action so that, e.g. foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. *Marcel Molina Jr.*
+
+* Restrict Request Method hacking with ?_method to POST requests. *Rick Olson*
+
+* Fixed the new_#{resource}_url route and added named route tests for Simply Restful. *Rick Olson*
+
+* Added map.resources from the Simply Restful plugin *DHH*. Examples (the API has changed to use plurals!):
+
+ map.resources :messages
+ map.resources :messages, :comments
+ map.resources :messages, :new => { :preview => :post }
+
+* Fixed that integration simulation of XHRs should set Accept header as well *Edward Frederick*
+
+* TestRequest#reset_session should restore a TestSession, not a hash *Michael Koziarski*
+
+* Don't search a load-path of '.' for controller files *Jamis Buck*
+
+* Update integration.rb to require test_process explicitly instead of via Dependencies. *Nicholas Seckar*
+
+* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 *lmarlow*
+
+* Allow form_for and fields_for to work with indexed form inputs. *Jeremy Kemper, Matt Lyon*
+
+ <% form_for 'post[]', @post do |f| -%>
+ <% end -%>
+
+* Remove leak in development mode by replacing define_method with module_eval. *Nicholas Seckar*
+
+* Provide support for decimal columns to form helpers. Closes #5672. *Dave Thomas*
+
+* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 *olivier_ansaldi@yahoo.com*
+
+* Reset @html_document between requests so assert_tag works. #4810 *Jarkko Laine, easleydp@gmail.com*
+
+* Integration tests behave well with render_component. #4632 *edward.frederick@revolution.com, dev.rubyonrails@maxdunn.com*
+
+* Added exception handling of missing layouts #5373 *chris@ozmm.org*
+
+* Fixed that real files and symlinks should be treated the same when compiling templates #5438 *zachary@panandscan.com*
+
+* Fixed that the flash should be reset when reset_session is called #5584 *Shugo Maeda*
+
+* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 *murpyh@rubychan.de*
+
+* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) *David Heinemeier Hansson*
+
+* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. *Nicholas Seckar*
+
+* Avoid naming collision among compiled view methods. *Jeremy Kemper*
+
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 *Mislav Marohnić*
+
+* Determine the correct template_root for deeply nested components. #2841 *s.brink@web.de*
+
+* Fix that routes with *path segments in the recall can generate URLs. *Rick Olson*
+
+* Fix strip_links so that it doesn't hang on multiline <acronym> tags *Jamis Buck*
+
+* Remove problematic control chars in rescue template. #5316 *Stefan Kaes*
+
+* Make sure passed routing options are not mutated by routing code. #5314 *Blair Zajac*
+
+* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. *Jamis Buck*
+
+* Escape the path before routing recognition. #3671
+
+* Make sure :id and friends are unescaped properly. #5275 *me@julik.nl*
+
+* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form). #210 *mklame@atxeu.com, matthew@walker.wattle.id.au*
+
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types *David Heinemeier Hansson*
+
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types *DHH*. Example: Mime::Type.register("image/gif", :gif)
+
+* Added support for Mime objects in render :content_type option *DHH*. Example: render :text => some_atom, :content_type => Mime::ATOM
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 *Manfred Stienstra <m.stienstra@fngtps.com>*
+
+* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. *Nicholas Seckar, Jamis Buck*
+
+ map.connect '/foo/:id', :controller => '...', :action => '...'
+ map.connect '/foo/:id.:format', :controller => '...', :action => '...'
+ map.connect '/foo/:id', ..., :conditions => { :method => :get }
+
+* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. *Jeremy Kemper*
+
+* Accept multipart PUT parameters. #5235 *guy.naor@famundo.com*
+
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output *DHH*. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find :all
+
+ respond_to do |format|
+ format.html
+ format.xml { render :xml => @posts.to_xml }
+ format.rss { render :action => "feed.rxml" }
+ end
+ end
+ end
+
+ \# returns HTML when requested by a browser, since the browser
+ \# has the HTML mimetype at the top of its priority list
+ Accept: text/html
+ GET /weblog
+
+ \# returns the XML
+ Accept: application/xml
+ GET /weblog
+
+ \# returns the HTML
+ Accept: application/xml
+ GET /weblog.html
+
+ \# returns the XML
+ Accept: text/html
+ GET /weblog.xml
+
+ All this relies on the fact that you have a route that includes .:format.
+
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post *David Heinemeier Hansson*
+
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete *David Heinemeier Hansson*
+
+* follow_redirect doesn't complain about being redirected to the same controller. #5153 *dymo@mk.ukrtelecom.ua*
+
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. *Kevin Clark*
+
+* Fix NoMethodError when parsing params like &&. *Adam Greenfield*
+
+* form.text_area handles the :size option just like the original text_area (:size => '60x10' becomes cols="60" rows="10"). *Jeremy Kemper*
+
+* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 *anonymous*
+
+* Small fix in routing to allow dynamic routes (broken after [4242]) *Rick Olson*
+
+ map.connect '*path', :controller => 'files', :action => 'show'
+
+* Use #flush between switching from #write to #syswrite. Closes #4907. *Blair Zajac <blair@orcaware.com>*
+
+* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. *andrew@redlinesoftware.com, Marcel Molina Jr.*
+
+ error_messages_for :account, :user, :subscription, :object_name => :account
+
+* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: *Rick Olson*
+
+ redirect_to :action => 'new'
+ assert_redirected_to :controller => 'monkeys', :action => 'new'
+
+* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method *Rick Olson*
+
+ assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
+ assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
+
+* Diff compared options with #assert_redirected_to *Rick Olson*
+
+* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action *Jamis Buck*
+
+* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. *zraii@comcast.net, Sam Stephenson*
+
+* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. *Jamis Buck*
+
+* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. *Sam Stephenson*
+ ex. verify :only => :speak, :method => :post,
+ :render => { :status => 405, :text => "Must be post" },
+ :add_headers => { "Allow" => "POST" }
+
+
+## 1.12.5 (August 10th, 2006) ##
+
+* Updated security fix
+
+
+## 1.12.4 (August 8th, 2006) ##
+
+* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. *Rick Olson*
+
+* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for *David Heinemeier Hansson*
+
+* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled. #1897 *jeremye@bsa.ca.gov*
+
+* Fixed that real files and symlinks should be treated the same when compiling templates. #5438 *zachary@panandscan.com*
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 *Manfred Stienstra <m.stienstra@fngtps.com>*
+
+* Update documentation for erb trim syntax. #5651 *matt@mattmargolis.net*
+
+* Short documentation to mention use of Mime::Type.register. #5710 *choonkeat@gmail.com*
+
+
+## 1.12.3 (June 28th, 2006) ##
+
+* Fix broken traverse_to_controller. We now:
+ Look for a _controller.rb file under RAILS_ROOT to load.
+ If we find it, we require_dependency it and return the controller it defined. (If none was defined we stop looking.)
+ If we don't find it, we look for a .rb file under RAILS_ROOT to load. If we find it, and it loads a constant we keep looking.
+ Otherwise we check to see if a directory of the same name exists, and if it does we create a module for it.
+
+
+## 1.12.2 (June 27th, 2006) ##
+
+* Refinement to avoid exceptions in traverse_to_controller.
+
+* (Hackish) Fix loading of arbitrary files in Ruby's load path by traverse_to_controller. *Nicholas Seckar*
+
+
+## 1.12.1 (April 6th, 2006) ##
+
+* Fixed that template extensions would be cached development mode #4624 *Stefan Kaes*
+
+* Update to Prototype 1.5.0_rc0 *Sam Stephenson*
+
+* Honor skipping filters conditionally for only certain actions even when the parent class sets that filter to conditionally be executed only for the same actions. #4522 *Marcel Molina Jr.*
+
+* Delegate xml_http_request in integration tests to the session instance. *Jamis Buck*
+
+* Update the diagnostics template skip the useless '<controller not set>' text. *Nicholas Seckar*
+
+* CHANGED DEFAULT: Don't parse YAML input by default, but keep it available as an easy option *David Heinemeier Hansson*
+
+* Add additional autocompleter options *aballai, Thomas Fuchs*
+
+* Fixed fragment caching of binary data on Windows #4493 *bellis@deepthought.org*
+
+* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us *Sam Stephenson, Thomas Fuchs*
+
+* Added automated timestamping to AssetTagHelper methods for stylesheets, javascripts, and images when Action Controller is run under Rails *DHH*. Example:
+
+ image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?1143664135" />'
+
+ ...to avoid frequent stats (not a problem for most people), you can set RAILS_ASSET_ID in the ENV to avoid stats:
+
+ ENV["RAILS_ASSET_ID"] = "2345"
+ image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?2345" />'
+
+ This can be used by deployment managers to set the asset id by application revision
+
+
+## 1.12.0 (March 27th, 2006) ##
+
+* Add documentation for respond_to. *Jamis Buck*
+
+* Fixed require of bluecloth and redcloth when gems haven't been loaded #4446 *murphy@cYcnus.de*
+
+* Update to Prototype 1.5.0_pre1 *Sam Stephenson*
+
+* Change #form_for and #fields_for so that the second argument is not required *Dave Thomas*
+
+ <% form_for :post, @post, :url => { :action => 'create' } do |f| -%>
+
+ becomes...
+
+ <% form_for :post, :url => { :action => 'create' } do |f| -%>
+
+* Update to script.aculo.us 1.6 *Thomas Fuchs*
+
+* Enable application/x-yaml processing by default *Jamis Buck*
+
+* Fix double url escaping of remote_function. Add :escape => false option to ActionView's url_for. *Nicholas Seckar*
+
+* Add :script option to in_place_editor to support evalScripts (closes #4194) *Cody Fauser*
+
+* Fix mixed case enumerable methods in the JavaScript Collection Proxy (closes #4314) *Cody Fauser*
+
+* Undo accidental escaping for mail_to; add regression test. *Nicholas Seckar*
+
+* Added nicer message for assert_redirected_to (closes #4294) *court3nay*
+
+ assert_redirected_to :action => 'other_host', :only_path => false
+
+ when it was expecting...
+
+ redirected_to :action => 'other_host', :only_path => true, :host => 'other.test.host'
+
+ gives the error message...
+
+ response is not a redirection to all of the options supplied (redirection is <{:only_path=>false, :host=>"other.test.host", :action=>"other_host"}>), difference: <{:only_path=>"true", :host=>"other.test.host"}>
+
+* Change url_for to escape the resulting URLs when called from a view. *Nicholas Seckar, coffee2code*
+
+* Added easy support for testing file uploads with fixture_file_upload #4105 *turnip@turnipspatch.com*. Example:
+
+ # Looks in Test::Unit::TestCase.fixture_path + '/files/spongebob.png'
+ post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
+
+* Fixed UrlHelper#current_page? to behave even when url-escaped entities are present #3929 *jeremy@planetargon.com*
+
+* Add ability for relative_url_root to be specified via an environment variable RAILS_RELATIVE_URL_ROOT. *isaac@reuben.com, Nicholas Seckar*
+
+* Fixed link_to "somewhere", :post => true to produce valid XHTML by using the parentnode instead of document.body for the instant form #3007 *Bob Silva*
+
+* Added :function option to PrototypeHelper#observe_field/observe_form that allows you to call a function instead of submitting an ajax call as the trigger #4268 *jonathan@daikini.com*
+
+* Make Mime::Type.parse consider q values (if any) *Jamis Buck*
+
+* XML-formatted requests are typecast according to "type" attributes for :xml_simple *Jamis Buck*
+
+* Added protection against proxy setups treating requests as local even when they're not #3898 *Steve Purcell*
+
+* Added TestRequest#raw_post that simulate raw_post from CgiRequest #3042 *François Beausoleil*
+
+* Underscore dasherized keys in formatted requests *Jamis Buck*
+
+* Add MimeResponds::Responder#any for managing multiple types with identical responses *Jamis Buck*
+
+* Make the xml_http_request testing method set the HTTP_ACCEPT header *Jamis Buck*
+
+* Add Verification to scaffolds. Prevent destructive actions using GET *Michael Koziarski*
+
+* Avoid hitting the filesystem when using layouts by using a File.directory? cache. *Stefan Kaes, Nicholas Seckar*
+
+* Simplify ActionController::Base#controller_path *Nicholas Seckar*
+
+* Added simple alert() notifications for RJS exceptions when config.action_view.debug_rjs = true. *Sam Stephenson*
+
+* Added :content_type option to render, so you can change the content type on the fly *DHH*. Example: render :action => "atom.rxml", :content_type => "application/atom+xml"
+
+* CHANGED DEFAULT: The default content type for .rxml is now application/xml instead of type/xml, see http://www.xml.com/pub/a/2004/07/21/dive.html for reason *David Heinemeier Hansson*
+
+* Added option to render action/template/file of a specific extension (and here by template type). This means you can have multiple templates with the same name but a different extension *DHH*. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find :all
+
+ respond_to do |type|
+ type.html # using defaults, which will render weblog/index.rhtml
+ type.xml { render :action => "index.rxml" }
+ type.js { render :action => "index.rjs" }
+ end
+ end
+ end
+
+* Added better support for using the same actions to output for different sources depending on the Accept header *DHH*. Example:
+
+ class WeblogController < ActionController::Base
+ def create
+ @post = Post.create(params[:post])
+
+ respond_to do |type|
+ type.js { render } # renders create.rjs
+ type.html { redirect_to :action => "index" }
+ type.xml do
+ headers["Location"] = url_for(:action => "show", :id => @post)
+ render(:nothing, :status => "201 Created")
+ end
+ end
+ end
+ end
+
+* Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 *David Heinemeier Hansson*
+
+* Integration test's url_for now runs in the context of the last request (if any) so after post /products/show/1 url_for :action => 'new' will yield /product/new *Tobias Lütke*
+
+* Re-added mixed-in helper methods for the JavascriptGenerator. Moved JavascriptGenerators methods to a module that is mixed in after the helpers are added. Also fixed that variables set in the enumeration methods like #collect are set correctly. Documentation added for the enumeration methods *Rick Olson*. Examples:
+
+ page.select('#items li').collect('items') do |element|
+ element.hide
+ end
+ # => var items = $$('#items li').collect(function(value, index) { return value.hide(); });
+
+* Added plugin support for parameter parsers, which allows for better support for REST web services. By default, posts submitted with the application/xml content type is handled by creating a XmlSimple hash with the same name as the root element of the submitted xml. More handlers can easily be registered like this:
+
+ # Assign a new param parser to a new content type
+ ActionController::Base.param_parsers['application/atom+xml'] = Proc.new do |data|
+ node = REXML::Document.new(post)
+ { node.root.name => node.root }
+ end
+
+ # Assign the default XmlSimple to a new content type
+ ActionController::Base.param_parsers['application/backpack+xml'] = :xml_simple
+
+ Default YAML web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functionality back. As part of this new plugin support, request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type *Tobias Lütke*
+* Fixed Effect.Appear in effects.js to work with floats in Safari #3524, #3813, #3044 *Thomas Fuchs*
+
+* Fixed that default image extension was not appended when using a full URL with AssetTagHelper#image_tag #4032, #3728 *rubyonrails@beautifulpixel.com*
+
+* Added that page caching will only happen if the response code is less than 400 #4033 *g.bucher@teti.ch*
+
+* Add ActionController::IntegrationTest to allow high-level testing of the way the controllers and routes all work together *Jamis Buck*
+
+* Added support to AssetTagHelper#javascript_include_tag for having :defaults appear anywhere in the list, so you can now make one call ala javascript_include_tag(:defaults, "my_scripts") or javascript_include_tag("my_scripts", :defaults) depending on how you want the load order #3506 *Bob Silva*
+
+* Added support for visual effects scoped queues to the visual_effect helper #3530 *Abdur-Rahman Advany*
+
+* Added .rxml (and any non-rhtml template, really) supportfor CaptureHelper#content_for and CaptureHelper#capture #3287 *Brian Takita*
+
+* Added script.aculo.us drag and drop helpers to RJS *Thomas Fuchs*. Examples:
+
+ page.draggable 'product-1'
+ page.drop_receiving 'wastebasket', :url => { :action => 'delete' }
+ page.sortable 'todolist', :url => { action => 'change_order' }
+
+* Fixed that form elements would strip the trailing [] from the first parameter #3545 *ruby@bobsilva.com*
+
+* During controller resolution, update the NameError suppression to check for the expected constant. *Nicholas Seckar*
+
+* Update script.aculo.us to V1.5.3 *Thomas Fuchs*
+
+* Added various InPlaceEditor options, #3746, #3891, #3896, #3906 *Bill Burcham, ruairi, sl33p3r*
+
+* Added :count option to pagination that'll make it possible for the ActiveRecord::Base.count call to using something else than * for the count. Especially important for count queries using DISTINCT #3839 *Stefan Kaes*
+
+* Update script.aculo.us to V1.5.2 *Thomas Fuchs*
+
+* Added element and collection proxies to RJS *DHH*. Examples:
+
+ page['blank_slate'] # => $('blank_slate');
+ page['blank_slate'].show # => $('blank_slate').show();
+ page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
+
+ page.select('p') # => $$('p');
+ page.select('p.welcome b').first # => $$('p.welcome b').first();
+ page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
+
+* Add JavaScriptGenerator#replace for replacing an element's "outer HTML". #3246 *tom@craz8.com, Sam Stephenson*
+
+* Remove over-engineered form_for code for a leaner implementation. *Nicholas Seckar*
+
+* Document form_for's :html option. *Nicholas Seckar*
+
+* Major components cleanup and speedup. #3527 *Stefan Kaes*
+
+* Fix problems with pagination and :include. *Kevin Clark*
+
+* Add ActiveRecordTestCase for testing AR integration. *Kevin Clark*
+
+* Add Unit Tests for pagination *Kevin Clark*
+
+* Add :html option for specifying form tag options in form_for. *Sam Stephenson*
+
+* Replace dubious controller parent class in filter docs. #3655, #3722 *info@rhalff.com, eigentone@gmail.com*
+
+* Don't interpret the :value option on text_area as an html attribute. Set the text_area's value. #3752 *gabriel@gironda.org*
+
+* Fix remote_form_for creates a non-ajax form. *Rick Olson*
+
+* Don't let arbitrary classes match as controllers -- a potentially dangerous bug. *Nicholas Seckar*
+
+* Fix Routing tests. Fix routing where failing to match a controller would prevent the rest of routes from being attempted. *Nicholas Seckar*
+
+* Add :builder => option to form_for and friends. *Nicholas Seckar, Rick Olson*
+
+* Fix controller resolution to avoid accidentally inheriting a controller from a parent module. *Nicholas Seckar*
+
+* Set sweeper's @controller to nil after a request so that the controller may be collected between requests. *Nicholas Seckar*
+
+* Subclasses of ActionController::Caching::Sweeper should be Reloadable. *Rick Olson*
+
+* Document the :xhr option for verifications. #3666 *leeo*
+
+* Added :only and :except controls to skip_before/after_filter just like for when you add filters *David Heinemeier Hansson*
+
+* Ensure that the instance variables are copied to the template when performing render :update. *Nicholas Seckar*
+
+* Add the ability to call JavaScriptGenerator methods from helpers called in update blocks. *Sam Stephenson* Example:
+ module ApplicationHelper
+ def update_time
+ page.replace_html 'time', Time.now.to_s(:db)
+ page.visual_effect :highlight, 'time'
+ end
+ end
+
+ class UserController < ApplicationController
+ def poll
+ render :update { |page| page.update_time }
+ end
+ end
+
+* Add render(:update) to ActionView::Base. *Sam Stephenson*
+
+* Fix render(:update) to not render layouts. *Sam Stephenson*
+
+* Fixed that SSL would not correctly be detected when running lighttpd/fcgi behind lighttpd w/mod_proxy #3548 *Steve Purcell*
+
+* Added the possibility to specify atomatic expiration for the memcachd session container #3571 *Stefan Kaes*
+
+* Change layout discovery to take into account the change in semantics with File.join and nil arguments. *Marcel Molina Jr.*
+
+* Raise a RedirectBackError if redirect_to :back is called when there's no HTTP_REFERER defined #3049 *Kevin Clark*
+
+* Treat timestamps like datetimes for scaffolding purposes #3388 *Maik Schmidt*
+
+* Fix IE bug with link_to "something", :post => true #3443 *Justin Palmer*
+
+* Extract Test::Unit::TestCase test process behavior into an ActionController::TestProcess module. *Sam Stephenson*
+
+* Pass along blocks from render_to_string to render. *Sam Stephenson*
+
+* Add render :update for inline RJS. *Sam Stephenson* Example:
+ class UserController < ApplicationController
+ def refresh
+ render :update do |page|
+ page.replace_html 'user_list', :partial => 'user', :collection => @users
+ page.visual_effect :highlight, 'user_list'
+ end
+ end
+ end
+
+* allow nil objects for error_messages_for *Michael Koziarski*
+
+* Refactor human_size to exclude decimal place if it is zero. *Marcel Molina Jr.*
+
+* Update to Prototype 1.5.0_pre0 *Sam Stephenson*
+
+* Automatically discover layouts when a controller is namespaced. #2199, #3424 *me@jonnii.com rails@jeffcole.net Marcel Molina Jr.*
+
+* Add support for multiple proxy servers to CgiRequest#host *gaetanot@comcast.net*
+
+* Documentation typo fix. #2367 *Blair Zajac*
+
+* Remove Upload Progress. #2871 *Sean Treadway*
+
+* Fix typo in function name mapping in auto_complete_field. #2929 #3446 *doppler@gmail.com phil.ross@gmail.com*
+
+* Allow auto-discovery of third party template library layouts. *Marcel Molina Jr.*
+
+* Have the form builder output radio button, not check box, when calling the radio button helper. #3331 *LouisStAmour@gmail.com*
+
+* Added assignment of the Autocompleter object created by JavaScriptMacroHelper#auto_complete_field to a local javascript variables *David Heinemeier Hansson*
+
+* Added :on option for PrototypeHelper#observe_field that allows you to specify a different callback hook to have the observer trigger on *David Heinemeier Hansson*
+
+* Added JavaScriptHelper#button_to_function that works just like JavaScriptHelper#link_to_function but uses a button instead of a href *David Heinemeier Hansson*
+
+* Added that JavaScriptHelper#link_to_function will honor existing :onclick definitions when adding the function call *David Heinemeier Hansson*
+
+* Added :disable_with option to FormTagHelper#submit_tag to allow for easily disabled submit buttons with different text *David Heinemeier Hansson*
+
+* Make auto_link handle nil by returning quickly if blank? *Scott Barron*
+
+* Make auto_link match urls with a port number specified. *Marcel Molina Jr.*
+
+* Added support for toggling visual effects to ScriptaculousHelper::visual_effect, #3323. *Thomas Fuchs*
+
+* Update to script.aculo.us to 1.5.0 rev. 3343 *Thomas Fuchs*
+
+* Added :select option for JavaScriptMacroHelper#auto_complete_field that makes it easier to only use part of the auto-complete suggestion as the value for insertion *Thomas Fuchs*
+
+* Added delayed execution of Javascript from within RJS #3264 *devslashnull@gmail.com*. Example:
+
+ page.delay(20) do
+ page.visual_effect :fade, 'notice'
+ end
+
+* Add session ID to default logging, but remove the verbose description of every step *David Heinemeier Hansson*
+
+* Add the following RJS methods: *Sam Stephenson*
+
+ * alert - Displays an alert() dialog
+ * redirect_to - Changes window.location.href to simulate a browser redirect
+ * call - Calls a JavaScript function
+ * assign - Assigns to a JavaScript variable
+ * << - Inserts an arbitrary JavaScript string
+
+* Fix incorrect documentation for form_for *Nicholas Seckar*
+
+* Don't include a layout when rendering an rjs template using render's :template option. *Marcel Molina Jr.*
+
+## 1.1.2 (December 13th, 2005) ##
+
+* Become part of Rails 1.0
+
+* Update to script.aculo.us 1.5.0 final (equals 1.5.0_rc6) *Thomas Fuchs*
+
+* Update to Prototype 1.4.0 final *Sam Stephenson*
+
+* Added form_remote_for (form_for meets form_remote_tag) *David Heinemeier Hansson*
+
+* Update to script.aculo.us 1.5.0_rc6
+
+* More robust relative url root discovery for SCGI compatibility. This solves the 'SCGI routes problem' -- you no longer need to prefix all your routes with the name of the SCGI mountpoint. #3070 *Dave Ringoen*
+
+* Fix docs for text_area_tag. #3083. *Christopher Cotton*
+
+* Change form_for and fields_for method signatures to take object name and object as separate arguments rather than as a Hash. *David Heinemeier Hansson*
+
+* Introduce :selected option to the select helper. Allows you to specify a selection other than the current value of object.method. Specify :selected => nil to leave all options unselected. #2991 *Jonathan Viney <jonathan@bluewire.net.nz>*
+
+* Initialize @optional in routing code to avoid warnings about uninitialized access to an instance variable. *Nicholas Seckar*
+
+* Make ActionController's render honor the :locals option when rendering a :file. #1665. *Emanuel Borsboom, Marcel Molina Jr.*
+
+* Allow assert_tag(:conditions) to match the empty string when a tag has no children. Closes #2959. *Jamis Buck*
+
+* Update html-scanner to handle CDATA sections better. Closes #2970. *Jamis Buck*
+
+* Don't put flash in session if sessions are disabled. *Jeremy Kemper*
+
+* Strip out trailing &_= for raw post bodies. Closes #2868. *Sam Stephenson*
+
+* Pass multiple arguments to Element.show and Element.hide in JavaScriptGenerator instead of using iterators. *Sam Stephenson*
+
+* Improve expire_fragment documentation. #2966 *court3nay*
+
+* Correct docs for automatic layout assignment. #2610. *Charles M. Gerungan*
+
+* Always create new AR sessions rather than trying too hard to avoid database traffic. #2731 *Jeremy Kemper*
+
+* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). *Sam Stephenson*
+
+* Use Element.update('id', 'html') instead of $('id').innerHTML = 'html' in JavaScriptGenerator#replace_html so that script tags are evaluated. *Sam Stephenson*
+
+* Make rjs templates always implicitly skip out on layouts. *Marcel Molina Jr.*
+
+* Correct length for the truncate text helper. #2913 *Stefan Kaes*
+
+* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. *Sam Stephenson*
+
+* Add support for new rjs templates which wrap an update_page block. *Marcel Molina Jr.*
+
+* Rename Version constant to VERSION. #2802 *Marcel Molina Jr.*
+
+* Correct time_zone_options_for_select docs. #2892 *pudeyo@rpi.com*
+
+* Remove the unused, slow response_dump and session_dump variables from error pages. #1222 *lmarlow*
+
+* Performance tweaks: use Set instead of Array to speed up prototype helper include? calls. Avoid logging code if logger is nil. Inline commonly-called template presence checks. #2880, #2881, #2882, #2883 *Stefan Kaes*
+
+* MemCache store may be given multiple addresses. #2869 *Ryan Carver <ryan@fivesevensix.com>*
+
+* Handle cookie parsing irregularity for certain Nokia phones. #2530 *zaitzow@gmail.com*
+
+* Added PrototypeHelper::JavaScriptGenerator and PrototypeHelper#update_page for easily modifying multiple elements in an Ajax response. *Sam Stephenson* Example:
+
+ update_page do |page|
+ page.insert_html :bottom, 'list', '<li>Last item</li>'
+ page.visual_effect :highlight, 'list'
+ page.hide 'status-indicator', 'cancel-link'
+ end
+
+ generates the following JavaScript:
+
+ new Insertion.Bottom("list", "<li>Last item</li>");
+ new Effect.Highlight("list");
+ ["status-indicator", "cancel-link"].each(Element.hide);
+
+* Refactored JavaScriptHelper into PrototypeHelper and ScriptaculousHelper *Sam Stephenson*
+
+* Update to latest script.aculo.us version (as of [3031])
+
+* Updated docs for in_place_editor, fixes a couple bugs and offers extended support for external controls *Justin Palmer*
+
+* Update documentation for render :file. #2858 *Tom Werner*
+
+* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb. #2855 *court3nay*
+
+* Added FormHelper#form_for and FormHelper#fields_for that makes it easier to work with forms for single objects also if they don't reside in instance variables *DHH*. Examples:
+
+ <% form_for :person, @person, :url => { :action => "update" } do |f| %>
+ First name: <%= f.text_field :first_name %>
+ Last name : <%= f.text_field :last_name %>
+ Biography : <%= f.text_area :biography %>
+ Admin? : <%= f.check_box :admin %>
+ <% end %>
+
+ <% form_for :person, person, :url => { :action => "update" } do |person_form| %>
+ First name: <%= person_form.text_field :first_name %>
+ Last name : <%= person_form.text_field :last_name %>
+
+ <% fields_for :permission => person.permission do |permission_fields| %>
+ Admin? : <%= permission_fields.check_box :admin %>
+ <% end %>
+ <% end %>
+
+* options_for_select allows any objects which respond_to? :first and :last rather than restricting to Array and Range. #2824 *Jacob Robbins <jrobbins@cmj.com>, Jeremy Kemper*
+
+* The auto_link text helper accepts an optional block to format the link text for each url and email address. Example: auto_link(post.body) { |text| truncate(text, 10) } *Jeremy Kemper*
+
+* assert_tag uses exact matches for string conditions, instead of partial matches. Use regex to do partial matches. #2799 *Jamis Buck*
+
+* CGI::Session::ActiveRecordStore.data_column_name = 'foobar' to use a different session data column than the 'data' default. *nbpwie102@sneakemail.com*
+
+* Do not raise an exception when default helper is missing; log a debug message instead. It's nice to delete empty helpers. *Jeremy Kemper*
+
+* Controllers with acronyms in their names (e.g. PDFController) require the correct default helper (PDFHelper in file pdf_helper.rb). #2262 *jeff@opendbms.com*
+
+
+## 1.11.0 (November 7th, 2005) ##
+
+* Added request as instance method to views, so you can do <%= request.env["HTTP_REFERER"] %>, just like you can already access response, session, and the likes *David Heinemeier Hansson*
+
+* Fix conflict with assert_tag and Glue gem #2255 *david.felstead@gmail.com*
+
+* Add documentation to assert_tag indicating that it only works with well-formed XHTML #1937, #2570 *Jamis Buck*
+
+* Added action_pack.rb stub so that ActionPack::Version loads properly *Sam Stephenson*
+
+* Added short-hand to assert_tag so assert_tag :tag => "span" can be written as assert_tag "span" *David Heinemeier Hansson*
+
+* Added skip_before_filter/skip_after_filter for easier control of the filter chain in inheritance hierachies *DHH*. Example:
+
+ class ApplicationController < ActionController::Base
+ before_filter :authenticate
+ end
+
+ class WeblogController < ApplicationController
+ # will run the :authenticate filter
+ end
+
+ class SignupController < ActionController::Base
+ # will not run the :authenticate filter
+ skip_before_filter :authenticate
+ end
+
+* Added redirect_to :back as a short-hand for redirect_to(request.env["HTTP_REFERER"]) *David Heinemeier Hansson*
+
+* Change javascript_include_tag :defaults to not use script.aculo.us loader, which facilitates the use of plugins for future script.aculo.us and third party javascript extensions, and provide register_javascript_include_default for plugins to specify additional JavaScript files to load. Removed slider.js and builder.js from actionpack. *Thomas Fuchs*
+
+* Fix problem where redirecting components can cause an infinite loop *Rick Olson*
+
+* Added support for the queue option on visual_effect *Thomas Fuchs*
+
+* Update script.aculo.us to V1.5_rc4 *Thomas Fuchs*
+
+* Fix that render :text didn't interpolate instance variables #2629, #2626 *Stefan Kaes*
+
+* Fix line number detection and escape RAILS_ROOT in backtrace Regexp *Nicholas Seckar*
+
+* Fixed document.getElementsByClassName from Prototype to be speedy again *Sam Stephenson*
+
+* Recognize ./#{RAILS_ROOT} as RAILS_ROOT in error traces *Nicholas Seckar*
+
+* Remove ARStore session fingerprinting *Nicholas Seckar*
+
+* Fix obscure bug in ARStore *Nicholas Seckar*
+
+* Added TextHelper#strip_tags for removing HTML tags from a string (using HTMLTokenizer) #2229 *marcin@junkheap.net*
+
+* Added a reader for flash.now, so it's possible to do stuff like flash.now[:alert] ||= 'New if not set' #2422 *Caio Chassot*
+
+
+## 1.10.2 (October 26th, 2005) ##
+
+* Reset template variables after using render_to_string *Stefan Kaes*
+
+* Expose the session model backing CGI::Session
+
+* Abbreviate RAILS_ROOT in traces
+
+
+## 1.10.1 (October 19th, 2005) ##
+
+* Update error trace templates *Nicholas Seckar*
+
+* Stop showing generated routing code in application traces *Nicholas Seckar*
+
+
+## 1.10.0 (October 16th, 2005) ##
+
+* Make string-keys locals assigns optional. Add documentation describing depreciated state *Stefan Kaes*
+
+* Improve line number detection for template errors *Nicholas Seckar*
+
+* Update/clean up documentation (rdoc)
+
+* Upgrade to Prototype 1.4.0_rc0 *Sam Stephenson*
+
+* Added assert_vaild. Reports the proper AR error messages as fail message when the passed record is invalid *Tobias Lütke*
+
+* Add temporary support for passing locals to render using string keys *Nicholas Seckar*
+
+* Clean up error pages by providing better backtraces *Nicholas Seckar*
+
+* Raise an exception if an attempt is made to insert more session data into the ActiveRecordStore data column than the column can hold. #2234. *justin@textdrive.com*
+
+* Removed references to assertions.rb from actionpack assert's backtraces. Makes error reports in functional unit tests much less noisy. *Tobias Lütke*
+
+* Updated and clarified documentation for JavaScriptHelper to be more concise about the various options for including the JavaScript libs. *Thomas Fuchs*
+
+* Hide "Retry with Breakpoint" button on error pages until feature is functional. *David Heinemeier Hansson*
+
+* Fix Request#host_with_port to use the standard port when Rails is behind a proxy. *Nicholas Seckar*
+
+* Escape query strings in the href attribute of URLs created by url_helper. #2333 *Michael Schuerig <michael@schuerig.de>*
+
+* Improved line number reporting for template errors *Nicholas Seckar*
+
+* Added :locals support for render :inline #2463 *mdabney@cavoksolutions.com*
+
+* Unset the X-Requested-With header when using the xhr wrapper in functional tests so that future requests aren't accidentally xhr'ed #2352 *me@julik.nl, Sam Stephenson*
+
+* Unescape paths before writing cache to file system. #1877. *Damien Pollet*
+
+* Wrap javascript_tag contents in a CDATA section and add a cdata_section method to TagHelper #1691 *Michael Schuerig, Sam Stephenson*
+
+* Misc doc fixes (typos/grammar/etc). #2445. *coffee2code*
+
+* Speed improvement for session_options. #2287. *Stefan Kaes*
+
+* Make cacheing binary files friendly with Windows. #1975. *Rich Olson*
+
+* Convert boolean form options form the tag_helper. #809. *Michael Schuerig <michael@schuerig.de>*
+
+* Fixed that an instance variable with the same name as a partial should be implicitly passed as the partial :object #2269 *court3nay*
+
+* Update Prototype to V1.4.0_pre11, script.aculo.us to [2502] *Thomas Fuchs*
+
+* Make assert_tag :children count appropriately. Closes #2181. *jamie@bravenet.com*
+
+* Forced newer versions of RedCloth to use hard breaks *David Heinemeier Hansson*
+
+* Added new scriptaculous options for auto_complete_field #2343 *Manfred Stienstra*
+
+* Don't prepend the asset host if the string is already a fully-qualified URL
+
+* Updated to script.aculo.us V1.5.0_rc2 and Prototype to V1.4.0_pre7 *Thomas Fuchs*
+
+* Undo condition change made in [2345] to prevent normal parameters arriving as StringIO.
+
+* Tolerate consecutive delimiters in query parameters. #2295 *darashi@gmail.com*
+
+* Streamline render process, code cleaning. Closes #2294. *skae*
+
+* Keep flash after components are rendered. #2291 *Rick Olson, Scott*
+
+* Shorten IE file upload path to filename only to match other browsers. #1507 *court3nay*
+
+* Fix open/save dialog in IE not opening files send with send_file/send_data, #2279 *Thomas Fuchs*
+
+* Fixed that auto_discovery_link_tag couldn't take a string as the URL *David Heinemeier Hansson*
+
+* Fixed problem with send_file and WEBrick using stdout #1812 *David Heinemeier Hansson*
+
+* Optimized tag_options to not sort keys, which is no longer necessary when assert_dom_equal and friend is available #1995 *skae*
+
+* Added assert_dom_equal and assert_dom_not_equal to compare tags generated by the helpers in an order-indifferent manner #1995 *skae*
+
+* Fixed that Request#domain caused an exception if the domain header wasn't set in the original http request #1795 *Michael Koziarski*
+
+* Make the truncate() helper multi-byte safe (assuming $KCODE has been set to something other than "NONE") #2103
+
+* Add routing tests from #1945 *ben@groovie.org*
+
+* Add a routing test case covering #2101 *Nicholas Seckar*
+
+* Cache relative_url_root for all webservers, not just Apache #2193 *skae*
+
+* Speed up cookie use by decreasing string copying #2194 *skae*
+
+* Fixed access to "Host" header with requests made by crappy old HTTP/1.0 clients #2124 *Marcel Molina Jr.*
+
+* Added easy assignment of fragment cache store through use of symbols for included stores (old way still works too)
+
+ Before:
+ ActionController::Base.fragment_cache_store =
+ ActionController::Base::Caching::Fragments::FileStore.new("/path/to/cache/directory")
+
+ After:
+ ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"
+
+* Added ActionController::Base.session_store=, session_store, and session_options to make it easier to tweak the session options (instead of going straight to ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)
+
+* Added TextHelper#cycle to cycle over an array of values on each hit (useful for alternating row colors etc) #2154 *dave-ml@dribin.org*
+
+* Ensure that request.path never returns nil. Closes #1675 *Nicholas Seckar*
+
+* Add ability to specify Route Regexps for controllers. Closes #1917. *Sebastian Kanthak*
+
+* Provide Named Route's hash methods as helper methods. Closes #1744. *Nicholas Seckar, Steve Purcell*
+
+* Added :multipart option to ActiveRecordHelper#form to make it possible to add file input fields #2034 *jstirk@oobleyboo.com*
+
+* Moved auto-completion and in-place editing into the Macros module and their helper counterparts into JavaScriptMacrosHelper
+
+* Added in-place editing support in the spirit of auto complete with ActionController::Base.in_place_edit_for, JavascriptHelper#in_place_editor_field, and Javascript support from script.aculo.us #2038 *Jon Tirsen*
+
+* Added :disabled option to all data selects that'll make the elements inaccessible for change #2167, #253 *eigentone*
+
+* Fixed that TextHelper#auto_link_urls would include punctuation in the links #2166, #1671 *eigentone*
+
+* Fixed that number_to_currency(1000, {:precision => 0})) should return "$1,000", instead of "$1,000." #2122 *sd@notso.net*
+
+* Allow link_to_remote to use any DOM-element as the parent of the form elements to be submitted #2137 *erik@ruby-lang.nl*. Example:
+
+ <tr id="row023">
+ <td><input name="foo"/></td>
+ <td><input name="bar"/></td>
+ <td><%= link_to_remote 'Save', :update => "row023",
+ :submit => "row023", :url => {:action => 'save_row'} %></td>
+ </tr>
+
+* Fixed that render :partial would fail when :object was a Hash (due to backwards compatibility issues) #2148 *Sam Stephenson*
+
+* Fixed JavascriptHelper#auto_complete_for to only include unique items #2153 *Thomas Fuchs*
+
+* Fixed all AssetHelper methods to work with relative paths, such that javascript_include_tag('stdlib/standard') will look in /javascripts/stdlib/standard instead of '/stdlib/standard/' #1963
+
+* Avoid extending view instance with helper modules each request. Closes #1979
+
+* Performance improvements to CGI methods. Closes #1980 *Stefan Kaes*
+
+* Added :post option to UrlHelper#link_to that makes it possible to do POST requests through normal ahref links using Javascript
+
+* Fixed overwrite_params
+
+* Added ActionController::Base.benchmark and ActionController::Base.silence to allow for easy benchmarking and turning off the log
+
+* Updated vendor copy of html-scanner to support better xml parsing
+
+* Added :popup option to UrlHelper#link_to #1996 *gabriel.gironda@gmail.com*. Examples:
+
+ link_to "Help", { :action => "help" }, :popup => true
+ link_to "Busy loop", { :action => "busy" }, :popup => ['new_window', 'height=300,width=600']
+
+* Drop trailing \000 if present on RAW_POST_DATA (works around bug in Safari Ajax implementation) #918
+
+* Fix observe_field to fall back to event-based observation if frequency <= 0 #1916 *Michael Schubert*
+
+* Allow use of the :with option for submit_to_remote #1936 *jon@instance-design.co.uk*
+
+* AbstractRequest#domain returns nil when host is an ip address #2012 *Kevin Clark*
+
+* ActionController documentation update #2051 *François Beausoleil*
+
+* Yield @content_for_ variables to templates #2058 *Sam Stephenson*
+
+* Make rendering an empty partial collection behave like :nothing => true #2080 *Sam Stephenson*
+
+* Add option to specify the singular name used by pagination.
+
+* Use string key to obtain action value. Allows indifferent hashes to be disabled.
+
+* Added ActionView::Base.cache_template_loading back.
+
+* Rewrote compiled templates to decrease code complexity. Removed template load caching in favour of compiled caching. Fixed template error messages. *Nicholas Seckar*
+
+* Fix Routing to handle :some_param => nil better. *Nicholas Seckar, Luminas*
+
+* Add support for :include with pagination (subject to existing constraints for :include with :limit and :offset) #1478 *Michael Schubert*
+
+* Prevent the benchmark module from blowing up if a non-HTTP/1.1 request is processed
+
+* Added :use_short_month option to select_month helper to show month names as abbreviations
+
+* Make link_to escape the javascript in the confirm option #1964 *nicolas.pouillard@gmail.com*
+
+* Make assert_redirected_to properly check URL's passed as strings #1910 *Scott Barron*
+
+* Make sure :layout => false is always used when rendering inside a layout
+
+* Use raise instead of assert_not_nil in Test::Unit::TestCase#process to ensure that the test variables (controller, request, response) have been set
+
+* Make sure assigns are built for every request when testing #1866
+
+* Allow remote_addr to be queried on TestRequest #1668
+
+* Fixed bug when a partial render was passing a local with the same name as the partial
+
+* Improved performance of test app req/sec with ~10% refactoring the render method #1823 *Stefan Kaes*
+
+* Improved performance of test app req/sec with 5-30% through a series of Action Pack optimizations #1811 *Stefan Kaes*
+
+* Changed caching/expiration/hit to report using the DEBUG log level and errors to use the ERROR log level instead of both using INFO
+
+* Added support for per-action session management #1763
+
+* Improved rendering speed on complicated templates by up to 100% (the more complex the templates, the higher the speedup) #1234 *Stefan Kaes*. This did necessasitate a change to the internals of ActionView#render_template that now has four parameters. Developers of custom view handlers (like Amrita) need to update for that.
+
+* Added options hash as third argument to FormHelper#input, so you can do input('person', 'zip', :size=>10) #1719 *jeremye@bsa.ca.gov*
+
+* Added Base#expires_in(seconds)/Base#expires_now to control HTTP content cache headers #1755 *Thomas Fuchs*
+
+* Fixed line number reporting for Builder template errors #1753 *piotr*
+
+* Fixed assert_routing so that testing controllers in modules works as expected *Nicholas Seckar, Rick Olson*
+
+* Fixed bug with :success/:failure callbacks for the JavaScriptHelper methods #1730 *court3nay/Thomas Fuchs*
+
+* Added named_route method to RouteSet instances so that RouteSet instance methods do not prevent certain names from being used. *Nicholas Seckar*
+
+* Fixed routes so that routes which do not specify :action in the path or in the requirements have a default of :action => 'index', In addition, fixed url generation so that :action => 'index' does not need to be provided for such urls. *Nicholas Seckar, Markjuh*
+
+* Worked around a Safari bug where it wouldn't pass headers through if the response was zero length by having render :nothing return ' ' instead of ''
+
+* Fixed Request#subdomains to handle "foo.foo.com" correctly
+
+
+## 1.9.1 (11 July, 2005) ##
+
+* Fixed that auto_complete_for didn't force the input string to lower case even as the db comparison was
+
+* Fixed that Action View should always use the included Builder, never attempt to require the gem, to ensure compatibility
+
+* Added that nil options are not included in tags, so tag("p", :ignore => nil) now returns <p /> not <p ignore="" /> but that tag("p", :ignore => "") still includes it #1465 *Michael Schuerig*
+
+* Fixed that UrlHelper#link_to_unless/link_to_if used html_escape on the name if no link was to be applied. This is unnecessary and breaks its use with images #1649 *joergd@pobox.com*
+
+* Improved error message for DoubleRenderError
+
+* Fixed routing to allow for testing of *path components #1650 *Nicholas Seckar*
+
+* Added :handle as an option to sortable_element to restrict the drag handle to a given class #1642 *thejohnny*
+
+* Added a bunch of script.aculo.us features #1644, #1677, #1695 *Thomas Fuchs*
+ * Effect.ScrollTo, to smoothly scroll the page to an element
+ * Better Firefox flickering handling on SlideUp/SlideDown
+ * Removed a possible memory leak in IE with draggables
+ * Added support for cancelling dragging my hitting ESC
+ * Added capability to remove draggables/droppables and redeclare sortables in dragdrop.js (this makes it possible to call sortable_element on the same element more than once, e.g. in AJAX returns that modify the sortable element. all current sortable 'stuff' on the element will be discarded and the sortable will be rebuilt)
+ * Always reset background color on Effect.Highlight; this make change backwards-compatibility, to be sure include style="background-color:(target-color)" on your elements or else elements will fall back to their CSS rules (which is a good thing in most circumstances)
+ * Removed circular references from element to prevent memory leaks (still not completely gone in IE)
+ * Changes to class extension in effects.js
+ * Make Effect.Highlight restore any previously set background color when finishing (makes effect work with CSS classes that set a background color)
+ * Fixed myriads of memory leaks in IE and Gecko-based browsers *David Zülke*
+ * Added incremental and local autocompleting and loads of documentation to controls.js *Ivan Krstic*
+ * Extended the auto_complete_field helper to accept tokens option
+ * Changed object extension mechanism to favor Object.extend to make script.aculo.us easily adaptable to support 3rd party libs like IE7.js *David Zülke*
+
+* Fixed that named routes didn't use the default values for action and possible other parameters #1534 *Nicholas Seckar*
+
+* Fixed JavascriptHelper#visual_effect to use camelize such that :blind_up will work #1639 *pelletierm@eastmedia.net*
+
+* Fixed that a SessionRestoreError was thrown if a model object was placed in the session that wasn't available to all controllers. This means that it's no longer necessary to use the 'model :post' work-around in ApplicationController to have a Post model in your session.
+
+
+## 1.9.0 (6 July, 2005) ##
+
+* Added logging of the request URI in the benchmark statement (makes it easy to grep for slow actions)
+
+* Added javascript_include_tag :defaults shortcut that'll include all the default javascripts included with Action Pack (prototype, effects, controls, dragdrop)
+
+* Cache several controller variables that are expensive to calculate #1229 *Stefan Kaes*
+
+* The session class backing CGI::Session::ActiveRecordStore may be replaced with any class that duck-types with a subset of Active Record. See docs for details #1238 *Stefan Kaes*
+
+* Fixed that hashes was not working properly when passed by GET to lighttpd #849 *Nicholas Seckar*
+
+* Fixed assert_template nil will be true when no template was rendered #1565 *maceywj@telus.net*
+
+* Added :prompt option to FormOptions#select (and the users of it, like FormOptions#select_country etc) to create "Please select" style descriptors #1181 *Michael Schuerig*
+
+* Added JavascriptHelper#update_element_function, which returns a Javascript function (or expression) that'll update a DOM element according to the options passed #933 *mortonda@dgrmm.net*. Examples:
+
+ <%= update_element_function("products", :action => :insert, :position => :bottom, :content => "<p>New product!</p>") %>
+
+ <% update_element_function("products", :action => :replace, :binding => binding) do %>
+ <p>Product 1</p>
+ <p>Product 2</p>
+ <% end %>
+
+* Added :field_name option to DateHelper#select_(year|month|day) to deviate from the year/month/day defaults #1266 *Marcel Molina Jr.*
+
+* Added JavascriptHelper#draggable_element and JavascriptHelper#drop_receiving_element to facilitate easy dragging and dropping through the script.aculo.us libraries #1578 *Thomas Fuchs*
+
+* Added that UrlHelper#mail_to will now also encode the default link title #749 *f.svehla@gmail.com*
+
+* Removed the default option of wrap=virtual on FormHelper#text_area to ensure XHTML compatibility #1300 *thomas@columbus.rr.com*
+
+* Adds the ability to include XML CDATA tags using Builder #1563 *Josh Knowles*. Example:
+
+ xml.cdata! "some text" # => <![CDATA[some text]]>
+
+* Added evaluation of SCRIPT blocks in content returned to Ajax calls #1577 *Thomas Fuchs/court3nay/Sean Treadway*
+
+* Directly generate paths with a leading slash instead of tacking it on later. #1543 *Nicholas Seckar*
+
+* Fixed errant parameter modification in functional tests. #1542 *Nicholas Seckar*
+
+* Routes fail with leading slash #1540 *Nicholas Seckar*
+
+* Added support for graceful error handling of Ajax calls #1217 *Jamis Buck/Thomas Fuchs*. Example:
+
+ link_to_remote(
+ "test",
+ :url => { :action => "faulty" },
+ :update => { :success => "good", :failure => "bad" },
+ 403 => "alert('Forbidden- got ya!')",
+ 404 => "alert('Nothing there...?')",
+ :failure => "alert('Unkown error ' + request.status)")
+
+* Attempt to explicitly flush the output at the end of CgiProcess#out
+
+* Fixed assert_redirected_to to handle absolute controller paths properly #1472 *Rick Olson/Nicholas Seckar*
+
+* Added event-based observations when frequency is not set on observe_field/form #1474 *flash@vanklinkenbergsoftware.nl*
+
+* Added script.aculo.us Javascripts (controls.js, dragdrop.js, effects.js) (NEEDS MORE DESCRIPTION) #1509 *Thomas Fuchs*
+
+* Fixed prototype to consider all fields it doesn't know as text (such as Safari's search) just like the browser in its serialization #1497 *Sean Treadway*
+
+* Improved performance of Routes generation by a factor of 5 #1434 *Nicholas Seckar*
+
+* Added named routes (NEEDS BETTER DESCRIPTION) #1434 *Nicholas Seckar*
+
+* Improved AbstractRequest documentation #1483 *court3nay*
+
+* Added ActionController::Base.allow_concurrency to control whether the application is thread-safe, so multi-threaded servers like WEBrick knows whether to apply a mutex around the performance of each action. Turned off by default. EXPERIMENTAL FEATURE.
+
+* Added TextHelper#word_wrap(text, line_length = 80) #1449 *tuxie@dekadance.se*
+
+* Added a fall-through action for form_remote_tag that'll be used in case Javascript is unavailable #1459 *Scott Barron*. Example:
+
+ form_remote_tag :html => { :action => url_for(:controller => "some", :action => "place") }
+
+* Added :xhr => true/false option to verify so you can ensure that a request is coming from an Ajax call or not #1464 *Thomas Fuchs*
+
+* Added tag_options as a third parameter to AssetHelper#auto_discovery_link_tag to control options like the title of the link #1430 *Kevin Clark*
+
+* Added option to pass in parameters to CaptureHelper#capture, so you can create more advanced view helper methods #1466 *duane.johnson@gmail.com*. Example:
+
+ <% show_calendar(:year => 2005, :month => 6) do |day, options| %>
+ <% options[:bgcolor] = '#dfd' if 10..15.include? day %>
+ [<%= day %>]
+ <% end %>
+
+* Changed the default name of the input tag generated by FormTagHelper#submit_tag from "submit" to "commit" so it doesn't clash with form.submit() calls in Javascript #1271
+
+* Fixed relative urls support for lighttpd #1048 *Nicholas Seckar/maznawak@nerim.net*
+
+* Correct distance_of_time_in_words for integer arguments and make the second arg optional, treating the first arg as a duration in seconds. #1458 *madrobby <thomas@fesch.at>*
+
+* Fixed query parser to deal gracefully with equal signs inside keys and values #1345 *gorou*.
+ Example: /?sig=abcdef=:foobar=&x=y will pass now.
+
+* Added Cuba to country list #1351 *todd*
+
+* Fixed radio_button to work with numeric values #1352 *demetrius*
+
+* Added :extension option to NumberHelper#number_to_phone #1361 *delynnb*
+
+* Added button_to as a form-based solution to deal with harmful actions that should be hidden behind POSTs. This makes it just as easy as link_to to create a safe trigger for actions like destroy, although it's limited by being a block element, the fixed look, and a no-no inside other forms. #1371 *tom@moertel.com*
+
+* Fixed image_tag so an exception is not thrown just because the image is missing and alt value can't be generated #1395 *Marcel Molina Jr.*
+
+* Added a third parameter to TextHelper#auto_link called href_options for specifying additional tag options on the links generated #1401 *tyler.kovacs@gmail.com*. Example: auto_link(text, :all, { :target => "_blank" }) to have all the generated links open in a new window.
+
+* Fixed TextHelper#highlight to return the text, not nil, if the phrase is blank #1409 *Patrick Lenz*
+
+* Fixed TagHelper such that :name and 'name' keys in the options doesn't result in two attributes #1455 *take_tk*
+
+* Ensure that helpers are only available to the controllers where they are defined and their subclasses. #1394 *kdole@tamu.edu*
+
+* render("foo/bar") works with a layout again
+
+* Fixed double-singularization on scaffolded pagination call (Address would be turned into Addres) #1216, #1404 *nilsga*
+
+* Removed the require hack used by functional testing to work around an earlier bug in rake.
+
+* Allow distance_of_time_in_words to work with any value that responds to #to_time (like dates) #969
+
+* Support :render option for :verify #1440 *Tobias Lütke*
+
+* Updated vendor copy of html-scanner lib to 0.5.2, for bug fixes and optimizations. The :content option may be used as expected--to find a tag whose textual content is a particular value--in assert_tag, now.
+
+* Changed test requests to come from 0.0.0.0 instead of 127.0.0.1 such that they don't trigger debugging screens on exceptions, but instead call rescue_action_in_public
+
+* Modernize scaffolding to match the generator: use the new render method and change style from the warty @params["id"] to the sleek params[:id]. #1367
+
+* Include :id in the action generated by the form helper method. Then, for example, the controller can do Model.find(params[:id]) for both edit and update actions. Updated scaffolding to take advantage. #1367
+
+* Add assertions with friendly messages to TestCase#process to ensure that @controller, @request, and @response are set. #1367
+
+* Arrays, hashes sent via multipart posts are converted to strings #1032 *dj@omelia.org, me@julik.nl*
+
+* render(:layout => true) is a synonym for render(:layout => nil)
+
+* Make sure the benchmarking render method always returns the output of the render.
+
+* render(:action), render(:template) and render() are the only three calls that default to using a layout. All other render calls assume :layout => false. This also fixes send_file, which was applying a layout if one existed for the current action.
+
+* verify with :redirect_to won't redirect if a redirect or render has already been performed #1350
+
+* render(:partial => true) is identical to the behavior of the deprecated render_partial()
+
+* Fixed render(:partial => "...") to use an empty Hash for the local assigns #1365
+
+* Fixed Caching::Fragments::FileStore.delete to not raise an exception if the delete fails.
+
+* Deprecated all render_* methods in favor of consolidating all rendering behavior in Base#render(options). This enables more natural use of combining options, such as layouts. Examples:
+
+ +---------------------------------------------------------------+-------------------------------------------------------+
+ | BEFORE | AFTER |
+ +---------------------------------------------------------------+-------------------------------------------------------+
+ | render_with_layout "weblog/show", "200 OK", "layouts/dialog" | render :action => "show", :layout => "dialog" |
+ | render_without_layout "weblog/show" | render :action => "show", :layout => false |
+ | render_action "error", "404 Not Found" | render :action => "error", :status => "404 Not Found" |
+ | render_template "xml.div('stuff')", "200 OK", :rxml | render :inline => "xml.div('stuff')", :type => :rxml |
+ | render_text "hello world!" | render :text => "hello world!" |
+ | render_partial_collection "person", @people, nil, :a => 1 | render :partial => "person", :collection => @people, |
+ | | :locals => { :a => 1 } |
+ +---------------------------------------------------------------+-------------------------------------------------------+
+
+* Deprecated redirect_to_path and redirect_to_url in favor of letting redirect_to do the right thing when passed either a path or url.
+
+* Fixed use of an integer as return code for renders, so render_text "hello world", 404 now works #1327
+
+* Fixed assert_redirect_to to work with redirect_to_path #869 *Nicholas Seckar*
+
+* Fixed escaping of :method option in remote_form_tag #1218 *Rick Olson*
+
+* Added Serbia and Montenegro to the country_select #1239 *todd@robotcoop.com*
+
+* Fixed Request#remote_ip in testing #1251 *Jeremy Kemper*
+
+* Fixed that compute_public_path should recognize external URLs, so image_tag("http://www.example.com/images/icon.gif") is not prefixed with the relative url path #1254 *victor-ronr-trac@carotena.net*
+
+* Added support for descending year values in DateHelper#select_year, like select_year(Date.today, :start_year => 2005, :end_year => 1900), which would count down from 2005 to 1900 instead of the other way #1274 *nwoods@mail.com*
+
+* Fixed that FormHelper#checkbox should return a checked checkbox if the value is the same as checked_value #1286 *Florian Weber*
+
+* Fixed Form.disable in Prototype #1317 *Wintermute*
+
+* Added accessors to logger, params, response, session, flash, and headers from the view, so you can write <% logger.info "stuff" %> instead of <% @logger.info "others" %> -- more consistent with the preferred way of accessing these attributes and collections from the controller
+
+* Added support for POST data in form of YAML or XML, which is controller through the Content-Type header. Example request:
+
+ Content-Type: application/xml
+ <request><item><content>HelloWorld</content></item></request>
+
+ ...is the same as:
+
+ Content-Type: application/x-yaml
+ ---
+ item:
+ content: HelloWorld
+
+ ...is the same as:
+
+ item[content]=HelloWorld
+
+ Which in the end turns into { "item" => { "content" => "HelloWorld" } }. This makes it a lot easier to publish REST web services on top of your regular actions (as they won't care).
+
+ Example Curl call:
+
+ curl -H 'Content-Type: application/xml' -d '<request><item><content>KillMeMore</content></item></request>' http://www.example.com/service
+
+ You can query to find out whether a given request came through as one of these types with:
+ - request.post_format? (:url_encoded, :xml or :yaml)
+ - request.formatted_post? (for either xml or yaml)
+ - request.xml_post?
+ - request.yaml_post?
+
+* Added bundling of XmlSimple by Maik Schmidt
+
+* Fixed that render_partial_collection should always return a string (and not sometimes an array, despite <%= %> not caring)
+
+* Added TextHelper#sanitize that can will remove any Javascript handlers, blocks, and forms from an input of HTML. This allows for use of HTML on public sites, but still be free of XSS issues. #1277 *Jamis Buck*
+
+* Fixed the HTML scanner used by assert_tag where a infinite loop could be caused by a stray less-than sign in the input #1270 *Jamis Buck*
+
+* Added functionality to assert_tag, so you can now do tests on the siblings of a node, to assert that some element comes before or after the element in question, or just to assert that some element exists as a sibling #1226 *Jamis Buck*
+
+* Added better error handling for regexp caching expiration
+
+* Fixed handling of requests coming from unknown HTTP methods not to kill the server
+
+* Added that both AssetHelper#stylesheet_link_tag and AssetHelper#javascript_include_tag now accept an option hash as the last parameter, so you can do stuff like: stylesheet_link_tag "style", :media => "all"
+
+* Added FormTagHelper#image_submit_tag for making submit buttons that uses images
+
+* Added ActionController::Base.asset_host that will then be used by all the asset helpers. This enables you to easily offload static content like javascripts and images to a separate server tuned just for that.
+
+* Fixed action/fragment caching using the filestore when a directory and a file wanted to use the same name. Now there's a .cache prefix that sidesteps the conflict #1188 *imbcmdth@hotmail.com*
+
+* Fixed missing id uniqueness in FormTag#radio_button #1207 *Jarkko Laine*
+
+* Fixed assert_redirected_to to work with :only_path => false #1204 *Alisdair McDiarmid*
+
+* Fixed render_partial_collection to output an empty string instead of nil when handed an empty array #1202 *Ryan Carver*
+
+* Improved the speed of regular expression expirations for caching by a factor of 10 #1221 *Jamis Buck*
+
+* Removed dumping of template assigns on the rescue page as it would very easily include a ton of data making page loads take seconds (and the information was rarely helpful) #1222
+
+* Added BenchmarkHelper that can measure the execution time of a block in a template and reports the result to the log. Example:
+
+ <% benchmark "Notes section" do %>
+ <%= expensive_notes_operation %>
+ <% end %>
+
+ Will add something like "Notes section (0.345234)" to the log.
+
+* Added ActionController::Caching::Sweeper as an improved an easier to use sweeper. The new sweepers work on a single-step approach instead of two-steps like the old ones. Before
+
+ def after_save(record)
+ @list = record.is_a?(List) ? record : record.list
+ end
+
+ def filter(controller)
+ controller.expire_page(:controller => "lists", :action => %w( show public feed ), :id => @list.id)
+ controller.expire_action(:controller => "lists", :action => "all")
+ @list.shares.each { |share| controller.expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
+ end
+
+ ..after:
+
+ def after_save(record)
+ list = record.is_a?(List) ? record : record.list
+ expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
+ expire_action(:controller => "lists", :action => "all")
+ list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
+ end
+
+ The new sweepers can also observe on the actions themselves by implementing methods according to (before|after)_$controller_$action. Example of a callback that'll be called after PagesController#update_title has been performed:
+
+ def after_pages_update_title
+ expire_fragment(%r{pages/#{controller.assigns["page"].id}/.*})
+ end
+
+ Note that missing_method is delegated to the controller instance, which is assigned in a before filter. This means that you can call expire_fragment instead of @controller.expire_fragment.
+
+* Added that Fragments#expire_fragment now accepts as a regular expression as the name thereby deprecating expire_matched_fragments
+
+* Fixed that fragments shouldn't use the current host and the path as part of the key like pages does
+
+* Added conditions to around_filters just like before_filter and after_filter
+
+
+## 1.8.1 (20th April, 2005) ##
+
+* Added xml_http_request/xhr method for simulating XMLHttpRequest in functional tests #1151 *Sam Stephenson*. Example:
+
+ xhr :post, :index
+
+* Fixed that Ajax.Base.options.asynchronous wasn't being respected in Ajax.Request (thanks Jon Casey)
+
+* Fixed that :get, :post, and the others should take a flash array as the third argument just like process #1144 *rails@cogentdude.com*
+
+* Fixed a problem with Flash.now
+
+* Fixed stringification on all assigned hashes. The sacrifice is that assigns[:person] won't work in testing. Instead assigns["person"] or assigns(:person) must be used. In other words, the keys of assigns stay strings but we've added a method-based accessor to appease the need for symbols.
+
+* Fixed that rendering a template would require a connection to the database #1146
+
+
+## 1.8.0 (19th April, 2005) ##
+
+* Added assert_tag and assert_no_tag as a much improved alternative to the deprecated assert_template_xpath_match #1126 *Jamis Buck*
+
+* Deprecated the majority of all the testing assertions and replaced them with a much smaller core and access to all the collections the old assertions relied on. That way the regular test/unit assertions can be used against these. Added documentation about how to use it all.
+
+* Added a wide range of new Javascript effects:
+ * Effect.Puff zooms the element out and makes it smoothly transparent at the same time, giving a "puff" illusion #996 *thomas@fesch.at*
+ After the animation is completed, the display property will be set to none.
+ This effect will work on relative and absolute positioned elements.
+
+ * Effect.Appear as the opposite of Effect.Fade #990 *thomas@fesch.at*
+ You should return elements with style="display:none;" or a like class for this to work best and have no chance of flicker.
+
+ * Effect.Squish for scaling down an element and making it disappear at the end #972 *thomas@fesch.at*
+
+ * Effect.Scale for smoothly scaling images or text up and down #972 *thomas@fesch.at*
+
+ * Effect.Fade which smoothly turns opacity from 100 to 0 and then hides the element #960 *thomas@fesch.at*
+
+* Added Request#xml_http_request? (and an alias xhr?) to that'll return true when the request came from one of the Javascript helper methods (Ajax). This can be used to give one behavior for modern browsers supporting Ajax, another to old browsers #1127 *Sam Stephenson*
+
+* Changed render_partial to take local assigns as the second parameter instead of an explicit object and then the assigns. So the API changes from:
+
+ <%= render_partial "account", person, "rules" => regulations.rules %>
+
+ ...to:
+
+ <%= render_partial "account", :account => person, :rules => regulations.rules %>
+
+ The old API will still work, though, and render_partial "account" will still assume :account => @account.
+
+* Added support for web servers that use PATH_INFO instead of REQUEST_URI like IIS #1014 *BradG/Nicholas Seckar*
+
+* Added graceful handling of PUT, DELETE, and OPTIONS requests for a complete coverage of REST functionality #1136 *Josh Knowles*
+
+* Fixed that you can now pass an alternative :href option to link_to_function/remote in order to point to somewhere other than # if the javascript fails or is turned off. You can do the same with form_remote_tag by passing in :action. #1113 *Sam Stephenson*
+
+* Fixed DateHelper to return values on the option tags such that they'll work properly in IE with form_remote_tag #1024 *Scott Raymond*
+
+* Fixed FormTagHelper#check_box to respect checked #1049 *DelynnB*
+
+* Added that render_partial called from a controller will use the action name as default #828 *Dan Peterson*
+
+* Added Element.toggle, Element.show, and Element.hide to the prototype javascript library. Toggle.display has been deprecated, but will still work #992 *Lucas Carlson*
+
+* Added that deleting a cookie should not just set it to an empty string but also instantly expire it #1118 *todd@robotcoop.com*
+
+* Added AssetTagHelper#image_path, AssetTagHelper#javascript_path, and AssetTagHelper#stylesheet_path #1110 *Larry Halff*
+
+* Fixed url_for(nil) in functional tests #1116 *Alisdair McDiarmid*
+
+* Fixed error handling of broken layouts #1115 *Michael Schubert*
+
+* Added submit_to_remote that allows you to trigger an Ajax form submition at the click of the submission button, which allows for multiple targets in a single form through the use of multiple submit buttons #930 *yrashk@gmail.com*
+
+* Fixed pagination to work with joins #1034 *scott@sigkill.org*
+
+* Fixed that *rest parameter in map.connect couldn't accept an empty list #1037 *Dee Zsombor*
+
+* Added :confirm option to link_to_remote just like link_to has #1082 *yrashk@fp.org.ua*
+
+* Added minute_step as an option to select_minute (and the helpers that use it) to jump in larger increments than just 1 minute. At 15, it would return 0, 15, 30, 45 options #1085 *ordwaye@evergreen.edu*
+
+* Fixed that an exception would be thrown when an empty form was submitted #1090 *jan@ulbrich-boerwang.de*
+
+* Moved TextHelper#human_size to NumberHelper#number_to_human_size, but kept an deprecated alias to the old method name
+
+* Fixed that the content-type for some browsers could include an additional \r which made wonky things happen #1067 *Thomas Fuchs*
+
+* Fixed that radio buttons shouldn't have a default size attribute #1074 *hendrik@mans.de*
+
+* Added ActionView::Helpers::InstanceTag::DEFAULT_RADIO_OPTIONS that contains a hash of default options for radio buttons #1074 *hendrik@mans.de*
+
+* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 *Nicholas Seckar*
+
+* Added JavascriptHelper#periodically_call_remote in order to create areas of a page that update automatically at a set interval #945 *Jon Tirsen*
+
+* Fixed Cache#expire_matched_fragments that couldn't recognize the difference between string and url_for options #1030 *Stefan Kaes*
+
+* Added simulation of @request.request_uri in functional tests #1038 *Jamis Buck*
+
+* Fixed autolinking to work better in more cases #1013 *Jamis Buck*
+
+* Added the possible of using symbols in form helpers that relate to instance variables like text_field :account, :name in addition to text_field "account", "name"'
+
+* Fixed javascript_include_tag to output type instead of language and conform to XHTML #1018 *Rick Olson*
+
+* Added NumberHelper for common string representations like phone number, currency, and percentage #1015 *DeLynn*
+
+* Added pagination for scaffolding (10 items per page) #964 *mortonda@dgrmm.net*
+
+* Added assert_no_cookie and fixed assert_cookie_equal to deal with non-existing cookies #979 *Jeremy Kemper*
+
+* Fixed :overwrite_param so it doesn't delete but reject elements from @request.parameters #982 *raphinou@yahoo.com*
+
+* Added :method option to verify for ensuring that either GET, POST, etc is allowed #984 *Jamis Buck*
+
+* Added options to set cc, bcc, subject, and body for UrlHelper#mail_to #966 *DeLynn*
+
+* Fixed include_blank for select_hour/minute/second #527 *edward@debian.org*
+
+* Improved the message display on the exception handler pages #963 *Johan Sorensen*
+
+* Fixed that on very rare occasions, webrick would raise a NoMethodError: private method 'split' called for nil #1001 *Flurin Egger*
+
+* Fixed problem with page caching #958 *Rick Olson*
+
+
+## 1.7.0 (27th March, 2005) ##
+
+* Added ActionController::Base.page_cache_extension for setting the page cache file extension (the default is .html) #903 *Andreas*
+
+* Fixed "bad environment variable value" exception caused by Safari, Apache, and Ajax calls #918
+
+* Fixed that pagination_helper would ignore :params #947 *Sebastian Kanthak*
+
+* Added :owerwrite_params back to url_for and friends -- it was AWL since the introduction of Routes #921 *raphinou*
+
+* Added :position option to link_to_remote/form_remote_tag that can be either :before, :top, :bottom, or :after and specifies where the return from the method should be inserted #952 *Matthew McCray/Sam Stephenson*
+
+* Added Effect.Highlight to prototype.js to do Yellow Fade Technique (of 37signals' fame) on any container #952 *Sam Stephenson/court3nay*
+
+* Added include_seconds option as the third parameter to distance_of_time_in_words which will render "less than a minute" in higher resolution ("less than 10 seconds" etc) #944 *thomas@fesch.at*
+
+* Added fourth option to process in test cases to specify the content of the flash #949 *Jamis Buck*
+
+* Added Verifications that allows you to specify preconditions to actions in form of statements like <tt>verify :only => :update_post, :params => "admin_privileges", :redirect_to => { :action => "settings" }</tt>, which ensure that the update_post action is only called if admin_privileges is available as a parameter -- otherwise the user is redirected to settings. #897 *Jamis Buck*
+
+* Fixed Form.Serialize for the JavascriptHelper to also seriliaze password fields #934 *dweitzman@gmail.com*
+
+* Added JavascriptHelper#escape_javascript as a public method (was private) and made it escape both single and double quotes and new lines #940 *mortonda@dgrmm.net*
+
+* Added trailing_slash option to url_for, so you can generate urls ending in a slash. Note that is currently not recommended unless you need it for special reasons since it breaks caching #937 *stian@grytoyr.net*
+
+* Added expire_matched_fragments(regular_expression) to clear out a lot of fragment caches at once #927 *Rick Olson*
+
+* Fixed the problems with : and ? in file names for fragment caches on Windows #927 *Rick Olson*
+
+* Added TextHelper#human_size for formatting file sizes, like human_size(1234567) => 1.2 MB #943 *thomas@fesch.at*
+
+* Fixed link_to :confirm #936 *Nicholas Seckar*
+
+* Improved error reporting especially around never shallowing exceptions. Debugging helpers should be much easier now #980 *Nicholas Seckar*
+
+* Fixed Toggle.display in prototype.js #902 *Lucas Carlson*
+
+
+## 1.6.0 (22th March, 2005) ##
+
+* Added a JavascriptHelper and accompanying prototype.js library that opens the world of Ajax to Action Pack with a large array of options for dynamically interacting with an application without reloading the page #884 *Sam Stephenson/David*
+
+* Added pagination support through both a controller and helper add-on #817 *Sam Stephenson*
+
+* Fixed routing and helpers to make Rails work on non-vhost setups #826 *Nicholas Seckar/Tobias Lütke*
+
+* Added a much improved Flash module that allows for finer-grained control on expiration and allows you to flash the current action #839 *Caio Chassot*. Example of flash.now:
+
+ class SomethingController < ApplicationController
+ def save
+ ...
+ if @something.save
+ # will redirect, use flash
+ flash[:message] = 'Save successful'
+ redirect_to :action => 'list'
+ else
+ # no redirect, message is for current action, use flash.now
+ flash.now[:message] = 'Save failed, review'
+ render_action 'edit'
+ end
+ end
+ end
+
+* Added to_param call for parameters when composing an url using url_for from something else than strings #812 *Sam Stephenson*. Example:
+
+ class Page
+   def initialize(number)
+     @number = number
+   end
+   # ...
+   def to_param
+     @number.to_s
+   end
+ end
+
+ You can now use instances of Page with url_for:
+
+ class BarController < ApplicationController
+   def baz
+     page = Page.new(4)
+     url = url_for :page => page # => "http://foo/bar/baz?page=4"
+   end
+ end
+
+* Fixed form helpers to query Model#id_before_type_cast instead of Model#id as a temporary workaround for Ruby 1.8.2 warnings #818 *DeLynn B*
+
+* Fixed TextHelper#markdown to use blank? instead of empty? so it can deal with nil strings passed #814 *Johan Sörensen*
+
+* Added TextHelper#simple_format as a non-dependency text presentation helper #814 *Johan Sörensen*
+
+* Added that the html options disabled, readonly, and multiple can all be treated as booleans. So specifying <tt>disabled => :true</tt> will give <tt>disabled="disabled"</tt>. #809 *mindel*
+
+* Added path collection syntax for Routes that will gobble up the rest of the url and pass it on to the controller #830 *rayners*. Example:
+
+ map.connect 'categories/*path_info', :controller => 'categories', :action => 'show'
+
+ A request for /categories/top-level-cat, would give @params[:path_info] with "top-level-cat".
+ A request for /categories/top-level-cat/level-1-cat, would give @params[:path_info] with "top-level-cat/level-1-cat" and so forth.
+
+ The @params[:path_info] return is really an array, but where to_s has been overwritten to do join("/").
+
+* Fixed options_for_select on selected line issue #624 *Florian Weber*
+
+* Added CaptureHelper with CaptureHelper#capture and CaptureHelper#content_for. See documentation in helper #837 *Tobias Lütke*
+
+* Fixed :anchor use in url_for #821 *Nicholas Seckar*
+
+* Removed the reliance on PATH_INFO as it was causing problems for caching and inhibited the new non-vhost support #822 *Nicholas Seckar*
+
+* Added assigns shortcut for @response.template.assigns to controller test cases *Jeremy Kemper*. Example:
+
+ Before:
+
+ def test_list
+ assert_equal 5, @response.template.assigns['recipes'].size
+ assert_equal 8, @response.template.assigns['categories'].size
+ end
+
+ After:
+
+ def test_list
+ assert_equal 5, assigns(:recipes).size
+ assert_equal 8, assigns(:categories).size
+ end
+
+* Added TagHelper#image_tag and deprecated UrlHelper#link_image_to (recommended approach is to combine image_tag and link_to instead)
+
+* Fixed textilize to be resilient to getting nil parsed (by using Object#blank? instead of String#empty?)
+
+* Fixed that the :multipart option in FormTagHelper#form_tag would be ignored *Yonatan Feldman*
+
+
+## 1.5.1 (7th March, 2005) ##
+
+* Fixed that the routes.rb file wouldn't be found on symlinked setups due to File.expand_path #793 *piotr@t-p-l.com*
+
+* Changed ActiveRecordStore to use Marshal instead of YAML as the latter proved troublesome in persisting circular dependencies. Updating existing applications MUST clear their existing session table from data to start using this updated store #739 *Jamis Buck*
+
+* Added shortcut :id assignment to render_component and friends (before you had to go through :params) #784 *Lucas Carlson*
+
+* Fixed that map.connect should convert arguments to strings #780 *Nicholas Seckar*
+
+* Added UrlHelper#link_to_if/link_to_unless to enable other conditions that just link_to_unless_current #757 *mindel*
+
+* Fixed that single quote was not escaped in a UrlHelper#link_to javascript confirm #549 *Scott Barron*
+
+* Removed the default border on link_image_to (it broke xhtml strict) -- can be specified with :border => 0 #517 *?/caleb*
+
+* Fixed that form helpers would treat string and symbol keys differently in html_options (and possibly create duplicate entries) #112 *Jeremy Kemper*
+
+* Fixed that broken pipe errors (clients disconnecting in mid-request) could bring down a fcgi process
+
+* Added the original exception message to session recall errors (so you can see which class wasnt required)
+
+* Fixed that RAILS_ROOT might not be defined when AP was loaded, so do a late initialization of the ROUTE_FILE #761 *Scott Barron*
+
+* Fix request.path_info and clear up LoadingModule behavior #754 *Nicholas Seckar*
+
+* Fixed caching to be aware of extensions (so you can cache files like api.wsdl or logo.png) #734 *Nicholas Seckar*
+
+* Fixed that Routes would raise NameErrors if a controller component contains characters that are not valid constant names #733 *Nicholas Seckar*
+
+* Added PATH_INFO access from the request that allows urls like the following to be interpreted by rails: http://www.example.com/dispatcher.cgi/controller/action -- that makes it possible to use rails as a CGI under lighttpd and would also allow (for example) Rublog to be ported to rails without breaking existing links to Rublog-powered blogs. #728 *Jamis Buck*
+
+* Fixed that caching the root would result in .html not index.html #731, #734 *alisdair/Nicholas Seckar*
+
+
+## 1.5.0 (24th February, 2005) ##
+
+* Added Routing as a replacement for mod_rewrite pretty urls [Nicholas Seckar]. Read more in ActionController::Base.url_for and on http://manuals.rubyonrails.com/read/book/9
+
+* Added components that allows you to call other actions for their rendered response while execution another action. You can either delegate the entire response rendering or you can mix a partial response in with your other content. Read more on http://manuals.rubyonrails.com/read/book/14
+
+*  Fixed that proxy IPs do not follow all RFC1918 nets #251 *caleb@aei-tech.com*
+
+* Added Base#render_to_string to parse a template and get the result back as a string #479
+
+* Fixed that send_file/data can work even if render* has been called before in action processing to render the content of a file to be send for example #601
+
+* Added FormOptionsHelper#time_zone_select and FormOptionsHelper#time_zone_options_for_select to work with the new value object TimeZone in Active Support #688 *Jamis Buck*
+
+* Added FormHelper#file_field and FormTagHelper#file_field_tag for creating file upload fields
+
+* Added :order option for date_select that allows control over the order in which the date dropdowns is used and which of them should be used #619 [Tim Bates]. Examples:
+
+ date_select("post", "written_on", :order => [:day, :month, :year])
+ date_select("user", "birthday", :order => [:month, :day])
+
+* Added ActionView::Base.register_template_handler for easy integration of an alternative template language to ERb and Builder. See test/controller/custom_handler_test.rb for a usage example #656 *Jamis Buck*
+
+* Added AssetTagHelper that provides methods for linking a HTML page together with other assets, such as javascripts, stylesheets, and feeds.
+
+* Added FormTagHelper that provides a number of methods for creating form tags that doesn't rely on conventions with an object assigned to the template like FormHelper does. With the FormTagHelper, you provide the names and values yourself.
+
+* Added Afghanistan, Iran, and Iraq to the countries list used by FormOptions#country_select and FormOptions#country_options_for_select
+
+* Renamed link_to_image to link_image_to (since thats what it actually does) -- kept alias for the old method name
+
+* Fixed textilize for RedCloth3 to keep doing hardbreaks
+
+* Fixed that assert_template_xpath_matches did not indicate when a path was not found #658 *Eric Hodel*
+
+* Added TextHelper#auto_link to turn email addresses and urls into ahrefs
+
+* Fixed that on validation errors, scaffold couldn't find template #654 *mindel*
+
+* Added Base#hide_action(*names) to hide public methods from a controller that would otherwise have been callable through the URL. For the majority of cases, its preferred just to make the methods you don't want to expose protected or private (so they'll automatically be hidden) -- but if you must have a public method, this is a way to make it uncallable. Base#hidden_actions retrieve the list of all hidden actions for the controller #644 *Nicholas Seckar*
+
+* Fixed that a bunch of methods from ActionController::Base was accessible as actions (callable through a URL) when they shouldn't have been #644 *Nicholas Seckar*
+
+* Added UrlHelper#current_page?(options) method to check if the url_for options passed corresponds to the current page
+
+* Fixed https handling on other ports than 443 *Alan Gano*
+
+* Added follow_redirect method for functional tests that'll get-request the redirect that was made. Example:
+
+ def test_create_post
+ post :create, "post" => { "title" => "Exciting!" }
+ assert_redirected_to :action => "show"
+
+ follow_redirect
+ assert_rendered_file "post/show"
+ end
+
+ Limitation: Only works for redirects to other actions within the same controller.
+
+* Fixed double requiring of models with the same name as the controller
+
+* Fixed that query params could be forced to nil on a POST due to the raw post fix #562 *moriq@moriq.com*
+
+* Fixed that cookies shouldn't be frozen in TestRequest #571 *Eric Hodel*
+
+
+## 1.4.0 (January 25th, 2005) ##
+
+* Fixed problems with ActiveRecordStore under the development environment in Rails
+
+* Fixed the ordering of attributes in the xml-decleration of Builder #540 *woeye*
+
+* Added @request.raw_post as a convenience access to @request.env['RAW_POST_DATA'] #534 *Tobias Lütke*
+
+* Added support for automatic id-based indexing for lists of items #532 [dblack]. Example:
+
+ <% @students.each do |@student| %>
+ <%= text_field "student[]", "first_name", :size => "20" %>
+ <%= text_field "student[]", "last_name" %>
+ <%= text_field "student[]", "grade", :size => "5" %>
+ <% end %>
+
+ ...would produce, for say David Black with id 123 and a grace of C+:
+
+ <input id="student_123_first_name" name="student[123][first_name]" size="20" size="30" type="text" value="David" />
+ <input id="student_123_last_name" name="student[123][last_name]" size="30" type="text" value="Black" />
+ <input id="student_123_grade" name="student[123][grade]" size="5" type="text" value="C+" />
+
+* Added :application_prefix to url_for and friends that makes it easier to setup Rails in non-vhost environments #516 *Jamis Buck*
+
+* Added :encode option to mail_to that'll allow you to masquarede the email address behind javascript or hex encoding #494 *Lucas Carlson*
+
+* Fixed that the content-header was being set to application/octet_stream instead of application/octet-stream on send_date/file *Alexey*
+
+* Removed the need for passing the binding when using CacheHelper#cache
+
+* Added TestResponse#binary_content that'll return as a string the data sent through send_data/send_file for testing #500 *Alexey*
+
+* Added @request.env['RAW_POST_DATA'] for people who need access to the data before Ruby's CGI has parsed it #505 *Jeremy Kemper*
+
+* Fixed that a default fragment store wan't being set to MemoryStore as intended.
+
+* Fixed that all redirect and render calls now return true, so you can use the pattern of "do and return". Example:
+
+ def show
+ redirect_to(:action => "login") and return unless @person.authenticated?
+ render_text "I won't happen unless the person is authenticated"
+ end
+
+* Added that renders and redirects called in before_filters will have the same effect as returning false: stopping the chain. Example:
+
+ class WeblogController
+ before_filter { |c| c.send(:redirect_to_url("http://www.farfaraway.com")}) }
+
+ def hello
+ render_text "I will never be called"
+ end
+ end
+
+
+* Added that only one render or redirect can happen per action. The first call wins and subsequent calls are ignored. Example:
+
+ def do_something
+ redirect_to :action => "elsewhere"
+ render_action "overthere"
+ end
+
+ Only the redirect happens. The rendering call is simply ignored.
+
+
+## 1.3.1 (January 18th, 2005) ##
+
+* Fixed a bug where cookies wouldn't be set if a symbol was used instead of a string as the key
+
+* Added assert_cookie_equal to assert the contents of a named cookie
+
+* Fixed bug in page caching that prevented it from working at all
+
+
+## 1.3.0 (January 17th, 2005) ##
+
+* Added an extensive caching module that offers three levels of granularity (page, action, fragment) and a variety of stores.
+ Read more in ActionController::Caching.
+
+* Added the option of passing a block to ActiveRecordHelper#form in order to add more to the auto-generated form #469 *dom@sisna.com*
+
+ form("entry", :action => "sign") do |form|
+ form << content_tag("b", "Department")
+ form << collection_select("department", "id", @departments, "id", "name")
+ end
+
+* Added arrays as a value option for params in url_for and friends #467 [Eric Anderson]. Example:
+
+ url_for(:controller => 'user', :action => 'delete', :params => { 'username' => %( paul john steve ) } )
+ # => /user/delete?username[]=paul&username[]=john&username[]=steve
+
+* Fixed that controller tests can now assert on the use of cookies #466 *Alexey*
+
+* Fixed that send_file would "remember" all the files sent by adding to the headers again and again #458 *Jeremy Kemper*
+
+* Fixed url rewriter confusion when the controller or action name was a substring of the controller_prefix or action_prefix
+
+* Added conditional layouts like <tt>layout "weblog_standard", :except => :rss</tt> #452 *Marcel Molina Jr.*
+
+* Fixed that MemCacheStore wasn't included by default and added default MemCache object pointing to localhost #447 *Lucas Carlson*
+
+* Added fourth argument to render_collection_of_partials that allows you to specify local_assigns -- just like render_partial #432 *Ryan Davis*
+
+* Fixed that host would choke when cgi.host returned nil #432 *Tobias Lütke*
+
+* Added that form helpers now take an index option #448 *Tim Bates*
+
+ Example:
+ text_field "person", "name", "index" => 3
+
+ Becomes:
+ <input type="text" name="person[3][name]" id="person_3_name" value="<%= @person.name %>" />
+
+* Fixed three issues with retrying breakpoints #417 *Florian Gross*
+
+ 1. Don't screw up pages that use multiple values for the same parameter (?foo=bar&foo=qux was converted to ?foo=barqux)
+ 2. Don't screw up all forms when you click the "Retry with Breakpoint" link multiple times instead of reloading
+ (This caused the parameters to be added multiple times for GET forms leading to trouble.)
+ 3. Don't add ?BP-RETRY=1 multiple times
+
+* Added that all renders and redirects now return false, so they can be used as the last line in before_filters to stop execution.
+
+ Before:
+ def authenticate
+ unless @session[:authenticated]
+ redirect_to :controller => "account", :action => "login"
+ return false
+ end
+ end
+
+ After:
+ def authenticate
+ redirect_to(:controller => "account", :action => "login") unless @session[:authenticated]
+ end
+
+* Added conditional filters #431 [Marcel Molina Jr.]. Example:
+
+ class JournalController < ActionController::Base
+ # only require authentication if the current action is edit or delete
+ before_filter :authorize, :only_on => [ :edit, :delete ]
+
+ private
+ def authorize
+ # redirect to login unless authenticated
+ end
+ end
+
+* Added Base#render_nothing as a cleaner way of doing render_text "" when you're not interested in returning anything but an empty response.
+
+* Added the possibility of passing nil to UrlHelper#link_to to use the link itself as the name
+
+
+## 1.2.0 (January 4th, 2005) ##
+
+* Added MemCacheStore for storing session data in Danga's MemCache system *Bob Cottrell*
+ Depends on: MemCached server (http://www.danga.com/memcached/), MemCache client (http://raa.ruby-lang.org/project/memcache/)
+
+* Added thread-safety to the DRbStore #66, #389 *Ben Stiglitz*
+
+* Added DateHelper#select_time and DateHelper#select_second #373 *Scott Baron*
+
+* Added :host and :protocol options to url_for and friends to redirect to another host and protocol than the current.
+
+* Added class declaration for the MissingFile exception #388 *Kent Sibilev*
+
+* Added "short hypertext note with a hyperlink to the new URI(s)" to redirects to fulfill compliance with RFC 2616 (HTTP/1.1) section 10.3.3 #397 *Tim Bates*
+
+* Added second boolean parameter to Base.redirect_to_url and Response#redirect to control whether the redirect is permanent or not (301 vs 302) #375 *Hodel*
+
+* Fixed redirects when the controller and action is named the same. Still haven't fixed same controller, module, and action, though #201 *Josh Peek*
+
+* Fixed problems with running multiple functional tests in Rails under 1.8.2 by including hack for test/unit weirdness
+
+* Fixed that @request.remote_ip didn't work in the test environment #369 *Bruno Mattarollo*
+
+
+## 1.1.0 ##
+
+* Added search through session to clear out association caches at the end of each request. This makes it possible to place Active Record objects
+ in the session without worrying about stale data in the associations (the main object is still subject to caching, naturally) #347 *Tobias Lütke*
+
+* Added more informative exception when using helper :some_helper and the helper requires another file that fails, you'll get an
+ error message tells you what file actually failed to load, rather than falling back on assuming it was the helper file itself #346 *dblack*
+
+* Added use of *_before_type_cast for all input and text fields. This is helpful for getting "100,000" back on a integer-based
+ validation where the value would normally be "100".
+
+* Added Request#port_string to get something like ":8080" back on 8080 and "" on 80 (or 443 with https).
+
+* Added Request#domain (returns string) and Request#subdomains (returns array).
+
+* Added POST support for the breakpoint retries, so form processing that raises an exception can be retried with the original request *Florian Gross*
+
+* Fixed regression with Base#reset_session that wouldn't use the DEFAULT_SESSION_OPTIONS *adam@the-kramers.net*
+
+* Fixed error rendering of rxml documents to not just swallow the exception and return 0 (still not guessing the right line, but hey)
+
+* Fixed that textilize and markdown would instantiate their engines even on empty strings. This also fixes #333 *Ulysses*
+
+* Fixed UrlHelper#link_to_unless so it doesn't care if the id is a string or fixnum *Ryan Davis*
+
+
+## 1.0.1 ##
+
+* Fixed a bug that would cause an ApplicationController to require itself three times and hence cause filters to be run three times *evl*
+
+
+## 1.0 ##
+
+* Added that controllers will now attempt to require a model dependency with their name and in a singular attempt for their name.
+ So both PostController and PostsController will automatically have the post.rb model required. If no model is found, no error is raised,
+ as it is then expected that no match is available and the programmer will have included his own models.
+
+* Fixed DateHelper#date_select so that you can pass include_blank as an option even if you don't use start_year and end_year #59 *what-a-day*
+
+* Added that controllers will now search for a layout in $template_root/layouts/$controller_name.r(html|xml), so PostsController will look
+ for layouts/posts.rhtml or layouts/posts.rxml and automatically configure this layout if found #307 *Marcel Molina Jr.*
+
+* Added FormHelper#radio_button to work with radio buttons like its already possible with check boxes *Michael Koziarski*
+
+* Added TemplateError#backtrace that makes it much easier to debug template errors from unit and functional tests
+
+* Added display of error messages with scaffolded form pages
+
+* Added option to ERB templates to swallow newlines by using <% if something -%> instead of just <% if something %>. Example:
+
+ class SomeController < ApplicationController
+ <% if options[:scaffold] %>
+ scaffold :<%= singular_name %>
+ <% end %>
+ helper :post
+
+ ...produces this on post as singular_name:
+
+ class SomeController < ApplicationController
+
+ scaffold :post
+
+ helper :post
+
+ ...where as:
+
+ class SomeController < ApplicationController
+ <% if options[:scaffold] -%>
+ scaffold :<%= singular_name %>
+ <% end -%>
+ helper :post
+
+ ...produces:
+
+ class SomeController < ApplicationController
+ scaffold :post
+ helper :post
+
+ *This undocumented gem for ERb was uncovered by bitsweat*
+
+* Fixed CgiRequest so that it'll now accept session options with Symbols as keys (as the documentation points out) *Suggested by Andreas*
+
+* Added that render_partial will always by default include a counter with value 1 unless there is a counter passed in via the
+ local_assigns hash that overrides it. As a result, render_collection_of_partials can still be written in terms of render_partial
+ and partials that make use of a counter can be called without problems from both render_collection_of_partials as well as
+ render_partial #295 *Marcel Molina Jr.*
+
+* Fixed CgiRequest#out to fall back to #write if $stdout doesn't have #syswrite *Jeremy Kemper*
+
+* Fixed all helpers so that they use XHTML compliant double quotes for values instead of single quotes *htonl/Jeremy Kemper*
+
+* Added link_to_image(src, options = {}, html_options = {}). Documentation:
+
+ Creates a link tag to the image residing at the +src+ using an URL created by the set of +options+. See the valid options in
+ link:classes/ActionController/Base.html#M000021. It's also possible to pass a string instead of an options hash to
+ get a link tag that just points without consideration. The <tt>html_options</tt> works jointly for the image and ahref tag by
+ letting the following special values enter the options on the image and the rest goes to the ahref:
+
+ ::alt: If no alt text is given, the file name part of the +src+ is used (capitalized and without the extension)
+ ::size: Supplied as "XxY", so "30x45" becomes width="30" and height="45"
+ ::align: Sets the alignment, no special features
+
+ The +src+ can be supplied as a...
+ * full path, like "/my_images/image.gif"
+ * file name, like "rss.gif", that gets expanded to "/images/rss.gif"
+ * file name without extension, like "logo", that gets expanded to "/images/logo.png"
+
+* Fixed to_input_field_tag so it no longer explicitly uses InstanceTag.value if value was specified in the options hash *evl*
+
+* Added the possibility of having validate be protected for assert_(in)valid_column #263 *Tobias Lütke*
+
+* Added that ActiveRecordHelper#form now calls url_for on the :action option.
+
+* Added all the HTTP methods as alternatives to the generic "process" for functional testing #276 *Tobias Lütke*. Examples:
+
+ # Calls Controller#miletone with a GET request
+ process :milestone
+
+ # Calls Controller#miletone with a POST request that has parameters
+ post :milestone, { "name" => "David" }
+
+ # Calls Controller#milestone with a HEAD request that has both parameters and session data
+ head :milestone, { "id" => 1 }, { "user_id" => 23 }
+
+ This is especially useful for testing idiomatic REST web services.
+
+* Added proper handling of HEAD requests, so that content isn't returned (Request#head? added as well) #277 *Eric Hodel*
+
+* Added indifference to whether @headers["Content-Type"], @headers["Content-type"], or @headers["content-type"] is used.
+
+* Added TestSession#session_id that returns an empty string to make it easier to functional test applications that doesn't use
+ cookie-based sessions #275 *jcf*
+
+* Fixed that cached template loading would still check the file system to see if the file existed #258 *Andreas Schwarz*
+
+* Added options to tailor header tag, div id, and div class on ActiveRecordHelper#error_messages_for *Josh Peek*
+
+* Added graceful handling of non-alphanumeric names and misplaced brackets in input parameters *Jeremy Kemper*
+
+* Added a new container for cookies that makes them more intuative to use. The old methods of cookie and @cookies have been deprecated.
+
+ Examples for writing:
+
+ cookies["user_name"] = "david" # => Will set a simple session cookie
+ cookies["login"] = { "value" => "XJ-122", "expires" => Time.now + 360} # => Will set a cookie that expires in 1 hour
+
+ Examples for reading:
+
+ cookies["user_name"] # => "david"
+ cookies.size # => 2
+
+ Read more in ActionController::Cookies
+
+ NOTE: If you were using the old accessor (cookies instead of @cookies), this could potentially break your code -- if you expect a full cookie object!
+
+* Added the opportunity to defined method_missing on a controller which will handle all requests for actions not otherwise defined #223 *timb*
+
+* Fixed AbstractRequest#remote_ip for users going through proxies - Patch #228 *Eric Hodel*
+
+* Added Request#ssl? which is shorthand for @request.protocol == "https://"
+
+* Added the choice to call form_tag with no arguments (resulting in a form posting to current action) *Jeremy Kemper*
+
+* Upgraded to Builder 1.2.1
+
+* Added :module as an alias for :controller_prefix to url_for and friends, so you can do redirect_to(:module => "shop", :controller => "purchases")
+ and go to /shop/purchases/
+
+* Added support for controllers in modules through @params["module"].
+
+* Added reloading for dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use those environments for development.
+ This is turned on by default, but can be turned off with ActionController::Base.reload_dependencies = false in production environments.
+
+ NOTE: This will only have an effect if you use the new model, service, and observer class methods to mark dependencies. All libraries loaded through
+ require will be "forever" cached. You can, however, use ActionController::Base.load_or_require("library") to get this behavior outside of the new
+ dependency style.
+
+* Added that controllers will automatically require their own helper if possible. So instead of doing:
+
+ class MsgController < ApplicationController
+ helper :msg
+ end
+
+ ...you can just do:
+
+ class MsgController < ApplicationController
+ end
+
+* Added dependencies_on(layer) to query the dependencies of a controller. Examples:
+
+ MsgController.dependencies_on(:model) # => [ :post, :comment, :attachment ]
+ MsgController.dependencies_on(:service) # => [ :notification_service ]
+ MsgController.dependencies_on(:observer) # => [ :comment_observer ]
+
+* Added a new dependency model with the class methods model, service, and observer. Example:
+
+ class MsgController < ApplicationController
+ model :post, :comment, :attachment
+ service :notification_service
+ observer :comment_observer
+ end
+
+ These new "keywords" remove the need for explicitly calling 'require' in most cases. The observer method even instantiates the
+ observer as well as requiring it.
+
+* Fixed that link_to would escape & in the url again after url_for already had done so
+
+
+## 0.9.5 (28) ##
+
+* Added helper_method to designate that a given private or protected method you should available as a helper in the view. *Jeremy Kemper*
+
+* Fixed assert_rendered_file so it actually verifies if that was the rendered file *htonl*
+
+* Added the option for sharing partial spacer templates just like partials themselves *radsaq*
+
+* Fixed that Russia was named twice in country_select *alexey*
+
+* Fixed request_origin to use remote_ip instead of remote_addr *Jeremy Kemper*
+
+* Fixed link_to breakage when nil was passed for html_options *alexey*
+
+* Fixed redirect_to on a virtual server setup with apache with a port other than the default where it would forget the port number *seanohalpin*
+
+* Fixed that auto-loading webrick on Windows would cause file uploads to fail *Jeremy Kemper*
+
+* Fixed issues with sending files on WEBrick by setting the proper binmode *Jeremy Kemper*
+
+* Added send_data as an alternative to send_file when the stream is not read off the filesystem but from a database or generated live *Jeremy Kemper*
+
+* Added a new way to include helpers that doesn't require the include hack and can go without the explicit require. *Jeremy Kemper*
+
+ Before:
+
+ module WeblogHelper
+ def self.included(controller) #:nodoc:
+ controller.ancestors.include?(ActionController::Base) ? controller.add_template_helper(self) : super
+ end
+ end
+
+ require 'weblog_helper'
+ class WeblogController < ActionController::Base
+ include WeblogHelper
+ end
+
+ After:
+
+ module WeblogHelper
+ end
+
+ class WeblogController < ActionController::Base
+ helper :weblog
+ end
+
+* Added a default content-type of "text/xml" to .rxml renders *Ryan Platte*
+
+* Fixed that when /controller/index was requested by the browser, url_for would generates wrong URLs *Ryan Platte*
+
+* Fixed a bug that would share cookies between users when using FastCGI and mod_ruby *The Robot Co-op*
+
+* Added an optional third hash parameter to the process method in functional tests that takes the session data to be used *alexey*
+
+* Added UrlHelper#mail_to to make it easier to create mailto: style ahrefs
+
+* Added better error messages for layouts declared with the .rhtml extension (which they shouldn't) *geech*
+
+* Added another case to DateHelper#distance_in_minutes to return "less than a minute" instead of "0 minutes" and "1 minute" instead of "1 minutes"
+
+* Added a hidden field to checkboxes generated with FormHelper#check_box that will make sure that the unchecked value (usually 0)
+ is sent even if the checkbox is not checked. This relieves the controller from doing custom checking if the checkbox wasn't
+ checked. BEWARE: This might conflict with your run-on-the-mill work-around code. *Tobias Lütke*
+
+* Fixed error_message_on to just use the first if more than one error had been added *Marcel Molina Jr.*
+
+* Fixed that URL rewriting with /controller/ was working but /controller was not and that you couldn't use :id on index *geech*
+
+* Fixed a bug with link_to where the :confirm option wouldn't be picked up if the link was a straight url instead of an option hash
+
+* Changed scaffolding of forms to use <label> tags instead of <b> to please W3C *evl*
+
+* Added DateHelper#distance_of_time_in_words_to_now(from_time) that works like distance_of_time_in_words,
+ but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
+
+* Added assert_flash_equal(expected, key, message), assert_session_equal(expected, key, message),
+ assert_assigned_equal(expected, key, message) to test the contents of flash, session, and template assigns.
+
+* Improved the failure report on assert_success when the action triggered a redirection *alexey*.
+
+* Added "markdown" to accompany "textilize" as a TextHelper method for converting text to HTML using the Markdown syntax.
+ BlueCloth must be installed in order for this method to become available.
+
+* Made sure that an active session exists before we attempt to delete it *Samuel*
+
+* Changed link_to with Javascript confirmation to use onclick instead of onClick for XHTML validity *Scott Barron*
+
+
+## 0.9.0 (43) ##
+
+* Added support for Builder-based templates for files with the .rxml extension. These new templates are an alternative to ERb that
+ are especially useful for generating XML content, such as this RSS example from Basecamp:
+
+ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
+ xml.channel do
+ xml.title(@feed_title)
+ xml.link(@url)
+ xml.description "Basecamp: Recent items"
+ xml.language "en-us"
+ xml.ttl "40"
+
+ for item in @recent_items
+ xml.item do
+ xml.title(item_title(item))
+ xml.description(item_description(item)) if item_description(item)
+ xml.pubDate(item_pubDate(item))
+ xml.guid(@person.firm.account.url + @recent_items.url(item))
+ xml.link(@person.firm.account.url + @recent_items.url(item))
+
+ xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
+ end
+ end
+ end
+ end
+
+ ...which will generate something like:
+
+ <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <channel>
+ <title>Web Site Redesign</title>
+ <link>http://www.basecamphq.com/clients/travelcenter/1/</link>
+ <description>Basecamp: Recent items</description>
+ <language>en-us</language>
+ <ttl>40</ttl>
+ <item>
+ <title>Post: don't you know</title>
+ <description>&amp;lt;p&amp;gt;deeper and down&amp;lt;/p&amp;gt;</description>
+ <pubDate>Fri, 20 Aug 2004 21:13:50 CEST</pubDate>
+ <guid>http://www.basecamphq.com/clients/travelcenter/1/msg/assets/96976/comments</guid>
+ <link>http://www.basecamphq.com/clients/travelcenter/1/msg/assets/96976/comments</link>
+ <dc:creator>David H. Heinemeier</dc:creator>
+ </item>
+ <item>
+ <title>Milestone completed: Design Comp 2</title>
+ <pubDate>Mon, 9 Aug 2004 14:42:06 CEST</pubDate>
+ <guid>http://www.basecamphq.com/clients/travelcenter/1/milestones/#49</guid>
+ <link>http://www.basecamphq.com/clients/travelcenter/1/milestones/#49</link>
+ </item>
+ </channel>
+ </rss>
+
+ The "xml" local variable is automatically available in .rxml templates. You construct the template by calling a method with the name
+ of the tag you want. Options for the tag can be specified as a hash parameter to that method.
+
+ Builder-based templates can be mixed and matched with the regular ERb ones. The only thing that differentiates them is the extension.
+ No new methods have been added to the public interface to handle them.
+
+ Action Pack ships with a version of Builder, but it will use the RubyGems version if you have one installed.
+
+ Read more about Builder on: http://onestepback.org/index.cgi/Tech/Ruby/StayingSimple.rdoc
+
+ *Builder is created by Jim Weirich*
+
+* Added much improved support for functional testing *what-a-day*.
+
+ # Old style
+ def test_failing_authenticate
+ @request.request_uri = "/login/authenticate"
+ @request.action = "authenticate"
+ @request.request_parameters["user_name"] = "nop"
+ @request.request_parameters["password"] = ""
+
+ response = LoginController.process_test(@request)
+
+ assert_equal "The username and/or password you entered is invalid.", response.session["flash"]["alert"]
+ assert_equal "http://37signals.basecamp.com/login/", response.headers["location"]
+ end
+
+ # New style
+ def test_failing_authenticate
+ process :authenticate, "user_name" => "nop", "password" => ""
+ assert_flash_has 'alert'
+ assert_redirected_to :action => "index"
+ end
+
+ See a full example on http://codepaste.org/view/paste/334
+
+* Increased performance by up to 100% with a revised cookie class that fixes the performance problems with the
+ default one that ships with 1.8.1 and below. It replaces the inheritance on SimpleDelegator with DelegateClass(Array)
+ following the suggestion from Matz on:
+ http://groups.google.com/groups?th=e3a4e68ba042f842&seekm=c3sioe%241qvm%241%40news.cybercity.dk#link14
+
+* Added caching for compiled ERb templates. On Basecamp, it gave between 8.5% and 71% increase in performance *Andreas Schwarz*.
+
+* Added implicit counter variable to render_collection_of_partials [Marcel Molina Jr.]. From the docs:
+
+ <%= render_collection_of_partials "ad", @advertisements %>
+
+ This will render "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display. An iteration counter
+ will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the
+ example above, the template would be fed +ad_counter+.
+
+* Fixed problems with two sessions being maintained on reset_session that would particularly screw up ActiveRecordStore.
+
+* Fixed reset_session to start an entirely new session instead of merely deleting the old. So you can now safely access @session
+ after calling reset_ression and expect it to work.
+
+* Added @request.get?, @request.post?, @request.put?, @request.delete? as convenience query methods for @request.method *geech*
+
+* Added @request.method that'll return a symbol representing the HTTP method, such as :get, :post, :put, :delete *geech*
+
+* Changed @request.remote_ip and @request.host to work properly even when a proxy is in front of the application *geech*
+
+* Added JavaScript confirm feature to link_to. Documentation:
+
+ The html_options have a special feature for creating javascript confirm alerts where if you pass
+ :confirm => 'Are you sure?', the link will be guarded with a JS popup asking that question.
+ If the user accepts, the link is processed, otherwise not.
+
+* Added link_to_unless_current as a UrlHelper method *Sam Stephenson*. Documentation:
+
+ Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
+ controller, action, and id are the same as the link's, in which case only the name is returned (or the
+ given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
+ to the page currently being viewed.
+
+* Fixed that UrlRewriter (the driver for url_for, link_to, etc) would blow up when the anchor was an integer *alexey*
+
+* Added that layouts defined with no directory defaults to layouts. So layout "weblog/standard" will use
+ weblog/standard (as always), but layout "standard" will use layouts/standard.
+
+* Fixed that partials (or any template starting with an underscore) was publically viewable *Marten*
+
+* Added HTML escaping to text_area helper.
+
+* Added :overwrite_params to url_for and friends to keep the parameters as they were passed to the current action and only overwrite a subset.
+ The regular :params will clear the slate so you need to manually add in existing parameters if you want to reuse them. *raphinou*
+
+* Fixed scaffolding problem with composite named objects *Moo Jester*
+
+* Added the possibility for shared partials. Example:
+
+ <%= render_partial "advertisement/ad", ad %>
+
+ This will render the partial "advertisement/_ad.rhtml" regardless of which controller this is being called from.
+
+ *Jacob Fugal*
+
+* Fixed crash when encountering forms that have empty-named fields *James Prudente*
+
+* Added check_box form helper method now accepts true/false as well as 1/0 *what-a-day*
+
+* Fixed the lacking creation of all directories with install.rb *Dave Steinberg*
+
+* Fixed that date_select returns valid XHTML selected options *Andreas Schwarz*
+
+* Fixed referencing an action with the same name as a controller in url_for *what-a-day*
+
+* Fixed the destructive nature of Base#attributes= on the argument *Kevin Watt*
+
+* Changed ActionControllerError to decent from StandardError instead of Exception. It can now be caught by a generic rescue.
+
+* Added SessionRestoreError that is raised when a session being restored holds objects where there is no class available.
+
+* Added block as option for inline filters. So what used to be written as:
+
+ before_filter Proc { |controller| return false if controller.params["stop_action"] }
+
+ ...can now be as:
+
+ before_filter { |controller| return false if controller.params["stop_action"] }
+
+ *Jeremy Kemper*
+
+* Made the following methods public (was protected): url_for, controller_class_name, controller_name, action_name
+ This makes it easier to write filters without cheating around the encapsulation with send.
+
+* ActionController::Base#reset_session now sticks even if you access @session afterwards *Kent Sibilev*
+
+* Improved the exception logging so the log file gets almost as much as in-browser debugging.
+
+* Changed base class setup from AbstractTemplate/ERbTemplate to ActionView::Base. This change should be harmless unless you were
+ accessing Action View directly in which case you now need to reference the Base class.\
+
+* Added that render_collection_of_partials returns nil if the collection is empty. This makes showing a “no items” message easier.
+ For example: <%= render_collection_of_partials("message", @messages) || "No messages found." %> *Sam Stephenson*
+
+* Added :month_before_year as an option to date_select to get the month select before the year. Especially useful for credit card forms.
+
+* Added :add_month_numbers to select_month to get options like "3 - March".
+
+* Removed Base.has_active_layout? as it couldn't answer the question without the instance. Use Base#active_layout instead.
+
+* Removed redundant call to update on ActionController::Base#close_session *Andreas Schwarz*
+
+* Fixed that DRb Store accidently started its own server (instead of just client) *Andreas*
+
+* Fixed strip_links so it now works across multiple lines *Chad Fowler*
+
+* Fixed the TemplateError exception to show the proper trace on to_s (useful for unit test debugging)
+
+* Implemented class inheritable attributes without eval *Caio Chassot*
+
+* Made TextHelper#concat accept binding as it would otherwise not work
+
+* The FormOptionsHelper will now call to_s on the keys and values used to generate options
+
+
+## 0.8.5 ##
+
+* Introduced passing of locally scoped variables between templates:
+
+ You can pass local variables to sub templates by using a hash of with the variable
+ names as keys and the objects as values:
+
+ <%= render "shared/header", { "headline" => "Welcome", "person" => person } %>
+
+ These can now be accessed in shared/header with:
+
+ Headline: <%= headline %>
+ First name: <%= person.first_name %>
+
+* Introduced the concept of partials as a certain type of sub templates:
+
+ There's also a convenience method for rendering sub templates within the current
+ controller that depends on a single object (we call this kind of sub templates for
+ partials). It relies on the fact that partials should follow the naming convention
+ of being prefixed with an underscore -- as to separate them from regular templates
+ that could be rendered on their own. In the template for Advertiser#buy, we could have:
+
+ <% for ad in @advertisements %>
+ <%= render_partial "ad", ad %>
+ <% end %>
+
+ This would render "advertiser/_ad.rhtml" and pass the local variable +ad+
+ for the template to display.
+
+ == Rendering a collection of partials
+
+ The example of partial use describes a familar pattern where a template needs
+ to iterate over a array and render a sub template for each of the elements.
+ This pattern has been implemented as a single method that accepts an array and
+ renders a partial by the same name of as the elements contained within. So the
+ three-lined example in "Using partials" can be rewritten with a single line:
+
+ <%= render_collection_of_partials "ad", @advertisements %>
+
+ So this will render "advertiser/_ad.rhtml" and pass the local variable +ad+ for
+ the template to display.
+
+* Improved send_file by allowing a wide range of options to be applied *Jeremy Kemper*:
+
+ Sends the file by streaming it 4096 bytes at a time. This way the
+ whole file doesn't need to be read into memory at once. This makes
+ it feasible to send even large files.
+
+ Be careful to sanitize the path parameter if it coming from a web
+ page. send_file(@params['path'] allows a malicious user to
+ download any file on your server.
+
+ Options:
+ * <tt>:filename</tt> - specifies the filename the browser will see.
+ Defaults to File.basename(path).
+ * <tt>:type</tt> - specifies an HTTP content type.
+ Defaults to 'application/octet-stream'.
+ * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+ Valid values are 'inline' and 'attachment' (default).
+ * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream
+ the file. Defaults to 4096.
+
+ The default Content-Type and Content-Disposition headers are
+ set to download arbitrary binary files in as many browsers as
+ possible. IE versions 4, 5, 5.5, and 6 are all known to have
+ a variety of quirks (especially when downloading over SSL).
+
+ Simple download:
+ send_file '/path/to.zip'
+
+ Show a JPEG in browser:
+ send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
+
+ Read about the other Content-* HTTP headers if you'd like to
+ provide the user with more information (such as Content-Description).
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
+
+ Also be aware that the document may be cached by proxies and browsers.
+ The Pragma and Cache-Control headers declare how the file may be cached
+ by intermediaries. They default to require clients to validate with
+ the server before releasing cached responses. See
+ http://www.mnot.net/cache_docs/ for an overview of web caching and
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
+ for the Cache-Control header spec.
+
+* Added pluralize method to the TextHelper that makes it easy to get strings like "1 message", "3 messages"
+
+* Added proper escaping for the rescues *Andreas Schwarz*
+
+* Added proper escaping for the option and collection tags *Andreas Schwarz*
+
+* Fixed NaN errors on benchmarking *Jim Weirich*
+
+* Fixed query string parsing for URLs that use the escaped versions of & or ; as part of a key or value
+
+* Fixed bug with custom Content-Type headers being in addition to rather than instead of the default header.
+ (This bug didn't matter with neither CGI or mod_ruby, but FCGI exploded on it) *With help from Ara T. Howard*
+
+
+## 0.8.0 ##
+
+* Added select, collection_select, and country_select to make it easier for Active Records to set attributes through
+ drop-down lists of options. Example:
+
+ <%= select "person", "gender", %w( Male Female ) %>
+
+ ...would give the following:
+
+ <select name="person[gender]" id="person_gender"><option>Male</option><option>Female</option></select>
+
+* Added an option for getting multiple values on a single form name into an array instead of having the last one overwrite.
+ This is especially useful for groups of checkboxes, which can now be written as:
+
+ <input type="checkbox" name="rights[]" value="CREATE" />
+ <input type="checkbox" name="rights[]" value="UPDATE" />
+ <input type="checkbox" name="rights[]" value="DELETE" />
+
+ ...and retrieved in the controller action with:
+
+ @params["rights"] # => [ "CREATE", "UPDATE", "DELETE" ]
+
+ The old behavior (where the last one wins, "DELETE" in the example) is still available. Just don't add "[]" to the
+ end of the name. *Scott Baron*
+
+* Added send_file which uses the new render_text block acceptance to make it feasible to send large files.
+ The files is sent with a bunch of voodoo HTTP headers required to get arbitrary files to download as
+ expected in as many browsers as possible (eg, IE hacks). Example:
+
+ def play_movie
+ send_file "/movies/that_movie.avi"
+ end
+
+ *Jeremy Kemper*
+
+* render_text now accepts a block for deferred rendering. Useful for streaming large files, displaying
+ a “please wait” message during a complex search, etc. Streaming example:
+
+ render_text do |response|
+ File.open(path, 'rb') do |file|
+ while buf = file.read(1024)
+ print buf
+ end
+ end
+ end
+
+ *Jeremy Kemper*
+
+* Added a new Tag Helper that can generate generic tags programmatically insted of through HTML. Example:
+
+ tag("br", "clear" => "all") => <br clear="all" />
+
+ ...that's usually not terribly interesting (unless you have a lot of options already in a hash), but it
+ gives way for more specific tags, like the new form tag:
+
+ form_tag({ :controller => "weblog", :action => "update" }, { :multipart => "true", "style" => "width: 200px"}) =>
+ <form action="/weblog/update" enctype="multipart/formdata" style="width: 200px">
+
+ There's even a "pretty" version for people who don't like to open tags in code and close them in HTML:
+
+ <%= start_form_tag :action => "update" %>
+ # all the input fields
+ <%= end_form_tag %>
+
+ (end_form_tag just returns "</form>")
+
+* The selected parameter in options_for_select may now also an array of values to be selected when
+ using a multiple select. Example:
+
+ options_for_select([ "VISA", "Mastercard", "Discover" ], ["VISA", "Discover"]) =>
+ <option selected>VISA</option>\n<option>Mastercard</option>\n<option selected>Discover</option>
+
+ *Scott Baron*
+
+* Changed the URL rewriter so controller_prefix and action_prefix can be used in isolation. You can now do:
+
+ url_for(:controller_prefix => "clients")
+
+ ...or:
+
+ url_for(:action_prefix => "category/messages")
+
+ Neither would have worked in isolation before (:controller_prefix required a :controller and :action_prefix required an :action)
+
+* Started process of a cleaner separation between Action Controller and ERb-based Action Views by introducing an
+ abstract base class for views. And Amita adapter could be fitted in more easily now.
+
+* The date helper methods date_select and datetime_select now also use the field error wrapping
+ (div with class fieldWithErrors by default).
+
+* The date helper methods date_select and datetime_select can now discard selects
+
+* Added option on AbstractTemplate to specify a different field error wrapping. Example:
+
+ ActionView::AbstractTemplate.field_error_proc = Proc.new do |html, instance|
+ "<p>#{instance.method_name + instance.error_message}</p><div style='background-color: red'>#{html}</div>"
+ end
+
+ ...would give the following on a Post#title (text field) error:
+
+ <p>Title can't be empty</p>
+ <div style='background-color: red'>
+ <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
+ </div>
+
+* The UrlHelper methods url_for and link_to will now by default only return paths, not complete URIs.
+ That should make it easier to fit a Rails application behind a proxy or load-balancer.
+ You can overwrite this by passing :only_path => false as part of the options. *Suggested by U235*
+
+* Fixed bug with having your own layout for use with scaffolding *Kevin Radloff*
+
+* Fixed bug where redirect_to_path didn't append the port on non-standard ports *dhawkins*
+
+* Scaffolding plays nicely with single-table inheritance (LoadErrors are caught) *Jeremy Kemper*
+
+* Scaffolding plays nice with plural models like Category/categories *Jeremy Kemper*
+
+* Fixed missing suffix appending in scaffolding *Kevin Radloff*
+
+
+## 0.7.9 ##
+
+* The "form" method now present boolean fields from PostgreSQL as drop-down menu. *Scott*
+
+* Scaffolding now automatically attempts to require the class that's being scaffolded.
+
+* Scaffolding will use the current active layout, instead of its own, if one has been specified. Example:
+
+ class WeblogController < ActionController::Base
+ layout "layouts/weblog"
+ scaffold :post
+ end
+
+ *Suggested by Scott*
+
+* Changed url_for (and all the that drives, like redirect_to, link_to, link_for) so you can pass it a symbol instead of a hash.
+ This symbol is a method reference which is then called to calculate the url. Example:
+
+ class WeblogController < ActionController::Base
+ def update
+ # do some update
+ redirect_to :dashboard_url
+ end
+
+ protected
+ def dashboard_url
+ if @project.active?
+ url_for :controller => "project", :action => "dashboard"
+ else
+ url_for :controller => "account", :action => "dashboard"
+ end
+ end
+ end
+
+* Added default_url_options to specialize behavior for all url_for (and friends) calls:
+
+ Overwrite to implement a number of default options that all url_for-based methods will use.
+ The default options should come in form of a hash, just like the one you would use for
+ url_for directly. Example:
+
+ def default_url_options(options)
+ { :controller_prefix => @project.active? ? "projects/" : "accounts/" }
+ end
+
+ As you can infer from the example, this is mostly useful for situations where you want to
+ centralize dynamic dissions about the urls as they stem from the business domain. Please note
+ that any individual url_for call can always override the defaults set by this method.
+
+
+* Changed url_for so that an "id" passed in the :params is not treated special. You need to use the dedicated :id to get
+ the special auto path-params treatment. Considering the url http://localhost:81/friends/list
+
+ url_for(:action => "show", :params => { "id" => 5 })
+ ...used to give http://localhost:81/friends/show/5
+ ......now gives http://localhost:81/friends/show?id=5
+
+ If you want the automated id behavior, do:
+
+ url_for(:action => "show", :id => 5 )
+ ....which gives http://localhost:81/friends/show/5
+
+
+* Fixed problem with anchor being inserted before path parameters with url_for (and friends)
+
+
+## 0.7.8 ##
+
+* Fixed session bug where you couldn't store any objects that didn't exist in the standard library
+ (such as Active Record objects).
+
+* Added reset_session method for Action Controller objects to clear out all objects in the session.
+
+* Fixed that exceptions raised during filters are now also caught by the default rescues
+
+* Added new around_filter for doing before and after filtering with a single object *Florian Weber*:
+
+ class WeblogController < ActionController::Base
+ around_filter BenchmarkingFilter.new
+
+ # Before this action is performed, BenchmarkingFilter#before(controller) is executed
+ def index
+ end
+ # After this action has been performed, BenchmarkingFilter#after(controller) is executed
+ end
+
+ class BenchmarkingFilter
+ def initialize
+ @runtime
+ end
+
+ def before
+ start_timer
+ end
+
+ def after
+ stop_timer
+ report_result
+ end
+ end
+
+* Added the options for specifying a different name and id for the form helper methods than what is guessed *Florian Weber*:
+
+ text_field "post", "title"
+ ...just gives: <input id="post_title" name="post[title]" size="30" type="text" value="" />
+
+ text_field "post", "title", "id" => "title_for_post", "name" => "first_post_title"
+ ...can now give: <input id="title_for_post" name="first_post_title" size="30" type="text" value="" />
+
+* Added DebugHelper with a single "debug" method for doing pretty dumps of objects in the view
+ (now used in the default rescues to better present the contents of session and template variables)
+
+* Added note to log about the templates rendered within layouts (before just the layout was shown)
+
+* Fixed redirects on https setups *Andreas*
+
+* Fixed scaffolding problem on the edit action when using :suffix => true *Scott*
+
+* Fixed scaffolding problem where implementing list.rhtml wouldn't work for the index action
+
+* URLs generated now uses &amp; instead of just & so pages using it can validate with W3C *Spotted by Andreas*
+
+
+## 0.7.7 ##
+
+* Fixed bug in CGI extension that prevented multipart forms from working
+
+
+## 0.7.6 ##
+
+* Included ERB::Util so all templates can easily escape HTML content with <%=h @person.content %>
+
+* All requests are now considered local by default, so everyone will be exposed to detailed debugging screens on errors.
+ When the application is ready to go public, set ActionController::Base.consider_all_requests_local to false,
+ and implement the protected method local_request? in the controller to determine when debugging screens should be shown.
+
+* Fixed three bugs with the url_for/redirect_to/link_to handling. Considering the url http://localhost:81/friends/show/1
+
+ url_for(:action => "list")
+ ...used to give http://localhost:81/friends/list/1
+ ......now gives http://localhost:81/friends/list
+
+ url_for(:controller => "friends", :action => "destroy", :id => 5)
+ ...used to give http://localhost:81/friends/destroy
+ ......now gives http://localhost:81/friends/destroy/5
+
+ Considering the url http://localhost:81/teachers/show/t
+
+ url_for(:action => "list", :id => 5)
+ ...used to give http://localhost:81/5eachers/list/t
+ ......now gives http://localhost:81/teachers/list/5
+
+ *Reported by David Morton & Radsaq*
+
+* Logs exception to logfile in addition to showing them for local requests
+
+* Protects the eruby load behind a begin/rescue block. eRuby is not required to run ActionController.
+
+* Fixed install.rb to also install clean_logger and the templates
+
+* Added ActiveRecordStore as a session option. Read more in lib/action_controller/session/active_record_store.rb *Tim Bates*
+
+* Change license to MIT License (and included license file in package)
+
+* Application error page now returns status code 500 instead of 200
+
+* Fixed using Procs as layout handlers *Florian Weber*
+
+* Fixed bug with using redirects ports other than 80
+
+* Added index method that calls list on scaffolding
+
+
+## 0.7.5 ##
+
+* First public release
diff --git a/actionpack/MIT-LICENSE b/actionpack/MIT-LICENSE
index 7ad1051066..810daf856c 100644
--- a/actionpack/MIT-LICENSE
+++ b/actionpack/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2011 David Heinemeier Hansson
+Copyright (c) 2004-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index 95301c21ee..076a93bbcd 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -206,7 +206,7 @@ A short rundown of some of the major features:
You specify a logger through a class method, such as:
- ActionController::Base.logger = Logger.new("Application Log")
+ ActionController::Base.logger = ActiveSupport::Logger.new("Application Log")
ActionController::Base.logger = Log4r::Logger.new("Application Log")
@@ -327,7 +327,9 @@ Source code can be downloaded as part of the Rails project on GitHub
== License
-Action Pack is released under the MIT license.
+Action Pack is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index d9e3e56fcc..bb1e704767 100755
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -16,7 +16,7 @@ Rake::TestTask.new(:test_action_pack) do |t|
# 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/{abstract,controller,dispatch,template}/**/*_test.rb').sort
+ t.test_files = Dir.glob('test/{abstract,controller,dispatch,template,assertions}/**/*_test.rb').sort
t.warning = true
t.verbose = true
@@ -55,15 +55,15 @@ end
task :lines do
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
- for file_name in FileList["lib/**/*.rb"]
+ FileList["lib/**/*.rb"].each do |file_name|
next if file_name =~ /vendor/
- f = File.open(file_name)
-
- while line = f.gets
- lines += 1
- next if line =~ /^\s*$/
- next if line =~ /^\s*#/
- codelines += 1
+ File.open(file_name, 'r') do |f|
+ while line = f.gets
+ lines += 1
+ next if line =~ /^\s*$/
+ next if line =~ /^\s*#/
+ codelines += 1
+ end
end
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index c2eac234b0..b205acdb79 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -6,13 +6,13 @@ Gem::Specification.new do |s|
s.version = version
s.summary = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).'
s.description = 'Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
+ s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
s.require_path = 'lib'
s.requirements << 'none'
@@ -20,11 +20,10 @@ Gem::Specification.new do |s|
s.add_dependency('activemodel', version)
s.add_dependency('rack-cache', '~> 1.1')
s.add_dependency('builder', '~> 3.0.0')
- s.add_dependency('i18n', '~> 0.6')
- s.add_dependency('rack', '~> 1.3.5')
+ s.add_dependency('rack', '~> 1.4.1')
s.add_dependency('rack-test', '~> 0.6.1')
- s.add_dependency('journey', '~> 1.0.0')
- s.add_dependency('sprockets', '~> 2.0.3')
+ s.add_dependency('journey', '~> 1.0.1')
+ s.add_dependency('sprockets', '~> 2.2.0')
s.add_dependency('erubis', '~> 2.7.0')
s.add_development_dependency('tzinfo', '~> 0.3.29')
diff --git a/actionpack/examples/performance.rb b/actionpack/examples/performance.rb
new file mode 100644
index 0000000000..994e745bb0
--- /dev/null
+++ b/actionpack/examples/performance.rb
@@ -0,0 +1,185 @@
+ENV['RAILS_ENV'] ||= 'production'
+
+require File.expand_path('../../../load_paths', __FILE__)
+require 'action_pack'
+require 'action_controller'
+require 'action_view'
+require 'active_model'
+require 'benchmark'
+
+MyHash = Class.new(Hash)
+
+Hash.class_eval do
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+end
+
+class Runner
+ def initialize(app, output)
+ @app, @output = app, output
+ end
+
+ def puts(*)
+ super if @output
+ 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)
+ return unless ENV["DEBUG"]
+ out = env['rack.errors']
+ out.puts response[0], response[1].to_yaml, '---'
+ response[2].each { |part| out.puts part }
+ out.puts '---'
+ end
+
+ def self.puts(*)
+ super if @output
+ end
+
+ def self.print(*)
+ super if @output
+ end
+
+ def self.app_and_env_for(action, n)
+ env = Rack::MockRequest.env_for("/")
+ env.merge!('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout)
+ app = lambda { |env| BasePostController.action(action).call(env) }
+ return app, env
+ end
+
+ $ran = []
+
+ def self.run(action, n, output = true)
+ print "."
+ STDOUT.flush
+ @output = output
+ label = action.to_s
+ app, env = app_and_env_for(action, n)
+ t = Benchmark.realtime { new(app, output).call(env) }
+ $ran << [label, (t * 1000).to_i.to_s] if output
+ end
+
+ def self.done
+ puts
+ header, content = "", ""
+ $ran.each do |k,v|
+ size = [k.size, v.size].max + 1
+ header << format("%#{size}s", k)
+ content << format("%#{size}s", v)
+ end
+ puts header
+ puts content
+ end
+end
+
+ActionController::Base.logger = nil
+ActionController::Base.config.compile_methods!
+ActionView::Resolver.caching = ENV["RAILS_ENV"] == "production"
+
+class BasePostController < ActionController::Base
+ append_view_path "#{File.dirname(__FILE__)}/views"
+
+ def overhead
+ self.response_body = ''
+ end
+
+ def index
+ render :text => ''
+ end
+
+ $OBJECT = {:name => "Hello my name is omg", :address => "333 omg"}
+
+ def partial
+ render :partial => "/collection", :object => $OBJECT
+ end
+
+ def partial_10
+ render :partial => "/ten_partials"
+ end
+
+ def partial_100
+ render :partial => "/hundred_partials"
+ end
+
+ $COLLECTION1 = []
+ 10.times do |i|
+ $COLLECTION1 << { :name => "Hello my name is omg", :address => "333 omg" }
+ end
+
+ def coll_10
+ render :partial => "/collection", :collection => $COLLECTION1
+ end
+
+ $COLLECTION2 = []
+ 100.times do |i|
+ $COLLECTION2 << { :name => "Hello my name is omg", :address => "333 omg" }
+ end
+
+ def coll_100
+ render :partial => "/collection", :collection => $COLLECTION2
+ end
+
+ def uniq_100
+ render :partial => $COLLECTION2
+ end
+
+ $COLLECTION3 = []
+ 50.times do |i|
+ $COLLECTION3 << {:name => "Hello my name is omg", :address => "333 omg"}
+ $COLLECTION3 << MyHash.new(:name => "Hello my name is omg", :address => "333 omg")
+ end
+
+ def diff_100
+ render :partial => $COLLECTION3
+ end
+
+ def template_1
+ render :template => "template"
+ end
+
+ module Foo
+ def omg
+ "omg"
+ end
+ end
+
+ helper Foo
+end
+
+N = (ENV['N'] || 1000).to_i
+# ActionController::Base.use_accept_header = false
+
+def run_all!(times, verbose)
+ Runner.run(:overhead, times, verbose)
+ Runner.run(:index, times, verbose)
+ Runner.run(:template_1, times, verbose)
+ Runner.run(:partial, times, verbose)
+ Runner.run(:partial_10, times, verbose)
+ Runner.run(:coll_10, times, verbose)
+ Runner.run(:partial_100, times, verbose)
+ Runner.run(:coll_100, times, verbose)
+ Runner.run(:uniq_100, times, verbose)
+ Runner.run(:diff_100, times, verbose)
+end
+
+unless ENV["PROFILE"]
+ run_all!(1, false)
+
+ (ENV["M"] || 1).to_i.times do
+ $ran = []
+ run_all!(N, true)
+ Runner.done
+ end
+else
+ Runner.run(ENV["PROFILE"].to_sym, 1, false)
+ require "ruby-prof"
+ RubyProf.start
+ Runner.run(ENV["PROFILE"].to_sym, N, true)
+ result = RubyProf.stop
+ printer = RubyProf::CallStackPrinter.new(result)
+ printer.print(File.open("output.html", "w"))
+end \ No newline at end of file
diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb
new file mode 100644
index 0000000000..cee3fe64c0
--- /dev/null
+++ b/actionpack/examples/views/_collection.erb
@@ -0,0 +1,3 @@
+<%= collection[:name] %>
+<%= collection[:address] %>
+<%= omg %> \ No newline at end of file
diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb
new file mode 100644
index 0000000000..5ab2f8a432
--- /dev/null
+++ b/actionpack/examples/views/_hello.erb
@@ -0,0 +1 @@
+Hello \ No newline at end of file
diff --git a/actionpack/examples/views/_hundred_partials.erb b/actionpack/examples/views/_hundred_partials.erb
new file mode 100644
index 0000000000..35c2a6c9d3
--- /dev/null
+++ b/actionpack/examples/views/_hundred_partials.erb
@@ -0,0 +1,3 @@
+<% 100.times do %>
+ <%= render :partial => "/collection", :object => $OBJECT %>
+<% end %> \ No newline at end of file
diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb
new file mode 100644
index 0000000000..3ca8e80b52
--- /dev/null
+++ b/actionpack/examples/views/_partial.erb
@@ -0,0 +1,10 @@
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
+<%= "Hello" %>
diff --git a/actionpack/examples/views/_ten_partials.erb b/actionpack/examples/views/_ten_partials.erb
new file mode 100644
index 0000000000..fd02991e22
--- /dev/null
+++ b/actionpack/examples/views/_ten_partials.erb
@@ -0,0 +1,10 @@
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %>
+<%= render :partial => '/collection', :object => $OBJECT %> \ No newline at end of file
diff --git a/actionpack/examples/views/hashes/_hash.erb b/actionpack/examples/views/hashes/_hash.erb
new file mode 100644
index 0000000000..c100a290bd
--- /dev/null
+++ b/actionpack/examples/views/hashes/_hash.erb
@@ -0,0 +1,3 @@
+<%= hash[:name] %>
+<%= hash[:address] %>
+<%= omg %> \ No newline at end of file
diff --git a/actionpack/examples/views/my_hashes/_my_hash.erb b/actionpack/examples/views/my_hashes/_my_hash.erb
new file mode 100644
index 0000000000..e25d84101a
--- /dev/null
+++ b/actionpack/examples/views/my_hashes/_my_hash.erb
@@ -0,0 +1,3 @@
+<%= my_hash[:name] %>
+<%= my_hash[:address] %>
+<%= omg %> \ No newline at end of file
diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb
new file mode 100644
index 0000000000..5ab2f8a432
--- /dev/null
+++ b/actionpack/examples/views/template.html.erb
@@ -0,0 +1 @@
+Hello \ No newline at end of file
diff --git a/actionpack/lib/abstract_controller/asset_paths.rb b/actionpack/lib/abstract_controller/asset_paths.rb
index c2a6809f58..dd5f9a1942 100644
--- a/actionpack/lib/abstract_controller/asset_paths.rb
+++ b/actionpack/lib/abstract_controller/asset_paths.rb
@@ -4,7 +4,7 @@ module AbstractController
included do
config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
- :stylesheets_dir, :default_asset_host_protocol
+ :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
end
end
end
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 7004e607a1..44c9ea34ba 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -8,7 +8,7 @@ module AbstractController
include ActiveSupport::Callbacks
included do
- define_callbacks :process_action, :terminator => "response_body"
+ define_callbacks :process_action, :terminator => "response_body", :skip_after_callbacks_if_terminated => true
end
# Override AbstractController::Base's process_action to run the
@@ -21,11 +21,9 @@ module AbstractController
module ClassMethods
# If :only or :except are used, convert the options into the
- # primitive form (:per_key) used by ActiveSupport::Callbacks.
+ # :unless and :if options of ActiveSupport::Callbacks.
# The basic idea is that :only => :index gets converted to
- # :if => proc {|c| c.action_name == "index" }, but that the
- # proc is only evaluated once per action for the lifetime of
- # a Rails process.
+ # :if => proc {|c| c.action_name == "index" }.
#
# ==== Options
# * <tt>only</tt> - The callback should be run only for this action
@@ -33,11 +31,11 @@ module AbstractController
def _normalize_callback_options(options)
if only = options[:only]
only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
- options[:per_key] = {:if => only}
+ options[:if] = Array(options[:if]) << only
end
if except = options[:except]
except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
- options[:per_key] = {:unless => except}
+ options[:unless] = Array(options[:unless]) << except
end
end
@@ -167,7 +165,6 @@ module AbstractController
# for details on the allowed parameters.
def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after} # options[:if] = (Array.wrap(options[:if]) << "!halted") if false
set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
end # end
end # end
@@ -176,7 +173,6 @@ module AbstractController
# for details on the allowed parameters.
def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after} # options[:if] = (Array.wrap(options[:if]) << "!halted") if false
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
end # end
end # end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 10aa34c76b..6a6387632c 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -66,27 +66,40 @@ module AbstractController
# == Inheritance Examples
#
# class BankController < ActionController::Base
- # layout "bank_standard"
+ # # bank.html.erb exists
+ #
+ # class ExchangeController < BankController
+ # # exchange.html.erb exists
+ #
+ # class CurrencyController < BankController
#
# class InformationController < BankController
+ # layout "information"
#
- # class TellerController < BankController
+ # class TellerController < InformationController
# # teller.html.erb exists
#
- # class TillController < TellerController
+ # class EmployeeController < InformationController
+ # # employee.html.erb exists
+ # layout nil
#
# class VaultController < BankController
# layout :access_level_layout
#
- # class EmployeeController < BankController
- # layout nil
+ # class TillController < BankController
+ # layout false
#
- # In these examples:
- # * The InformationController uses the "bank_standard" layout, inherited from BankController.
- # * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+.
- # * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well.
+ # In these examples, we have three implicit lookup scenrios:
+ # * The BankController uses the "bank" layout.
+ # * The ExchangeController uses the "exchange" layout.
+ # * The CurrencyController inherits the layout from BankController.
+ #
+ # However, when a layout is explicitly set, the explicitly set layout wins:
+ # * The InformationController uses the "information" layout, explicitly set.
+ # * The TellerController also uses the "information" layout, because the parent explicitly set it.
+ # * The EmployeeController uses the "employee" layout, because it set the layout to nil, resetting the parent configuration.
# * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
- # * The EmployeeController does not use a layout at all.
+ # * The TillController does not use a layout at all.
#
# == Types of layouts
#
@@ -126,6 +139,22 @@ module AbstractController
# If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
# Otherwise, it will be looked up relative to the template root.
#
+ # Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
+ # Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
+ #
+ # class ApplicationController < ActionController::Base
+ # layout "application"
+ # end
+ #
+ # class PostsController < ApplicationController
+ # # Will use "application" layout
+ # end
+ #
+ # class CommentsController < ApplicationController
+ # # Will search for "comments" layout and fallback "application" layout
+ # layout nil
+ # end
+ #
# == Conditional layouts
#
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
@@ -166,13 +195,15 @@ module AbstractController
include Rendering
included do
- class_attribute :_layout_conditions
- remove_possible_method :_layout_conditions
- delegate :_layout_conditions, :to => :'self.class'
+ class_attribute :_layout, :_layout_conditions,
+ :instance_reader => false, :instance_writer => false
+ self._layout = nil
self._layout_conditions = {}
_write_layout_method
end
+ delegate :_layout_conditions, :to => "self.class"
+
module ClassMethods
def inherited(klass)
super
@@ -188,7 +219,7 @@ module AbstractController
#
# ==== Returns
# * <tt> Boolean</tt> - True if the action has a layout, false otherwise.
- def action_has_layout?
+ def conditional_layout?
return unless super
conditions = _layout_conditions
@@ -213,7 +244,7 @@ module AbstractController
# true:: raise an ArgumentError
#
# ==== Parameters
- # * <tt>String, Symbol, false</tt> - The layout to use.
+ # * <tt>layout</tt> - The layout to use.
#
# ==== Options (conditions)
# * :only - A list of actions to apply this layout to.
@@ -224,7 +255,7 @@ module AbstractController
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
self._layout_conditions = conditions
- @_layout = layout || false # Converts nil to false
+ self._layout = layout
_write_layout_method
end
@@ -244,43 +275,46 @@ module AbstractController
def _write_layout_method
remove_possible_method(:_layout)
- case defined?(@_layout) ? @_layout : nil
- when String
- self.class_eval %{def _layout; #{@_layout.inspect} end}, __FILE__, __LINE__
- when Symbol
- self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def _layout
- #{@_layout}.tap do |layout|
+ prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
+ name_clause = if name
+ <<-RUBY
+ lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super
+ RUBY
+ end
+
+ layout_definition = case _layout
+ when String
+ _layout.inspect
+ when Symbol
+ <<-RUBY
+ #{_layout}.tap do |layout|
unless layout.is_a?(String) || !layout
- raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
+ raise ArgumentError, "Your layout method :#{_layout} returned \#{layout}. It " \
"should have returned a String, false, or nil"
end
end
- end
- ruby_eval
- when Proc
- define_method :_layout_from_proc, &@_layout
- self.class_eval %{def _layout; _layout_from_proc(self) end}, __FILE__, __LINE__
- when false
- self.class_eval %{def _layout; end}, __FILE__, __LINE__
- when true
- raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
- when nil
- if name
- _prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
-
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
- if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
- "#{_implied_layout_name}"
- else
- super
- end
- end
RUBY
- end
+ when Proc
+ define_method :_layout_from_proc, &_layout
+ "_layout_from_proc(self)"
+ when false
+ nil
+ when true
+ raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
+ when nil
+ name_clause
end
- self.class_eval { private :_layout }
+
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def _layout
+ if conditional_layout?
+ #{layout_definition}
+ else
+ #{name_clause}
+ end
+ end
+ private :_layout
+ RUBY
end
end
@@ -289,8 +323,7 @@ module AbstractController
if _include_layout?(options)
layout = options.key?(:layout) ? options.delete(:layout) : :default
- value = _layout_for_option(layout)
- options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
+ options[:layout] = _layout_for_option(layout)
end
end
@@ -305,24 +338,25 @@ module AbstractController
@_action_has_layout
end
+ def conditional_layout?
+ true
+ end
+
private
# This will be overwritten by _write_layout_method
def _layout; end
- # Determine the layout for a given name and details, taking into account
- # the name type.
+ # Determine the layout for a given name, taking into account the name type.
#
# ==== Parameters
# * <tt>name</tt> - The name of the template
- # * <tt>details</tt> - A list of details to restrict
- # the lookup to. By default, layout lookup is limited to the
- # formats specified for the current request.
def _layout_for_option(name)
case name
- when String then name
- when true then _default_layout(true)
- when :default then _default_layout(false)
+ when String then _normalize_layout(name)
+ when Proc then name
+ when true then Proc.new { _default_layout(true) }
+ when :default then Proc.new { _default_layout(false) }
when false, nil then nil
else
raise ArgumentError,
@@ -330,31 +364,32 @@ module AbstractController
end
end
- # Returns the default layout for this controller and a given set of details.
+ def _normalize_layout(value)
+ value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value
+ end
+
+ # Returns the default layout for this controller.
# Optionally raises an exception if the layout could not be found.
#
# ==== Parameters
- # * <tt>details</tt> - A list of details to restrict the search by. This
- # might include details like the format or locale of the template.
- # * <tt>require_layout</tt> - If this is true, raise an ArgumentError
- # with details about the fact that the exception could not be
- # found (defaults to false)
+ # * <tt>require_layout</tt> - If set to true and layout is not found,
+ # an ArgumentError exception is raised (defaults to false)
#
# ==== Returns
# * <tt>template</tt> - The template object for the default layout (or nil)
def _default_layout(require_layout = false)
begin
- layout_name = _layout if action_has_layout?
+ value = _layout if action_has_layout?
rescue NameError => e
raise e, "Could not render layout: #{e.message}"
end
- if require_layout && action_has_layout? && !layout_name
+ if require_layout && action_has_layout? && !value
raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
end
- layout_name
+ _normalize_layout(value)
end
def _include_layout?(options)
diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb
index 0b196119f4..a4e31cd2e5 100644
--- a/actionpack/lib/abstract_controller/logger.rb
+++ b/actionpack/lib/abstract_controller/logger.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/logger"
require "active_support/benchmarkable"
module AbstractController
@@ -7,7 +6,7 @@ module AbstractController
included do
config_accessor :logger
- extend ActiveSupport::Benchmarkable
+ include ActiveSupport::Benchmarkable
end
end
end
diff --git a/actionpack/lib/abstract_controller/railties/routes_helpers.rb b/actionpack/lib/abstract_controller/railties/routes_helpers.rb
index dec1e9d6d9..6684f46f64 100644
--- a/actionpack/lib/abstract_controller/railties/routes_helpers.rb
+++ b/actionpack/lib/abstract_controller/railties/routes_helpers.rb
@@ -5,8 +5,8 @@ module AbstractController
Module.new do
define_method(:inherited) do |klass|
super(klass)
- if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
- klass.send(:include, namespace._railtie.routes.url_helpers)
+ if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
+ klass.send(:include, namespace.railtie_routes_url_helpers)
else
klass.send(:include, routes.url_helpers)
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 41fdc11196..ddc93464cd 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,6 +1,5 @@
require "abstract_controller/base"
require "action_view"
-require "active_support/core_ext/object/instance_variables"
module AbstractController
class DoubleRenderError < Error
@@ -35,7 +34,7 @@ module AbstractController
include AbstractController::ViewPaths
included do
- config_accessor :protected_instance_variables, :instance_reader => false
+ class_attribute :protected_instance_variables
self.protected_instance_variables = []
end
@@ -59,19 +58,8 @@ module AbstractController
attr_internal_writer :view_context_class
- # Explicitly define protected_instance_variables so it can be
- # inherited and overwritten by other modules if needed.
- def protected_instance_variables
- config.protected_instance_variables
- end
-
def view_context_class
- @_view_context_class || self.class.view_context_class
- end
-
- def initialize(*)
- @_view_context_class = nil
- super
+ @_view_context_class ||= self.class.view_context_class
end
# An instance of a view class. The default view class is ActionView::Base
@@ -120,20 +108,20 @@ module AbstractController
view_renderer.render(view_context, options)
end
- DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w(
- @_action_name @_response_body @_formats @_prefixes @_config
- @_view_context_class @_view_renderer @_lookup_context
- )
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = [
+ :@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config,
+ :@_view_context_class, :@_view_renderer, :@_lookup_context
+ ]
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
# :api: public
def view_assigns
hash = {}
- variables = instance_variable_names
+ variables = instance_variables
variables -= protected_instance_variables
variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
- variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) }
+ variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) }
hash
end
diff --git a/actionpack/lib/abstract_controller/view_paths.rb b/actionpack/lib/abstract_controller/view_paths.rb
index e8394447a7..c08b3a0e2a 100644
--- a/actionpack/lib/abstract_controller/view_paths.rb
+++ b/actionpack/lib/abstract_controller/view_paths.rb
@@ -10,7 +10,7 @@ module AbstractController
self._view_paths.freeze
end
- delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
+ delegate :template_exists?, :view_paths, :formats, :formats=,
:locale, :locale=, :to => :lookup_context
module ClassMethods
@@ -89,7 +89,7 @@ module AbstractController
# * <tt>paths</tt> - If a PathSet is provided, use that;
# otherwise, process the parameter into a PathSet.
def view_paths=(paths)
- self._view_paths = ActionView::PathSet.new(Array.wrap(paths))
+ self._view_paths = ActionView::PathSet.new(Array(paths))
end
end
end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index f4eaa2fd1b..a0c54dbd84 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -40,7 +40,6 @@ module ActionController
autoload :Integration, 'action_controller/deprecated/integration_test'
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
- autoload :UrlWriter, 'action_controller/deprecated'
autoload :Routing, 'action_controller/deprecated'
autoload :TestCase, 'action_controller/test_case'
autoload :TemplateAssertions, 'action_controller/test_case'
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 98bfe72fef..3b82231b15 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -116,12 +116,12 @@ module ActionController
#
# Title: <%= @post.title %>
#
- # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
+ # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
# will use the manual rendering methods:
#
# def search
# @results = Search.find(params[:query])
- # case @results
+ # case @results.count
# when 0 then render :action => "no_results"
# when 1 then render :action => "show"
# when 2..10 then render :action => "show_many"
@@ -133,7 +133,7 @@ module ActionController
# == Redirects
#
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
- # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
+ # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
# going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
#
# def create
@@ -228,8 +228,11 @@ module ActionController
include mod
end
- # Rails 2.x compatibility
- include ActionController::Compatibility
+ # Define some internal variables that should not be propagated to the view.
+ self.protected_instance_variables = [
+ :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
+ :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
+ ]
ActiveSupport.run_load_hooks(:action_controller, self)
end
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 14137f2886..112573a38d 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -24,7 +24,6 @@ module ActionController #:nodoc:
#
# config.action_controller.cache_store = :memory_store
# config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
- # config.action_controller.cache_store = :drb_store, "druby://localhost:9192"
# config.action_controller.cache_store = :mem_cache_store, "localhost"
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
# config.action_controller.cache_store = MyOwnStore.new("parameter")
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index 0031d2701f..ba96735e56 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -47,7 +47,8 @@ module ActionController #:nodoc:
# And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
# proc that specifies when the action should be cached.
#
- # Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
+ # As of Rails 3.0, you can also pass <tt>:expires_in</tt> with a time
+ # interval (in seconds) to schedule expiration of the cached item.
#
# The following example depicts some of the points made above:
#
@@ -56,14 +57,14 @@ module ActionController #:nodoc:
#
# caches_page :public
#
- # caches_action :index, :if => proc do
+ # caches_action :index, :if => Proc.new do
# !request.format.json? # cache if is not a JSON request
# end
#
# caches_action :show, :cache_path => { :project => 1 },
# :expires_in => 1.hour
#
- # caches_action :feed, :cache_path => proc do
+ # caches_action :feed, :cache_path => Proc.new do
# if params[:user_id]
# user_list_url(params[:user_id, params[:id])
# else
@@ -102,8 +103,10 @@ module ActionController #:nodoc:
end
def _save_fragment(name, options)
- content = response_body
- content = content.join if content.is_a?(Array)
+ content = ""
+ response_body.each do |parts|
+ content << parts
+ end
if caching_allowed?
write_fragment(name, content, options)
@@ -116,9 +119,8 @@ module ActionController #:nodoc:
def expire_action(options = {})
return unless cache_configured?
- actions = options[:action]
- if actions.is_a?(Array)
- actions.each {|action| expire_action(options.merge(:action => action)) }
+ if options.is_a?(Hash) && options[:action].is_a?(Array)
+ options[:action].each {|action| expire_action(options.merge(:action => action)) }
else
expire_fragment(ActionCachePath.new(self, options, false).path)
end
@@ -175,7 +177,7 @@ module ActionController #:nodoc:
private
def normalize!(path)
path << 'index' if path[-1] == ?/
- path << ".#{extension}" if extension and !path.ends_with?(extension)
+ path << ".#{extension}" if extension and !path.split('?').first.ends_with?(".#{extension}")
URI.parser.unescape(path)
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 957bb7de6b..159f718029 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -16,7 +16,7 @@ module ActionController #:nodoc:
# caches_page :show, :new
# end
#
- # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
+ # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
# that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the
# existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack.
# This is much faster than handling the full dynamic request in the usual way.
@@ -38,23 +38,25 @@ module ActionController #:nodoc:
extend ActiveSupport::Concern
included do
- ##
- # :singleton-method:
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
# web server to look in the new location for cached files.
- config_accessor :page_cache_directory
+ class_attribute :page_cache_directory
self.page_cache_directory ||= ''
- ##
- # :singleton-method:
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
- config_accessor :page_cache_extension
+ class_attribute :page_cache_extension
self.page_cache_extension ||= '.html'
+
+ # The compression used for gzip. If false (default), the page is not compressed.
+ # If can be a symbol showing the ZLib compression method, for example, :best_compression
+ # or :best_speed or an integer configuring the compression level.
+ class_attribute :page_cache_compression
+ self.page_cache_compression ||= false
end
module ClassMethods
@@ -66,24 +68,31 @@ module ActionController #:nodoc:
instrument_page_cache :expire_page, path do
File.delete(path) if File.exist?(path)
+ File.delete(path + '.gz') if File.exist?(path + '.gz')
end
end
# Manually cache the +content+ in the key determined by +path+. Example:
# cache_page "I'm the cached content", "/lists/show"
- def cache_page(content, path, extension = nil)
+ def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
return unless perform_caching
path = page_cache_path(path, extension)
instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
File.open(path, "wb+") { |f| f.write(content) }
+ if gzip
+ Zlib::GzipWriter.open(path + '.gz', gzip) { |f| f.write(content) }
+ end
end
end
- # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
+ # Caches the +actions+ using the page-caching approach that'll store
+ # the cache in a path within the page_cache_directory that
# matches the triggering url.
#
+ # You can also pass a :gzip option to override the class configuration one.
+ #
# Usage:
#
# # cache the index action
@@ -91,10 +100,28 @@ module ActionController #:nodoc:
#
# # cache the index action except for JSON requests
# caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
+ #
+ # # don't gzip images
+ # caches_page :image, :gzip => false
def caches_page(*actions)
return unless perform_caching
options = actions.extract_options!
- after_filter({:only => actions}.merge(options)) { |c| c.cache_page }
+
+ gzip_level = options.fetch(:gzip, page_cache_compression)
+ gzip_level = case gzip_level
+ when Symbol
+ Zlib.const_get(gzip_level.to_s.upcase)
+ when Fixnum
+ gzip_level
+ when false
+ nil
+ else
+ Zlib::BEST_COMPRESSION
+ end
+
+ after_filter({:only => actions}.merge(options)) do |c|
+ c.cache_page(nil, nil, gzip_level)
+ end
end
private
@@ -136,7 +163,7 @@ module ActionController #:nodoc:
# Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used.
# If no options are provided, the url of the current request being handled is used. Example:
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
- def cache_page(content = nil, options = nil)
+ def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
return unless self.class.perform_caching && caching_allowed?
path = case options
@@ -152,7 +179,7 @@ module ActionController #:nodoc:
extension = ".#{type_symbol}"
end
- self.class.cache_page(content || response.body, path, extension)
+ self.class.cache_page(content || response.body, path, extension, gzip)
end
end
diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb
index aa0cfc9395..2405bebb97 100644
--- a/actionpack/lib/action_controller/deprecated.rb
+++ b/actionpack/lib/action_controller/deprecated.rb
@@ -1,3 +1,7 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
-ActionController::Routing = ActionDispatch::Routing \ No newline at end of file
+ActionController::Routing = ActionDispatch::Routing
+
+ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.'
+ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.'
+ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.' \ No newline at end of file
diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb
index 86336b6bc4..54eae48f47 100644
--- a/actionpack/lib/action_controller/deprecated/integration_test.rb
+++ b/actionpack/lib/action_controller/deprecated/integration_test.rb
@@ -1,2 +1,5 @@
ActionController::Integration = ActionDispatch::Integration
ActionController::IntegrationTest = ActionDispatch::IntegrationTest
+
+ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.'
+ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.'
diff --git a/actionpack/lib/action_controller/deprecated/performance_test.rb b/actionpack/lib/action_controller/deprecated/performance_test.rb
index fcf47d31a7..c7ba5a2fe7 100644
--- a/actionpack/lib/action_controller/deprecated/performance_test.rb
+++ b/actionpack/lib/action_controller/deprecated/performance_test.rb
@@ -1 +1,3 @@
ActionController::PerformanceTest = ActionDispatch::PerformanceTest
+
+ActiveSupport::Deprecation.warn 'ActionController::PerformanceTest is deprecated and will be removed, use ActionDispatch::PerformanceTest instead.'
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index 35e29398e6..4c76f4c43b 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -20,15 +20,18 @@ module ActionController
status = payload[:status]
if status.nil? && payload[:exception].present?
- status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
+ status = Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code)
end
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
message << " (#{additions.join(" | ")})" unless additions.blank?
- message << "\n"
info(message)
end
+ def halted_callback(event)
+ info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
+ end
+
def send_file(event)
message = "Sent file %s"
message << " (%.1fms)"
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 0133b2ecbc..92433ab462 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -181,9 +181,13 @@ module ActionController
@_status = Rack::Utils.status_code(status)
end
- def response_body=(val)
- body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
- super body
+ def response_body=(body)
+ body = [body] unless body.nil? || body.respond_to?(:each)
+ super
+ end
+
+ def performed?
+ !!response_body
end
def dispatch(name, request) #:nodoc:
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
deleted file mode 100644
index 05dca445a4..0000000000
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-module ActionController
- module Compatibility
- extend ActiveSupport::Concern
-
- class ::ActionController::ActionControllerError < StandardError #:nodoc:
- end
-
- # Temporary hax
- included do
- ::ActionController::UnknownAction = ::AbstractController::ActionNotFound
- ::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError
-
- # ROUTES TODO: This should be handled by a middleware and route generation
- # should be able to handle SCRIPT_NAME
- self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
-
- class << self
- delegate :default_charset=, :to => "ActionDispatch::Response"
- end
-
- self.protected_instance_variables = %w(
- @_status @_headers @_params @_env @_response @_request
- @_view_runtime @_stream @_url_options @_action_has_layout
- )
-
- def rescue_action(env)
- raise env["action_dispatch.rescue.exception"]
- end
- end
-
- # For old tests
- def initialize_template_class(*) end
- def assign_shortcuts(*) end
-
- def _normalize_options(options)
- options[:text] = nil if options.delete(:nothing) == true
- options[:text] = " " if options.key?(:text) && options[:text].nil?
- super
- end
-
- def render_to_body(options)
- options[:template].sub!(/^\//, '') if options.key?(:template)
- super || " "
- end
-
- 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 performed?
- response_body
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 809894395d..2a8e4c575e 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -23,8 +23,27 @@ module ActionController
# This will render the show template if the request isn't sending a matching etag or
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
#
- def fresh_when(options)
- options.assert_valid_keys(:etag, :last_modified, :public)
+ # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ # fresh_when(@article)
+ # end
+ #
+ # When passing a record, you can still set whether the public header:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ # fresh_when(@article, :public => true)
+ # end
+ def fresh_when(record_or_options, additional_options = {})
+ if record_or_options.is_a? Hash
+ options = record_or_options
+ options.assert_valid_keys(:etag, :last_modified, :public)
+ else
+ record = record_or_options
+ options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
+ end
response.etag = options[:etag] if options[:etag]
response.last_modified = options[:last_modified] if options[:last_modified]
@@ -55,8 +74,34 @@ module ActionController
# end
# end
# end
- def stale?(options)
- fresh_when(options)
+ #
+ # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ #
+ # if stale?(@article)
+ # @statistics = @article.really_expensive_call
+ # respond_to do |format|
+ # # all the supported formats
+ # end
+ # end
+ # end
+ #
+ # When passing a record, you can still set whether the public header:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ #
+ # if stale?(@article, :public => true)
+ # @statistics = @article.really_expensive_call
+ # respond_to do |format|
+ # # all the supported formats
+ # end
+ # end
+ # end
+ def stale?(record_or_options, additional_options = {})
+ fresh_when(record_or_options, additional_options)
!request.fresh?(response)
end
@@ -65,7 +110,7 @@ module ActionController
#
# Examples:
# expires_in 20.minutes
- # expires_in 3.hours, :public => true
+ # expires_in 3.hours, :public => true, :must_revalidate => true
# expires_in 3.hours, 'max-stale' => 5.hours, :public => true
#
# This method will overwrite an existing Cache-Control header.
@@ -73,7 +118,11 @@ module ActionController
#
# The method will also ensure a HTTP Date header for client compatibility.
def expires_in(seconds, options = {}) #:doc:
- response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public))
+ response.cache_control.merge!(
+ :max_age => seconds,
+ :public => options.delete(:public),
+ :must_revalidate => options.delete(:must_revalidate)
+ )
options.delete(:private)
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 0670a58d97..30ddf6c16e 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/file/path'
require 'action_controller/metal/exceptions'
module ActionController #:nodoc:
@@ -115,7 +114,7 @@ module ActionController #:nodoc:
private
def send_file_headers!(options)
type_provided = options.has_key?(:type)
-
+
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
[:type, :disposition].each do |arg|
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index 07024d0a9a..ece9ba3725 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -43,4 +43,4 @@ module ActionController
class UnknownHttpMethod < ActionControllerError #:nodoc:
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index 0fd42f9d8a..69e37d8713 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -29,6 +29,8 @@ module ActionController
if !request.ssl? && !Rails.env.development?
redirect_options = {:protocol => 'https://', :status => :moved_permanently}
redirect_options.merge!(:host => host) if host
+ redirect_options.merge!(:params => request.query_parameters)
+ flash.keep
redirect_to redirect_options
end
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index bd515bba82..d070eaae5d 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
module ActionController
@@ -56,7 +55,7 @@ module ActionController
include AbstractController::Helpers
included do
- config_accessor :helpers_path, :include_all_helpers
+ class_attribute :helpers_path, :include_all_helpers
self.helpers_path ||= []
self.include_all_helpers = true
end
@@ -94,7 +93,7 @@ module ActionController
def all_helpers_from_path(path)
helpers = []
- Array.wrap(path).each do |_path|
+ Array(path).each do |_path|
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 264806cd36..3d46163b74 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -1,4 +1,4 @@
-require 'active_support/base64'
+require 'base64'
require 'active_support/core_ext/object/blank'
module ActionController
@@ -67,7 +67,7 @@ module ActionController
# class PostsController < ApplicationController
# REALM = "SuperSecret"
# USERS = {"dhh" => "secret", #plain text password
- # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
+ # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
#
# before_filter :authenticate, :except => [:index]
#
@@ -141,11 +141,11 @@ module ActionController
end
def decode_credentials(request)
- ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
+ ::Base64.decode64(request.authorization.split(' ', 2).last || '')
end
def encode_credentials(user_name, password)
- "Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}"
+ "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
end
def authentication_request(controller, realm)
@@ -192,12 +192,15 @@ module ActionController
return false unless password
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
- uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url
+ uri = credentials[:uri][0,1] == '/' ? request.original_fullpath : request.original_url
- [true, false].any? do |password_is_ha1|
- expected = expected_response(method, uri, credentials, password, password_is_ha1)
- expected == credentials[:response]
- end
+ [true, false].any? do |trailing_question_mark|
+ [true, false].any? do |password_is_ha1|
+ _uri = trailing_question_mark ? uri + "?" : uri
+ expected = expected_response(method, _uri, credentials, password, password_is_ha1)
+ expected == credentials[:response]
+ end
+ end
end
end
@@ -286,7 +289,7 @@ module ActionController
t = time.to_i
hashed = [t, secret_key]
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
- ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '')
+ ::Base64.strict_encode64("#{t}:#{digest}")
end
# Might want a shorter timeout depending on whether the request
@@ -295,7 +298,7 @@ module ActionController
# allow a user to use new nonce without prompting user again for their
# username and password.
def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
- t = ActiveSupport::Base64.decode64(value).split(":").first.to_i
+ t = ::Base64.decode64(value).split(":").first.to_i
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index e8e465d3ba..ae04b53825 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -2,7 +2,7 @@ module ActionController
module ImplicitRender
def send_action(method, *args)
ret = super
- default_render unless response_body
+ default_render unless performed?
ret
end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 777a0ab343..640ebf5f00 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -64,7 +64,12 @@ module ActionController
end
end
- protected
+ private
+
+ # A hook invoked everytime a before callback is halted.
+ def halted_callback_hook(filter)
+ ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
+ end
# A hook which allows you to clean up any time taken into account in
# views wrongly, like database querying time.
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 00bd1706e7..80ecc16d53 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -182,7 +182,7 @@ module ActionController #:nodoc:
#
# def index
# @people = Person.all
- # respond_with(@person)
+ # respond_with(@people)
# end
# end
#
@@ -191,8 +191,9 @@ module ActionController #:nodoc:
def respond_to(*mimes, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
- if response = retrieve_response_from_mimes(mimes, &block)
- response.call(nil)
+ if collector = retrieve_collector_from_mimes(mimes, &block)
+ response = collector.response
+ response ? response.call : default_render({})
end
end
@@ -232,10 +233,18 @@ module ActionController #:nodoc:
raise "In order to use respond_with, first you need to declare the formats your " <<
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
- if response = retrieve_response_from_mimes(&block)
+ if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
- options.merge!(:default_response => response)
- (options.delete(:responder) || self.class.responder).call(self, resources, options)
+
+ if defined_response = collector.response
+ if action = options.delete(:action)
+ render :action => action
+ else
+ defined_response.call
+ end
+ else
+ (options.delete(:responder) || self.class.responder).call(self, resources, options)
+ end
end
end
@@ -263,15 +272,16 @@ module ActionController #:nodoc:
# Collects mimes and return the response for the negotiated format. Returns
# nil if :not_acceptable was sent to the client.
#
- def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc:
+ def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
mimes ||= collect_mimes_from_class_level
- collector = Collector.new(mimes) { |options| default_render(options || {}) }
+ collector = Collector.new(mimes)
block.call(collector) if block_given?
+ format = collector.negotiate_format(request)
- if format = request.negotiate_mime(collector.order)
+ if format
self.content_type ||= format.to_s
lookup_context.freeze_formats([format.to_sym])
- collector.response_for(format)
+ collector
else
head :not_acceptable
nil
@@ -280,10 +290,10 @@ module ActionController #:nodoc:
class Collector #:nodoc:
include AbstractController::Collector
- attr_accessor :order
+ attr_accessor :order, :format
- def initialize(mimes, &block)
- @order, @responses, @default_response = [], {}, block
+ def initialize(mimes)
+ @order, @responses = [], {}
mimes.each { |mime| send(mime) }
end
@@ -302,8 +312,12 @@ module ActionController #:nodoc:
@responses[mime_type] ||= block
end
- def response_for(mime)
- @responses[mime] || @responses[Mime::ALL] || @default_response
+ def response
+ @responses[format] || @responses[Mime::ALL]
+ end
+
+ def negotiate_format(request)
+ @format = request.negotiate_mime(order)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index e0d8e1c992..fa760f2658 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/anonymous'
require 'action_dispatch/http/mime_types'
@@ -43,6 +42,11 @@ module ActionController
# wrap_parameters :person, :include => [:username, :password]
# end
#
+ # On ActiveRecord models with no +:include+ or +:exclude+ option set,
+ # if attr_accessible is set on that model, it will only wrap the accessible
+ # parameters, else it will only wrap the parameters returned by the class
+ # method attribute_names.
+ #
# If you're going to pass the parameters to an +ActiveModel+ object (such as
# +User.new(params[:user])+), you might consider passing the model class to
# the method instead. The +ParamsWrapper+ will actually try to determine the
@@ -141,7 +145,7 @@ module ActionController
# try to find Foo::Bar::User, Foo::User and finally User.
def _default_wrap_model #:nodoc:
return nil if self.anonymous?
- model_name = self.name.sub(/Controller$/, '').singularize
+ model_name = self.name.sub(/Controller$/, '').classify
begin
if model_klass = model_name.safe_constantize
@@ -162,7 +166,9 @@ module ActionController
unless options[:include] || options[:exclude]
model ||= _default_wrap_model
- if model.respond_to?(:attribute_names) && model.attribute_names.present?
+ if model.respond_to?(:accessible_attributes) && model.accessible_attributes.present?
+ options[:include] = model.accessible_attributes.to_a
+ elsif model.respond_to?(:attribute_names) && model.attribute_names.present?
options[:include] = model.attribute_names
end
end
@@ -173,9 +179,9 @@ module ActionController
controller_name.singularize
end
- options[:include] = Array.wrap(options[:include]).collect(&:to_s) if options[:include]
- options[:exclude] = Array.wrap(options[:exclude]).collect(&:to_s) if options[:exclude]
- options[:format] = Array.wrap(options[:format])
+ options[:include] = Array(options[:include]).collect(&:to_s) if options[:include]
+ options[:exclude] = Array(options[:exclude]).collect(&:to_s) if options[:exclude]
+ options[:format] = Array(options[:format])
self._wrapper_options = options
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 0355c9f458..b07742e0e1 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -18,7 +18,7 @@ module ActionController
#
# * <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> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</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>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
@@ -81,7 +81,8 @@ module ActionController
# 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+.-]*:.*}
+ # The protocol relative scheme starts with a double slash "//"
+ when %r{^(\w[\w+.-]*:|//).*}
options
when String
request.protocol + request.host_with_port + options
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 0ad9dbeda9..6e9ce450ac 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/blank'
+require 'set'
module ActionController
# See <tt>Renderers.add</tt>
@@ -12,16 +13,13 @@ module ActionController
included do
class_attribute :_renderers
- self._renderers = {}.freeze
+ self._renderers = Set.new.freeze
end
module ClassMethods
def use_renderers(*args)
- new = _renderers.dup
- args.each do |key|
- new[key] = RENDERERS[key]
- end
- self._renderers = new.freeze
+ renderers = _renderers + args
+ self._renderers = renderers.freeze
end
alias use_renderer use_renderers
end
@@ -31,10 +29,10 @@ module ActionController
end
def _handle_render_options(options)
- _renderers.each do |name, value|
- if options.key?(name.to_sym)
+ _renderers.each do |name|
+ if options.key?(name)
_process_options(options)
- return send("_render_option_#{name}", options.delete(name.to_sym), options)
+ return send("_render_option_#{name}", options.delete(name), options)
end
end
nil
@@ -42,7 +40,7 @@ module ActionController
# Hash of available renderers, mapping a renderer name to its proc.
# Default keys are :json, :js, :xml.
- RENDERERS = {}
+ RENDERERS = Set.new
# Adds a new renderer to call within controller actions.
# A renderer is invoked by passing its name as an option to
@@ -79,7 +77,7 @@ module ActionController
# <tt>ActionController::MimeResponds#respond_with</tt>
def self.add(key, &block)
define_method("_render_option_#{key}", &block)
- RENDERERS[key] = block
+ RENDERERS << key.to_sym
end
module All
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 70fd79bb8b..a677cdf15d 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -29,6 +29,10 @@ module ActionController
self.response_body = nil
end
+ def render_to_body(*)
+ super || " "
+ end
+
private
# Normalize arguments by catching blocks and setting them on :update.
@@ -44,6 +48,10 @@ module ActionController
options[:text] = options[:text].to_text
end
+ if options.delete(:nothing) || (options.key?(:text) && options[:text].nil?)
+ options[:text] = " "
+ end
+
if options[:status]
options[:status] = Rack::Utils.status_code(options[:status])
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index bc22e39efb..afa9243f02 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -74,7 +74,7 @@ module ActionController #:nodoc:
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
unless verified_request?
- logger.warn "WARNING: Can't verify CSRF token authenticity" if logger
+ logger.warn "Can't verify CSRF token authenticity" if logger
handle_unverified_request
end
end
diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb
index eb037aa1b0..68cc9a9c9b 100644
--- a/actionpack/lib/action_controller/metal/rescue.rb
+++ b/actionpack/lib/action_controller/metal/rescue.rb
@@ -1,4 +1,7 @@
module ActionController #:nodoc:
+ # This module is responsible to provide `rescue_from` helpers
+ # to controllers and configure when detailed exceptions must be
+ # shown.
module Rescue
extend ActiveSupport::Concern
include ActiveSupport::Rescuable
@@ -12,10 +15,20 @@ module ActionController #:nodoc:
super(exception)
end
+ # Override this method if you want to customize when detailed
+ # exceptions must be shown. This method is only called when
+ # consider_all_requests_local is false. By default, it returns
+ # false, but someone may set it to `request.local?` so local
+ # requests in production still shows the detailed exception pages.
+ def show_detailed_exceptions?
+ false
+ end
+
private
def process_action(*args)
super
rescue Exception => exception
+ request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions?
rescue_with_handler(exception) || raise(exception)
end
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index b932302a60..4ad64bff20 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -84,8 +84,8 @@ module ActionController #:nodoc:
#
# === Custom options
#
- # <code>respond_with</code> also allow you to pass options that are forwarded
- # to the underlying render call. Those options are only applied success
+ # <code>respond_with</code> also allows you to pass options that are forwarded
+ # to the underlying render call. Those options are only applied for success
# scenarios. For instance, you can do the following in the create method above:
#
# def create
@@ -95,7 +95,7 @@ module ActionController #:nodoc:
# respond_with(@project, @task, :status => 201)
# end
#
- # This will return status 201 if the task was saved with success. If not,
+ # This will return status 201 if the task was saved successfully. If not,
# it will simply ignore the given options and return status 422 and the
# resource errors. To customize the failure scenario, you can pass a
# a block to <code>respond_with</code>:
@@ -129,7 +129,6 @@ module ActionController #:nodoc:
@resources = resources
@options = options
@action = options.delete(:action)
- @default_response = options.delete(:default_response)
end
delegate :head, :render, :redirect_to, :to => :controller
@@ -222,11 +221,11 @@ module ActionController #:nodoc:
alias :navigation_location :resource_location
alias :api_location :resource_location
- # If a given response block was given, use it, otherwise call render on
+ # If a response block was given, use it, otherwise call render on
# controller.
#
def default_render
- @default_response.call(options)
+ controller.default_render(options)
end
# Display is just a shortcut to render a resource with the current format.
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 5fe5334458..e9783e6919 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/file/path'
require 'rack/chunked'
module ActionController #:nodoc:
@@ -195,7 +194,7 @@ module ActionController #:nodoc:
# ==== Passenger
#
# To be described.
- #
+ #
module Streaming
extend ActiveSupport::Concern
@@ -217,7 +216,7 @@ module ActionController #:nodoc:
end
end
- # Call render_to_body if we are streaming instead of usual +render+.
+ # Call render_body if we are streaming instead of usual +render+.
def _render_template(options) #:nodoc:
if options.delete(:stream)
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index f0c29825ba..a288e69649 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -14,14 +14,17 @@ module ActionController
end
initializer "action_controller.initialize_framework_caches" do
- ActiveSupport.on_load(:action_controller) { self.cache_store ||= RAILS_CACHE }
+ ActiveSupport.on_load(:action_controller) { self.cache_store ||= Rails.cache }
+ end
+
+ initializer "action_controller.assets_config", :group => :all do |app|
+ app.config.action_controller.assets_dir ||= app.config.paths["public"].first
end
initializer "action_controller.set_configs" do |app|
paths = app.config.paths
options = app.config.action_controller
- options.assets_dir ||= paths["public"].first
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
options.page_cache_directory ||= paths["public"].first
@@ -29,6 +32,7 @@ module ActionController
# make sure readers methods get compiled
options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
+ options.relative_url_root ||= app.config.relative_url_root
ActiveSupport.on_load(:action_controller) do
include app.routes.mounted_helpers
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
index 699c44c62c..bbe63149ad 100644
--- a/actionpack/lib/action_controller/railties/paths.rb
+++ b/actionpack/lib/action_controller/railties/paths.rb
@@ -6,13 +6,14 @@ module ActionController
define_method(:inherited) do |klass|
super(klass)
- if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
- paths = namespace._railtie.paths["app/helpers"].existent
+ if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
+ paths = namespace.railtie_helpers_paths
else
- paths = app.config.helpers_paths
+ paths = app.helpers_paths
end
klass.helpers_path = paths
+
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
klass.helper :all
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 6913c1ef4a..1e226fc336 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -69,15 +69,12 @@ module ActionController
# assert_template :partial => '_customer', :locals => { :customer => @customer }
#
def assert_template(options = {}, message = nil)
- validate_request!
-
case options
when NilClass, String, Symbol
options = options.to_s if Symbol === options
rendered = @templates
- msg = build_message(message,
- "expecting <?> but rendering with <?>",
- options, rendered.keys.join(', '))
+ msg = message || sprintf("expecting <%s> but rendering with <%s>",
+ options, rendered.keys)
assert_block(msg) do
if options
rendered.any? { |t,num| t.match(options) }
@@ -86,6 +83,20 @@ module ActionController
end
end
when Hash
+ if expected_layout = options[:layout]
+ msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
+ expected_layout, @layouts.keys)
+
+ case expected_layout
+ when String
+ assert_includes @layouts.keys, expected_layout, msg
+ when Regexp
+ assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
+ when nil
+ assert(@layouts.empty?, msg)
+ end
+ end
+
if expected_partial = options[:partial]
if expected_locals = options[:locals]
actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
@@ -94,28 +105,13 @@ module ActionController
end
elsif expected_count = options[:count]
actual_count = @partials[expected_partial]
- msg = build_message(message,
- "expecting ? to be rendered ? time(s) but rendered ? time(s)",
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
expected_partial, expected_count, actual_count)
assert(actual_count == expected_count.to_i, msg)
- elsif options.key?(:layout)
- msg = build_message(message,
- "expecting layout <?> but action rendered <?>",
- expected_layout, @layouts.keys)
-
- case layout = options[:layout]
- when String
- assert(@layouts.include?(expected_layout), msg)
- when Regexp
- assert(@layouts.any? {|l| l =~ layout }, msg)
- when nil
- assert(@layouts.empty?, msg)
- end
else
- msg = build_message(message,
- "expecting partial <?> but action rendered <?>",
+ msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
options[:partial], @partials.keys)
- assert(@partials.include?(expected_partial), msg)
+ assert_includes @partials, expected_partial, msg
end
else
assert @partials.empty?,
@@ -250,6 +246,13 @@ module ActionController
# end
# end
#
+ # You can also send a real document in the simulated HTTP request.
+ #
+ # def test_create
+ # json = {:book => { :title => "Love Hina" }}.to_json
+ # post :create, json
+ # end
+ #
# == Special instance variables
#
# ActionController::TestCase will also automatically provide the following instance
@@ -296,11 +299,11 @@ module ActionController
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
# assert flash.empty? # makes sure that there's nothing in the flash
#
- # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To
+ # For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
# appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
- # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work.
+ # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
#
- # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
+ # On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
#
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
# action call which can then be asserted against.
@@ -324,6 +327,12 @@ module ActionController
#
# assert_redirected_to page_url(:title => 'foo')
class TestCase < ActiveSupport::TestCase
+
+ # Use AS::TestCase for the base class when describing a model
+ register_spec_type(self) do |desc|
+ desc < ActionController::Base
+ end
+
module Behavior
extend ActiveSupport::Concern
include ActionDispatch::TestProcess
@@ -342,7 +351,7 @@ module ActionController
def tests(controller_class)
case controller_class
when String, Symbol
- self.controller_class = "#{controller_class.to_s.underscore}_controller".camelize.constantize
+ self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
when Class
self.controller_class = controller_class
else
@@ -374,28 +383,28 @@ module ActionController
end
# 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")
+ def get(action, *args)
+ process(action, "GET", *args)
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")
+ def post(action, *args)
+ process(action, "POST", *args)
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")
+ def put(action, *args)
+ process(action, "PUT", *args)
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")
+ def delete(action, *args)
+ process(action, "DELETE", *args)
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")
+ process(action, "HEAD", parameters, session, flash)
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@@ -421,19 +430,20 @@ module ActionController
end
end
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
+ def process(action, http_method = 'GET', *args)
+ check_required_ivars
+ http_method, args = handle_old_process_api(http_method, args)
+
+ if args.first.is_a?(String) && http_method != 'HEAD'
+ @request.env['RAW_POST_DATA'] = args.shift
+ end
+
+ parameters, session, flash = args
+
# Ensure that numbers and symbols passed as params are converted to
# proper params, as is the case when engaging rack.
parameters = paramify_values(parameters)
- # Sanity check for required instance variables so we can give an
- # understandable error message.
- %w(@routes @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
@@ -494,6 +504,26 @@ module ActionController
end
private
+ def check_required_ivars
+ # Sanity check for required instance variables so we can give an
+ # understandable error message.
+ [:@routes, :@controller, :@request, :@response].each do |iv_name|
+ if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
+ end
+ end
+ end
+
+ def handle_old_process_api(http_method, args)
+ # 4.0: Remove this method.
+ if http_method.is_a?(Hash)
+ ActiveSupport::Deprecation.warn("TestCase#process now expects the HTTP method as second argument: process(action, http_method, params, session, flash)")
+ args.unshift(http_method)
+ http_method = args.last.is_a?(String) ? args.last : "GET"
+ end
+
+ [http_method, args]
+ end
def build_request_uri(action, parameters)
unless @request.env["PATH_INFO"]
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 af06bffa16..24ffc28710 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -171,7 +171,7 @@ module HTML
def contains_bad_protocols?(attr_name, value)
uri_attributes.include?(attr_name) &&
- (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
+ (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip))
end
end
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
index c252e01cf5..8ac8d34430 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
@@ -23,7 +23,7 @@ module HTML #:nodoc:
# Create a new Tokenizer for the given text.
def initialize(text)
- text.encode! if text.encoding_aware?
+ text.encode!
@scanner = StringScanner.new(text)
@position = 0
@line = 0
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 1e92d14542..a9542a7d1b 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2011 David Heinemeier Hansson
+# Copyright (c) 2004-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -29,6 +29,7 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include
require 'active_support'
require 'active_support/dependencies/autoload'
+require 'active_support/core_ext/module/attribute_accessors'
require 'action_pack'
require 'active_model'
@@ -51,17 +52,18 @@ module ActionDispatch
autoload :BestStandardsSupport
autoload :Callbacks
autoload :Cookies
+ autoload :DebugExceptions
+ autoload :ExceptionWrapper
autoload :Flash
autoload :Head
autoload :ParamsParser
+ autoload :PublicExceptions
autoload :Reloader
autoload :RemoteIp
- autoload :Rescue
autoload :ShowExceptions
autoload :Static
end
- autoload :ClosedError, 'action_dispatch/middleware/closed_error'
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
autoload :Routing
@@ -86,6 +88,8 @@ module ActionDispatch
autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
end
+ mattr_accessor :test_app
+
autoload_under 'testing' do
autoload :Assertions
autoload :Integration
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 7d83ecb058..5ee4c044ea 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -4,14 +4,18 @@ module ActionDispatch
module Http
module Cache
module Request
+
+ HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
+ HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
+
def if_modified_since
- if since = env['HTTP_IF_MODIFIED_SINCE']
+ if since = env[HTTP_IF_MODIFIED_SINCE]
Time.rfc2822(since) rescue nil
end
end
def if_none_match
- env['HTTP_IF_NONE_MATCH']
+ env[HTTP_IF_NONE_MATCH]
end
def not_modified?(modified_at)
@@ -43,17 +47,17 @@ module ActionDispatch
alias :etag? :etag
def last_modified
- if last = headers['Last-Modified']
+ if last = headers[LAST_MODIFIED]
Time.httpdate(last)
end
end
def last_modified?
- headers.include?('Last-Modified')
+ headers.include?(LAST_MODIFIED)
end
def last_modified=(utc_time)
- headers['Last-Modified'] = utc_time.httpdate
+ headers[LAST_MODIFIED] = utc_time.httpdate
end
def date
@@ -72,16 +76,20 @@ module ActionDispatch
def etag=(etag)
key = ActiveSupport::Cache.expand_cache_key(etag)
- @etag = self["ETag"] = %("#{Digest::MD5.hexdigest(key)}")
+ @etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
end
private
+ LAST_MODIFIED = "Last-Modified".freeze
+ ETAG = "ETag".freeze
+ CACHE_CONTROL = "Cache-Control".freeze
+
def prepare_cache_control!
@cache_control = {}
- @etag = self["ETag"]
+ @etag = self[ETAG]
- if cache_control = self["Cache-Control"]
+ if cache_control = self[CACHE_CONTROL]
cache_control.split(/,\s*/).each do |segment|
first, last = segment.split("=")
@cache_control[first.to_sym] = last || true
@@ -95,28 +103,32 @@ module ActionDispatch
end
end
- DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
+ NO_CACHE = "no-cache".freeze
+ PUBLIC = "public".freeze
+ PRIVATE = "private".freeze
+ MUST_REVALIDATE = "must-revalidate".freeze
def set_conditional_cache_control!
- return if self["Cache-Control"].present?
+ return if self[CACHE_CONTROL].present?
control = @cache_control
if control.empty?
- headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
+ headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
elsif control[:no_cache]
- headers["Cache-Control"] = "no-cache"
+ headers[CACHE_CONTROL] = NO_CACHE
else
extras = control[:extras]
max_age = control[:max_age]
options = []
options << "max-age=#{max_age.to_i}" if max_age
- options << (control[:public] ? "public" : "private")
- options << "must-revalidate" if control[:must_revalidate]
+ options << (control[:public] ? PUBLIC : PRIVATE)
+ options << MUST_REVALIDATE if control[:must_revalidate]
options.concat(extras) if extras
- headers["Cache-Control"] = options.join(", ")
+ headers[CACHE_CONTROL] = options.join(", ")
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 8dd1af7f3d..132b0c82bc 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -50,7 +50,7 @@ module ActionDispatch
end
def env_filter
- parameter_filter_for(Array.wrap(@env["action_dispatch.parameter_filter"]) << /RAW_POST_DATA/)
+ parameter_filter_for(Array(@env["action_dispatch.parameter_filter"]) + [/RAW_POST_DATA/, "rack.request.form_vars"])
end
def parameter_filter_for(filters)
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 505d5560b1..040b51e040 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -1,5 +1,3 @@
-require 'active_support/memoizable'
-
module ActionDispatch
module Http
class Headers < ::Hash
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 8a9f9c4315..2152351703 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -82,6 +82,7 @@ module Mime
class << self
TRAILING_STAR_REGEXP = /(text|application)\/\*/
+ Q_SEPARATOR_REGEXP = /;\s*q=/
def lookup(string)
LOOKUP[string]
@@ -103,11 +104,12 @@ module Mime
SET << Mime.const_get(symbol.to_s.upcase)
([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
- ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last }
+ ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last }
end
def parse(accept_header)
if accept_header !~ /,/
+ accept_header = accept_header.split(Q_SEPARATOR_REGEXP).first
if accept_header =~ TRAILING_STAR_REGEXP
parse_data_with_trailing_star($1)
else
@@ -117,7 +119,7 @@ module Mime
# keep track of creation order to keep the subsequent sort stable
list, index = [], 0
accept_header.split(/,/).each do |header|
- params, q = header.split(/;\s*q=/)
+ params, q = header.split(Q_SEPARATOR_REGEXP)
if params.present?
params.strip!
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 3da4f91051..a6b3aee5e7 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -9,7 +9,7 @@ Mime::Type.register "text/calendar", :ics
Mime::Type.register "text/csv", :csv
Mime::Type.register "image/png", :png, [], %w(png)
-Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe)
+Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
Mime::Type.register "image/gif", :gif, [], %w(gif)
Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index 1480e8f77c..490b46c990 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -20,6 +20,8 @@ module ActionDispatch
@filters.present?
end
+ FILTERED = '[FILTERED]'.freeze
+
def compiled_filter
@compiled_filter ||= begin
regexps, blocks = compile_filter
@@ -29,7 +31,7 @@ module ActionDispatch
original_params.each do |key, value|
if regexps.find { |r| key =~ r }
- value = '[FILTERED]'
+ value = FILTERED
elsif value.is_a?(Hash)
value = filter(value)
elsif value.is_a?(Array)
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index ef5d207b26..d9b63faf5e 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -41,8 +41,6 @@ module ActionDispatch
# you'll get a weird error down the road, but our form handling
# should really prevent that from happening
def encode_params(params)
- return params unless "ruby".encoding_aware?
-
if params.is_a?(String)
return params.force_encoding("UTF-8").encode!
elsif !params.is_a?(Hash)
diff --git a/actionpack/lib/action_dispatch/http/rack_cache.rb b/actionpack/lib/action_dispatch/http/rack_cache.rb
index cc8edee300..003ae4029d 100644
--- a/actionpack/lib/action_dispatch/http/rack_cache.rb
+++ b/actionpack/lib/action_dispatch/http/rack_cache.rb
@@ -8,8 +8,7 @@ module ActionDispatch
new
end
- # TODO: Finally deal with the RAILS_CACHE global
- def initialize(store = RAILS_CACHE)
+ def initialize(store = Rails.cache)
@store = store
end
@@ -33,7 +32,7 @@ module ActionDispatch
new
end
- def initialize(store = RAILS_CACHE)
+ def initialize(store = Rails.cache)
@store = store
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 7a5237dcf3..0a0ebe7fad 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -35,14 +35,6 @@ module ActionDispatch
METHOD
end
- def self.new(env)
- if request = env["action_dispatch.request"] && request.instance_of?(self)
- return request
- end
-
- super
- end
-
def key?(key)
@env.key?(key)
end
@@ -94,31 +86,31 @@ module ActionDispatch
end
# Is this a GET (or HEAD) request?
- # Equivalent to <tt>request.request_method == :get</tt>.
+ # Equivalent to <tt>request.request_method_symbol == :get</tt>.
def get?
HTTP_METHOD_LOOKUP[request_method] == :get
end
# Is this a POST request?
- # Equivalent to <tt>request.request_method == :post</tt>.
+ # Equivalent to <tt>request.request_method_symbol == :post</tt>.
def post?
HTTP_METHOD_LOOKUP[request_method] == :post
end
# Is this a PUT request?
- # Equivalent to <tt>request.request_method == :put</tt>.
+ # Equivalent to <tt>request.request_method_symbol == :put</tt>.
def put?
HTTP_METHOD_LOOKUP[request_method] == :put
end
# Is this a DELETE request?
- # Equivalent to <tt>request.request_method == :delete</tt>.
+ # Equivalent to <tt>request.request_method_symbol == :delete</tt>.
def delete?
HTTP_METHOD_LOOKUP[request_method] == :delete
end
# Is this a HEAD request?
- # Equivalent to <tt>request.method == :head</tt>.
+ # Equivalent to <tt>request.method_symbol == :head</tt>.
def head?
HTTP_METHOD_LOOKUP[method] == :head
end
@@ -130,10 +122,18 @@ module ActionDispatch
Http::Headers.new(@env)
end
+ def original_fullpath
+ @original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
+ end
+
def fullpath
@fullpath ||= super
end
+ def original_url
+ base_url + original_fullpath
+ end
+
def media_type
content_mime_type.to_s
end
@@ -155,24 +155,7 @@ module ActionDispatch
@ip ||= super
end
- # Which IP addresses are "trusted proxies" that can be stripped from
- # the right-hand-side of X-Forwarded-For.
- #
- # http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces.
- TRUSTED_PROXIES = %r{
- ^127\.0\.0\.1$ | # localhost
- ^(10 | # private IP 10.x.x.x
- 172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
- 192\.168 # private IP 192.168.x.x
- )\.
- }x
-
- # Determines originating IP address. REMOTE_ADDR is the standard
- # but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
- # HTTP_X_FORWARDED_FOR are set by proxies so check for these if
- # REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
- # delimited list in the case of multiple chained proxies; the last
- # address which is not trusted is the originating IP.
+ # Originating IP address, usually set by the RemoteIp middleware.
def remote_ip
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
@@ -206,7 +189,7 @@ module ActionDispatch
# variable is already set, wrap it in a StringIO.
def body
if raw_post = @env['RAW_POST_DATA']
- raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
+ raw_post.force_encoding(Encoding::BINARY)
StringIO.new(raw_post)
else
@env['rack.input']
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index f1e85559a3..84732085f0 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -53,8 +53,10 @@ module ActionDispatch # :nodoc:
# information.
attr_accessor :charset, :content_type
- CONTENT_TYPE = "Content-Type"
-
+ CONTENT_TYPE = "Content-Type".freeze
+ SET_COOKIE = "Set-Cookie".freeze
+ LOCATION = "Location".freeze
+
cattr_accessor(:default_charset) { "utf-8" }
include Rack::Response::Helpers
@@ -66,10 +68,10 @@ module ActionDispatch # :nodoc:
@sending_file = false
@blank = false
- if content_type = self["Content-Type"]
+ if content_type = self[CONTENT_TYPE]
type, charset = content_type.split(/;\s*charset=/)
@content_type = Mime::Type.lookup(type)
- @charset = charset || "UTF-8"
+ @charset = charset || self.class.default_charset
end
prepare_cache_control!
@@ -109,9 +111,9 @@ module ActionDispatch # :nodoc:
end
def body
- str = ''
- each { |part| str << part.to_s }
- str
+ strings = []
+ each { |part| strings << part.to_s }
+ strings.join
end
EMPTY = " "
@@ -119,14 +121,7 @@ module ActionDispatch # :nodoc:
def body=(body)
@blank = true if body == EMPTY
- # Explicitly check for strings. This is *wrong* theoretically
- # but if we don't check this, the performance on string bodies
- # is bad on Ruby 1.8 (because strings responds to each then).
- @body = if body.respond_to?(:to_str) || !body.respond_to?(:each)
- [body]
- else
- body
- end
+ @body = body.respond_to?(:each) ? body : [body]
end
def body_parts
@@ -142,12 +137,12 @@ module ActionDispatch # :nodoc:
end
def location
- headers['Location']
+ headers[LOCATION]
end
alias_method :redirect_url, :location
def location=(url)
- headers['Location'] = url
+ headers[LOCATION] = url
end
def close
@@ -158,10 +153,10 @@ module ActionDispatch # :nodoc:
assign_default_content_type_and_charset!
handle_conditional_get!
- @header["Set-Cookie"] = @header["Set-Cookie"].join("\n") if @header["Set-Cookie"].respond_to?(:join)
+ @header[SET_COOKIE] = @header[SET_COOKIE].join("\n") if @header[SET_COOKIE].respond_to?(:join)
if [204, 304].include?(@status)
- @header.delete "Content-Type"
+ @header.delete CONTENT_TYPE
[@status, @header, []]
else
[@status, @header, self]
@@ -175,7 +170,7 @@ module ActionDispatch # :nodoc:
# assert_equal 'AuthorOfNewPage', r.cookies['author']
def cookies
cookies = {}
- if header = self["Set-Cookie"]
+ if header = self[SET_COOKIE]
header = header.split("\n") if header.respond_to?(:to_str)
header.each do |cookie|
if pair = cookie.split(';').first
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 94fa747a79..5ab99d1061 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -22,8 +22,8 @@ module ActionDispatch
private
def encode_filename(filename)
- # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
- if "ruby".encoding_aware? && filename
+ # Encode the filename in the utf8 encoding, unless it is nil
+ if filename
filename.force_encoding("UTF-8").encode!
else
filename
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index c8ddd07bfa..80ffbe575b 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -1,6 +1,8 @@
module ActionDispatch
module Http
module URL
+ IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
+
mattr_accessor :tld_length
self.tld_length = 1
@@ -21,7 +23,7 @@ module ActionDispatch
end
def url_for(options = {})
- unless options[:host].present? || options[:only_path].present?
+ if options[:host].blank? && options[:only_path].blank?
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
end
@@ -52,7 +54,7 @@ module ActionDispatch
private
def named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+ host && IP_HOST_REGEXP !~ host
end
def rewrite_authentication(options)
@@ -64,13 +66,13 @@ module ActionDispatch
end
def host_or_subdomain_and_domain(options)
- return options[:host] if options[:subdomain].nil? && options[:domain].nil?
+ return options[:host] if !named_host?(options[:host]) || (options[:subdomain].nil? && options[:domain].nil?)
tld_length = options[:tld_length] || @@tld_length
host = ""
unless options[:subdomain] == false
- host << (options[:subdomain] || extract_subdomain(options[:host], tld_length))
+ host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)).to_param
host << "."
end
host << (options[:domain] || extract_domain(options[:host], tld_length))
@@ -167,7 +169,7 @@ module ActionDispatch
# such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
# in "www.rubyonrails.co.uk".
def subdomain(tld_length = @@tld_length)
- subdomains(tld_length).join(".")
+ ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/closed_error.rb b/actionpack/lib/action_dispatch/middleware/closed_error.rb
deleted file mode 100644
index 0a4db47f4b..0000000000
--- a/actionpack/lib/action_dispatch/middleware/closed_error.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module ActionDispatch
- class ClosedError < StandardError #:nodoc:
- def initialize(kind)
- super "Cannot modify #{kind} because it was closed. This means it was already streamed back to the client or converted to HTTP headers."
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index a4ffd40a66..25f1db8228 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -121,10 +121,6 @@ module ActionDispatch
@cookies = {}
end
- attr_reader :closed
- alias :closed? :closed
- def close!; @closed = true end
-
def each(&block)
@cookies.each(&block)
end
@@ -165,7 +161,6 @@ module ActionDispatch
# Sets the cookie named +name+. The second argument may be the very cookie
# value, or a hash of options as documented above.
def []=(key, options)
- raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
value = options[:value]
@@ -196,6 +191,15 @@ module ActionDispatch
value
end
+ # Whether the given cookie is to be deleted by this CookieJar.
+ # Like <tt>[]=</tt>, you can pass in an options hash to test if a
+ # deletion applies to a specific <tt>:path</tt>, <tt>:domain</tt> etc.
+ def deleted?(key, options = {})
+ options.symbolize_keys!
+ handle_options(options)
+ @delete_cookies[key.to_s] == options
+ end
+
# Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
def clear(options = {})
@cookies.each_key{ |k| delete(k, options) }
@@ -243,10 +247,13 @@ module ActionDispatch
@delete_cookies.clear
end
+ mattr_accessor :always_write_cookie
+ self.always_write_cookie = false
+
private
def write_cookie?(cookie)
- @secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
+ @secure || !cookie[:secure] || always_write_cookie
end
end
@@ -256,7 +263,6 @@ module ActionDispatch
end
def []=(key, options)
- raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
else
@@ -295,7 +301,6 @@ module ActionDispatch
end
def []=(key, options)
- raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
options[:value] = @verifier.generate(options[:value])
@@ -349,9 +354,6 @@ module ActionDispatch
end
[status, headers, body]
- ensure
- cookie_jar = ActionDispatch::Request.new(env).cookie_jar unless cookie_jar
- cookie_jar.close!
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
new file mode 100644
index 0000000000..b903f98761
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -0,0 +1,82 @@
+require 'action_dispatch/http/request'
+require 'action_dispatch/middleware/exception_wrapper'
+
+module ActionDispatch
+ # This middleware is responsible for logging exceptions and
+ # showing a debugging page in case the request is local.
+ class DebugExceptions
+ RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ begin
+ response = @app.call(env)
+
+ if response[1]['X-Cascade'] == 'pass'
+ body = response[2]
+ body.close if body.respond_to?(:close)
+ raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
+ end
+ rescue Exception => exception
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ end
+
+ exception ? render_exception(env, exception) : response
+ end
+
+ private
+
+ def render_exception(env, exception)
+ wrapper = ExceptionWrapper.new(env, exception)
+ log_error(env, wrapper)
+
+ if env['action_dispatch.show_detailed_exceptions']
+ template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
+ :request => Request.new(env),
+ :exception => wrapper.exception,
+ :application_trace => wrapper.application_trace,
+ :framework_trace => wrapper.framework_trace,
+ :full_trace => wrapper.full_trace
+ )
+
+ file = "rescues/#{wrapper.rescue_template}"
+ body = template.render(:template => file, :layout => 'rescues/layout')
+ render(wrapper.status_code, body)
+ else
+ raise exception
+ end
+ end
+
+ def render(status, body)
+ [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
+ end
+
+ def log_error(env, wrapper)
+ logger = logger(env)
+ return unless logger
+
+ exception = wrapper.exception
+
+ trace = wrapper.application_trace
+ trace = wrapper.framework_trace if trace.empty?
+
+ ActiveSupport::Deprecation.silence do
+ message = "\n#{exception.class} (#{exception.message}):\n"
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
+ message << " " << trace.join("\n ")
+ logger.fatal("#{message}\n\n")
+ end
+ end
+
+ def logger(env)
+ env['action_dispatch.logger'] || stderr_logger
+ end
+
+ def stderr_logger
+ @stderr_logger ||= ActiveSupport::Logger.new($stderr)
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
new file mode 100644
index 0000000000..c0532c80c4
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -0,0 +1,78 @@
+require 'action_controller/metal/exceptions'
+require 'active_support/core_ext/exception'
+
+module ActionDispatch
+ class ExceptionWrapper
+ cattr_accessor :rescue_responses
+ @@rescue_responses = Hash.new(:internal_server_error)
+ @@rescue_responses.merge!(
+ 'ActionController::RoutingError' => :not_found,
+ 'AbstractController::ActionNotFound' => :not_found,
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
+ 'ActionController::NotImplemented' => :not_implemented,
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
+ )
+
+ cattr_accessor :rescue_templates
+ @@rescue_templates = Hash.new('diagnostics')
+ @@rescue_templates.merge!(
+ 'ActionView::MissingTemplate' => 'missing_template',
+ 'ActionController::RoutingError' => 'routing_error',
+ 'AbstractController::ActionNotFound' => 'unknown_action',
+ 'ActionView::Template::Error' => 'template_error'
+ )
+
+ attr_reader :env, :exception
+
+ def initialize(env, exception)
+ @env = env
+ @exception = original_exception(exception)
+ end
+
+ def rescue_template
+ @@rescue_templates[@exception.class.name]
+ end
+
+ def status_code
+ Rack::Utils.status_code(@@rescue_responses[@exception.class.name])
+ end
+
+ def application_trace
+ clean_backtrace(:silent)
+ end
+
+ def framework_trace
+ clean_backtrace(:noise)
+ end
+
+ def full_trace
+ clean_backtrace(:all)
+ end
+
+ private
+
+ def original_exception(exception)
+ if registered_original_exception?(exception)
+ exception.original_exception
+ else
+ exception
+ end
+ end
+
+ def registered_original_exception?(exception)
+ exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
+ end
+
+ def clean_backtrace(*args)
+ if backtrace_cleaner
+ backtrace_cleaner.clean(@exception.backtrace, *args)
+ else
+ @exception.backtrace
+ end
+ end
+
+ def backtrace_cleaner
+ @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index e59404ef68..cff0877030 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -78,7 +78,7 @@ module ActionDispatch
include Enumerable
def initialize #:nodoc:
- @used = Set.new
+ @discard = Set.new
@closed = false
@flashes = {}
@now = nil
@@ -93,8 +93,7 @@ module ActionDispatch
end
def []=(k, v) #:nodoc:
- raise ClosedError, :flash if closed?
- keep(k)
+ @discard.delete k
@flashes[k] = v
end
@@ -103,7 +102,7 @@ module ActionDispatch
end
def update(h) #:nodoc:
- h.keys.each { |k| keep(k) }
+ @discard.subtract h.keys
@flashes.update h
self
end
@@ -117,6 +116,7 @@ module ActionDispatch
end
def delete(key)
+ @discard.delete key
@flashes.delete key
self
end
@@ -130,6 +130,7 @@ module ActionDispatch
end
def clear
+ @discard.clear
@flashes.clear
end
@@ -140,7 +141,7 @@ module ActionDispatch
alias :merge! :update
def replace(h) #:nodoc:
- @used = Set.new
+ @discard.clear
@flashes.replace h
self
end
@@ -159,16 +160,13 @@ module ActionDispatch
@now ||= FlashNow.new(self)
end
- attr_reader :closed
- alias :closed? :closed
- def close!; @closed = true; end
-
# Keeps either the entire current flash or a specific flash entry available for the next action:
#
# flash.keep # keeps the entire flash
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
def keep(k = nil)
- use(k, false)
+ @discard.subtract Array(k || keys)
+ k ? self[k] : self
end
# Marks the entire flash or a single flash entry to be discarded by the end of the current action:
@@ -176,24 +174,16 @@ module ActionDispatch
# flash.discard # discard the entire flash at the end of the current action
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
def discard(k = nil)
- use(k)
+ @discard.merge Array(k || keys)
+ k ? self[k] : self
end
# Mark for removal entries that were kept, and delete unkept ones.
#
# This method is called automatically by filters, so you generally don't need to care about it.
def sweep #:nodoc:
- keys.each do |k|
- unless @used.include?(k)
- @used << k
- else
- delete(k)
- @used.delete(k)
- end
- end
-
- # clean up after keys that could have been left over by calling reject! or shift on the flash
- (@used - keys).each{ |k| @used.delete(k) }
+ @discard.each { |k| @flashes.delete k }
+ @discard.replace @flashes.keys
end
# Convenience accessor for flash[:alert]
@@ -217,22 +207,9 @@ module ActionDispatch
end
protected
-
- def now_is_loaded?
- !!@now
- end
-
- # 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)
- # 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 ? @used << k : @used.delete(k) }
- return key ? self[key] : self
- end
+ def now_is_loaded?
+ @now
+ end
end
def initialize(app)
@@ -258,7 +235,6 @@ module ActionDispatch
end
env[KEY] = new_hash
- new_hash.close!
end
if session.key?('flash') && session['flash'].empty?
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index d4208ca96e..1cb803ffb9 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -52,14 +52,9 @@ module ActionDispatch
false
end
rescue Exception => e # YAML, XML or Ruby code block errors
- logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
+ logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
- raise
- { "body" => request.raw_post,
- "content_type" => request.content_mime_type,
- "content_length" => request.content_length,
- "exception" => "#{e.message} (#{e.class})",
- "backtrace" => e.backtrace }
+ raise e
end
def content_type_from_legacy_post_data_format_header(env)
@@ -73,8 +68,8 @@ module ActionDispatch
nil
end
- def logger
- defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
+ def logger(env)
+ env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
new file mode 100644
index 0000000000..85b8d178bf
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
@@ -0,0 +1,30 @@
+module ActionDispatch
+ # A simple Rack application that renders exceptions in the given public path.
+ class PublicExceptions
+ attr_accessor :public_path
+
+ def initialize(public_path)
+ @public_path = public_path
+ end
+
+ def call(env)
+ status = env["PATH_INFO"][1..-1]
+ 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
+ [404, { "X-Cascade" => "pass" }, []]
+ end
+ end
+
+ private
+
+ def render(status, body)
+ [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb
index 29289a76b4..a0388e0e13 100644
--- a/actionpack/lib/action_dispatch/middleware/reloader.rb
+++ b/actionpack/lib/action_dispatch/middleware/reloader.rb
@@ -43,34 +43,47 @@ module ActionDispatch
# Execute all prepare callbacks.
def self.prepare!
- new(nil).run_callbacks :prepare
+ new(nil).prepare!
end
# Execute all cleanup callbacks.
def self.cleanup!
- new(nil).run_callbacks :cleanup
+ new(nil).cleanup!
end
- def initialize(app)
+ def initialize(app, condition=nil)
@app = app
- end
-
- module CleanupOnClose
- def close
- super if defined?(super)
- ensure
- ActionDispatch::Reloader.cleanup!
- end
+ @condition = condition || lambda { true }
+ @validated = true
end
def call(env)
- run_callbacks :prepare
+ @validated = @condition.call
+ prepare!
+
response = @app.call(env)
- response[2].extend(CleanupOnClose)
+ response[2] = ::Rack::BodyProxy.new(response[2]) { cleanup! }
+
response
rescue Exception
- run_callbacks :cleanup
+ cleanup!
raise
end
+
+ def prepare! #:nodoc:
+ run_callbacks :prepare if validated?
+ end
+
+ def cleanup! #:nodoc:
+ run_callbacks :cleanup if validated?
+ ensure
+ @validated = true
+ end
+
+ private
+
+ def validated? #:nodoc:
+ @validated
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index c7d710b98e..d924f21fad 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -2,50 +2,82 @@ module ActionDispatch
class RemoteIp
class IpSpoofAttackError < StandardError ; end
- class RemoteIpGetter
- def initialize(env, check_ip_spoofing, trusted_proxies)
- @env = env
- @check_ip_spoofing = check_ip_spoofing
- @trusted_proxies = trusted_proxies
+ # IP addresses that are "trusted proxies" that can be stripped from
+ # the comma-delimited list in the X-Forwarded-For header. See also:
+ # http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
+ TRUSTED_PROXIES = %r{
+ ^127\.0\.0\.1$ | # localhost
+ ^(10 | # private IP 10.x.x.x
+ 172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
+ 192\.168 # private IP 192.168.x.x
+ )\.
+ }x
+
+ attr_reader :check_ip, :proxies
+
+ def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
+ @app = app
+ @check_ip = check_ip_spoofing
+ @proxies = case custom_proxies
+ when Regexp
+ custom_proxies
+ when nil
+ TRUSTED_PROXIES
+ else
+ Regexp.union(TRUSTED_PROXIES, custom_proxies)
end
+ end
- def remote_addrs
- @remote_addrs ||= begin
- list = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
- list.reject { |addr| addr =~ @trusted_proxies }
- end
+ def call(env)
+ env["action_dispatch.remote_ip"] = GetIp.new(env, self)
+ @app.call(env)
+ end
+
+ class GetIp
+ def initialize(env, middleware)
+ @env = env
+ @middleware = middleware
+ @calculated_ip = false
end
- def to_s
- return remote_addrs.first if remote_addrs.any?
-
- forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
-
- if client_ip = @env['HTTP_CLIENT_IP']
- if @check_ip_spoofing && !forwarded_ips.include?(client_ip)
- # We don't know which came from the proxy, and which from the user
- raise IpSpoofAttackError, "IP spoofing attack?!" \
- "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
- "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
- end
- return client_ip
+ # Determines originating IP address. REMOTE_ADDR is the standard
+ # but will be wrong if the user is behind a proxy. Proxies will set
+ # HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR, so we prioritize those.
+ # HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of
+ # multiple chained proxies. The last address which is not a known proxy
+ # will be the originating IP.
+ def calculate_ip
+ client_ip = @env['HTTP_CLIENT_IP']
+ forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR')
+ remote_addrs = ips_from('REMOTE_ADDR')
+
+ check_ip = client_ip && @middleware.check_ip
+ if check_ip && !forwarded_ips.include?(client_ip)
+ # We don't know which came from the proxy, and which from the user
+ raise IpSpoofAttackError, "IP spoofing attack?!" \
+ "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
+ "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
end
- return forwarded_ips.reject { |ip| ip =~ @trusted_proxies }.last || @env["REMOTE_ADDR"]
+ not_proxy = client_ip || forwarded_ips.first || remote_addrs.first
+
+ # Return first REMOTE_ADDR if there are no other options
+ not_proxy || ips_from('REMOTE_ADDR', :allow_proxies).first
end
- end
- def initialize(app, check_ip_spoofing = true, trusted_proxies = nil)
- @app = app
- @check_ip_spoofing = check_ip_spoofing
- regex = '(^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.)'
- regex << "|(#{trusted_proxies})" if trusted_proxies
- @trusted_proxies = Regexp.new(regex, "i")
- end
+ def to_s
+ return @ip if @calculated_ip
+ @calculated_ip = true
+ @ip = calculate_ip
+ end
- def call(env)
- env["action_dispatch.remote_ip"] = RemoteIpGetter.new(env, @check_ip_spoofing, @trusted_proxies)
- @app.call(env)
+ protected
+
+ def ips_from(header, allow_proxies = false)
+ ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
+ allow_proxies ? ips : ips.reject{|ip| ip =~ @middleware.proxies }
+ end
end
+
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb
index bee446c8a5..d5a0b80fd5 100644
--- a/actionpack/lib/action_dispatch/middleware/request_id.rb
+++ b/actionpack/lib/action_dispatch/middleware/request_id.rb
@@ -33,7 +33,7 @@ module ActionDispatch
end
def internal_request_id
- SecureRandom.hex(16)
+ SecureRandom.uuid
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/rescue.rb b/actionpack/lib/action_dispatch/middleware/rescue.rb
deleted file mode 100644
index aee672112c..0000000000
--- a/actionpack/lib/action_dispatch/middleware/rescue.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module ActionDispatch
- class Rescue
- def initialize(app, rescuers = {}, &block)
- @app, @rescuers = app, {}
- rescuers.each { |exception, rescuer| rescue_from(exception, rescuer) }
- instance_eval(&block) if block_given?
- end
-
- def call(env)
- @app.call(env)
- rescue Exception => exception
- if rescuer = @rescuers[exception.class.name]
- env['action_dispatch.rescue.exception'] = exception
- rescuer.call(env)
- else
- raise exception
- end
- end
-
- protected
- def rescue_from(exception, rescuer)
- exception = exception.class.name if exception.is_a?(Exception)
- @rescuers[exception.to_s] = rescuer
- 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 6bcf099d2c..6a8e690d18 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -30,7 +30,7 @@ module ActionDispatch
def generate_sid
sid = SecureRandom.hex(16)
- sid.encode!('UTF-8') if sid.respond_to?(:encode!)
+ sid.encode!('UTF-8')
sid
end
@@ -74,10 +74,6 @@ module ActionDispatch
class AbstractStore < Rack::Session::Abstract::ID
include Compatibility
include StaleSessionCheck
-
- def destroy_session(env, sid, options)
- raise '#destroy_session needs to be implemented.'
- end
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 2fa68c64c5..836136eb95 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -1,172 +1,57 @@
-require 'active_support/core_ext/exception'
-require 'action_controller/metal/exceptions'
-require 'active_support/notifications'
require 'action_dispatch/http/request'
+require 'action_dispatch/middleware/exception_wrapper'
module ActionDispatch
- # This middleware rescues any exception returned by the application and renders
- # nice exception pages if it's being rescued locally.
+ # This middleware rescues any exception returned by the application
+ # and calls an exceptions app that will wrap it in a format for the end user.
+ #
+ # The exceptions app should be passed as parameter on initialization
+ # of ShowExceptions. Everytime there is an exception, ShowExceptions will
+ # store the exception in env["action_dispatch.exception"], rewrite the
+ # PATH_INFO to the exception status code and call the rack app.
+ #
+ # If the application returns a "X-Cascade" pass response, this middleware
+ # will send an empty response as result with the correct status code.
+ # If any exception happens inside the exceptions app, this middleware
+ # catches the exceptions and returns a FAILSAFE_RESPONSE.
class ShowExceptions
- 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,
- 'AbstractController::ActionNotFound' => :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',
- 'AbstractController::ActionNotFound' => 'unknown_action',
- 'ActionView::Template::Error' => '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)
+ def initialize(app, exceptions_app)
@app = app
- @consider_all_requests_local = consider_all_requests_local
+ @exceptions_app = exceptions_app
end
def call(env)
begin
- status, headers, body = @app.call(env)
- exception = nil
-
- # Only this middleware cares about RoutingError. So, let's just raise
- # it here.
- if headers['X-Cascade'] == 'pass'
- raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
- end
+ response = @app.call(env)
rescue Exception => exception
raise exception if env['action_dispatch.show_exceptions'] == false
end
- exception ? render_exception(env, exception) : [status, headers, body]
+ response || render_exception(env, exception)
end
private
- def render_exception(env, exception)
- log_error(exception)
- exception = original_exception(exception)
-
- request = Request.new(env)
- if @consider_all_requests_local || request.local?
- rescue_action_locally(request, exception)
- else
- rescue_action_in_public(exception)
- end
- rescue Exception => failsafe_error
- $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
- 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,
- :application_trace => application_trace(exception),
- :framework_trace => framework_trace(exception),
- :full_trace => full_trace(exception)
- )
- file = "rescues/#{@@rescue_templates[exception.class.name]}"
- body = template.render(:template => file, :layout => 'rescues/layout')
- 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
-
- def status_code(exception)
- Rack::Utils.status_code(@@rescue_responses[exception.class.name])
- end
-
- def render(status, body)
- [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.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
- message = "\n#{exception.class} (#{exception.message}):\n"
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
- message << " " << application_trace(exception).join("\n ")
- logger.fatal("#{message}\n\n")
- end
- end
-
- def application_trace(exception)
- clean_backtrace(exception, :silent)
- end
-
- def framework_trace(exception)
- clean_backtrace(exception, :noise)
- end
-
- def full_trace(exception)
- clean_backtrace(exception, :all)
- end
-
- def clean_backtrace(exception, *args)
- defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
- Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
- exception.backtrace
- end
-
- def logger
- defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
- end
-
- def original_exception(exception)
- if registered_original_exception?(exception)
- exception.original_exception
- else
- exception
- end
+ def render_exception(env, exception)
+ wrapper = ExceptionWrapper.new(env, exception)
+ status = wrapper.status_code
+ env["action_dispatch.exception"] = wrapper.exception
+ env["PATH_INFO"] = "/#{status}"
+ response = @exceptions_app.call(env)
+ response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
+ rescue Exception => failsafe_error
+ $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
+ FAILSAFE_RESPONSE
end
- def registered_original_exception?(exception)
- exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
+ def pass_response(status)
+ [status, {"Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0"}, []]
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index a4308f528c..28e8fbdab8 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -93,8 +93,9 @@ module ActionDispatch
end
def swap(target, *args, &block)
- insert_before(target, *args, &block)
- delete(target)
+ index = assert_index(target, :before)
+ insert(index, *args, &block)
+ middlewares.delete_at(index + 1)
end
def delete(target)
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 404943d720..63b7422287 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -1,4 +1,5 @@
require 'rack/utils'
+require 'active_support/core_ext/uri'
module ActionDispatch
class FileHandler
@@ -11,14 +12,14 @@ module ActionDispatch
def match?(path)
path = path.dup
- full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path))
+ full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(unescape_path(path)))
paths = "#{full_path}#{ext}"
matches = Dir[paths]
match = matches.detect { |m| File.file?(m) }
if match
match.sub!(@compiled_root, '')
- match
+ ::Rack::Utils.escape(match)
end
end
@@ -32,6 +33,14 @@ module ActionDispatch
"{,#{ext},/index#{ext}}"
end
end
+
+ def unescape_path(path)
+ URI.parser.unescape(path)
+ end
+
+ def escape_glob_chars(path)
+ path.gsub(/[*?{}\[\]]/, "\\\\\\&")
+ end
end
class Static
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index 6e71fd7ddc..1a308707d1 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -16,6 +16,7 @@
background-color: #eee;
padding: 10px;
font-size: 11px;
+ white-space: pre-wrap;
}
a { color: #000; }
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
index ccfa858cce..f06c07daa5 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
@@ -1,10 +1,15 @@
<h1>Routing Error</h1>
<p><pre><%=h @exception.message %></pre></p>
-<% unless @exception.failures.empty? %><p>
- <h2>Failure reasons:</h2>
- <ol>
- <% @exception.failures.each do |route, reason| %>
- <li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li>
- <% end %>
- </ol>
-</p><% end %>
+<% unless @exception.failures.empty? %>
+ <p>
+ <h2>Failure reasons:</h2>
+ <ol>
+ <% @exception.failures.each do |route, reason| %>
+ <li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li>
+ <% end %>
+ </ol>
+ </p>
+<% end %>
+<p>
+ Try running <code>rake routes</code> for more information on available routes.
+</p> \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 1af89858d1..35f901c575 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -9,11 +9,28 @@ module ActionDispatch
config.action_dispatch.best_standards_support = true
config.action_dispatch.tld_length = 1
config.action_dispatch.ignore_accept_header = false
- config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true}
+ config.action_dispatch.rescue_templates = { }
+ config.action_dispatch.rescue_responses = { }
+ config.action_dispatch.default_charset = nil
+
+ config.action_dispatch.rack_cache = {
+ :metastore => "rails:/",
+ :entitystore => "rails:/",
+ :verbose => true
+ }
initializer "action_dispatch.configure" do |app|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
+ ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
+
+ ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses)
+ ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates)
+
+ config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
+ ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
+
+ ActionDispatch.test_app = app
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 1dcd83ceb5..107fe80d1f 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -190,7 +190,7 @@ module ActionDispatch
# Examples:
#
# match 'post/:id' => 'posts#show', :via => :get
- # match 'post/:id' => "posts#create_comment', :via => :post
+ # match 'post/:id' => 'posts#create_comment', :via => :post
#
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
# URL will route to the <tt>show</tt> action.
@@ -203,7 +203,7 @@ module ActionDispatch
# Examples:
#
# get 'post/:id' => 'posts#show'
- # post 'post/:id' => "posts#create_comment'
+ # post 'post/:id' => 'posts#create_comment'
#
# This syntax is less verbose and the intention is more apparent to someone else reading your code,
# however if your route needs to respond to more than one HTTP method (or all methods) then using the
@@ -277,7 +277,6 @@ module ActionDispatch
#
module Routing
autoload :Mapper, 'action_dispatch/routing/mapper'
- autoload :Route, 'action_dispatch/routing/route'
autoload :RouteSet, 'action_dispatch/routing/route_set'
autoload :RoutesProxy, 'action_dispatch/routing/routes_proxy'
autoload :UrlFor, 'action_dispatch/routing/url_for'
@@ -285,10 +284,5 @@ module ActionDispatch
SEPARATORS = %w( / . ? ) #:nodoc:
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc:
-
- # A helper module to hold URL related helpers.
- module Helpers #:nodoc:
- include PolymorphicRoutes
- end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 09a8c10043..cf9c0d7b6a 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -16,7 +16,7 @@ module ActionDispatch
end
end
- attr_reader :app
+ attr_reader :app, :constraints
def initialize(app, constraints, request)
@app, @constraints, @request = app, constraints, request
@@ -54,6 +54,7 @@ module ActionDispatch
def initialize(set, scope, path, options)
@set, @scope = set, scope
+ @segment_keys = nil
@options = (@scope[:options] || {}).merge(options)
@path = normalize_path(path)
normalize_options!
@@ -213,7 +214,9 @@ module ActionDispatch
end
def segment_keys
- @segment_keys ||= Journey::Path::Pattern.new(
+ return @segment_keys if @segment_keys
+
+ @segment_keys = Journey::Path::Pattern.new(
Journey::Router::Strexp.compile(@path, requirements, SEPARATORS)
).names
end
@@ -285,7 +288,7 @@ module ActionDispatch
# A pattern can also point to a +Rack+ endpoint i.e. anything that
# responds to +call+:
#
- # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon" }
+ # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon"] }
# match 'photos/:id' => PhotoRackApp
# # Yes, controller actions are just rack endpoints
# match 'photos/:id' => PhotosController.action(:show)
@@ -374,10 +377,6 @@ module ActionDispatch
# # Matches any request starting with 'path'
# match 'path' => 'c#a', :anchor => false
def match(path, options=nil)
- mapping = Mapping.new(@set, @scope, path, options || {})
- app, conditions, requirements, defaults, as, anchor = mapping.to_route
- @set.add_route(app, conditions, requirements, defaults, as, anchor)
- self
end
# Mount a Rack-based application to be used within the application.
@@ -468,7 +467,7 @@ module ActionDispatch
#
# get 'bacon', :to => 'food#bacon'
def get(*args, &block)
- map_method(:get, *args, &block)
+ map_method(:get, args, &block)
end
# Define a route that only recognizes HTTP POST.
@@ -478,7 +477,7 @@ module ActionDispatch
#
# post 'bacon', :to => 'food#bacon'
def post(*args, &block)
- map_method(:post, *args, &block)
+ map_method(:post, args, &block)
end
# Define a route that only recognizes HTTP PUT.
@@ -488,25 +487,24 @@ module ActionDispatch
#
# put 'bacon', :to => 'food#bacon'
def put(*args, &block)
- map_method(:put, *args, &block)
+ map_method(:put, args, &block)
end
- # Define a route that only recognizes HTTP PUT.
+ # Define a route that only recognizes HTTP DELETE.
# For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
# delete 'broccoli', :to => 'food#broccoli'
def delete(*args, &block)
- map_method(:delete, *args, &block)
+ map_method(:delete, args, &block)
end
private
- def map_method(method, *args, &block)
+ def map_method(method, args, &block)
options = args.extract_options!
options[:via] = method
- args.push(options)
- match(*args, &block)
+ match(*args, options, &block)
self
end
end
@@ -735,7 +733,7 @@ module ActionDispatch
# if the user should be given access to that route, or +false+ if the user should not.
#
# class Iphone
- # def self.matches(request)
+ # def self.matches?(request)
# request.env["HTTP_USER_AGENT"] =~ /iPhone/
# end
# end
@@ -867,8 +865,6 @@ module ActionDispatch
CANONICAL_ACTIONS = %w(index create new show update destroy)
class Resource #:nodoc:
- DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
-
attr_reader :controller, :path, :options
def initialize(entities, options = {})
@@ -880,7 +876,7 @@ module ActionDispatch
end
def default_actions
- self.class::DEFAULT_ACTIONS
+ [:index, :create, :new, :show, :update, :destroy, :edit]
end
def actions
@@ -934,16 +930,17 @@ module ActionDispatch
end
class SingletonResource < Resource #:nodoc:
- DEFAULT_ACTIONS = [:show, :create, :update, :destroy, :new, :edit]
-
def initialize(entities, options)
super
-
@as = nil
@controller = (options[:controller] || plural).to_s
@as = options[:as]
end
+ def default_actions
+ [:show, :create, :update, :destroy, :new, :edit]
+ end
+
def plural
@plural ||= name.to_s.pluralize
end
@@ -991,7 +988,7 @@ module ActionDispatch
return self
end
- resource_scope(SingletonResource.new(resources.pop, options)) do
+ resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
yield if block_given?
collection do
@@ -1023,6 +1020,7 @@ module ActionDispatch
# creates seven different routes in your application, all mapping to
# the +Photos+ controller:
#
+ # GET /photos
# GET /photos/new
# POST /photos
# GET /photos/:id
@@ -1038,6 +1036,7 @@ module ActionDispatch
#
# This generates the following comments routes:
#
+ # GET /photos/:photo_id/comments
# GET /photos/:photo_id/comments/new
# POST /photos/:photo_id/comments
# GET /photos/:photo_id/comments/:id
@@ -1049,13 +1048,20 @@ module ActionDispatch
# Takes same options as <tt>Base#match</tt> as well as:
#
# [:path_names]
- # Allows you to change the paths of the seven default actions.
- # Paths not specified are not changed.
+ # Allows you to change the segment component of the +edit+ and +new+ actions.
+ # Actions not specified are not changed.
#
# resources :posts, :path_names => { :new => "brand_new" }
#
# The above example will now change /posts/new to /posts/brand_new
#
+ # [:path]
+ # Allows you to change the path prefix for the resource.
+ #
+ # resources :posts, :path => 'postings'
+ #
+ # The resource and all segments will now route to /postings instead of /posts
+ #
# [:only]
# Only generate routes for the given actions.
#
@@ -1120,7 +1126,7 @@ module ActionDispatch
return self
end
- resource_scope(Resource.new(resources.pop, options)) do
+ resource_scope(:resources, Resource.new(resources.pop, options)) do
yield if block_given?
collection do
@@ -1243,32 +1249,47 @@ module ActionDispatch
parent_resource.instance_of?(Resource) && @scope[:shallow]
end
- def match(*args)
- options = args.extract_options!.dup
- options[:anchor] = true unless options.key?(:anchor)
-
- if args.length > 1
- args.each { |path| match(path, options.dup) }
- return self
+ # match 'path' => 'controller#action'
+ # match 'path', to: 'controller#action'
+ # match 'path', 'otherpath', on: :member, via: :get
+ def match(path, *rest)
+ if rest.empty? && Hash === path
+ options = path
+ path, to = options.find { |name, value| name.is_a?(String) }
+ options[:to] = to
+ options.delete(path)
+ paths = [path]
+ else
+ options = rest.pop || {}
+ paths = [path] + rest
end
- on = options.delete(:on)
- if VALID_ON_OPTIONS.include?(on)
- args.push(options)
- return send(on){ match(*args) }
- elsif on
+ options[:anchor] = true unless options.key?(:anchor)
+
+ if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
end
- if @scope[:scope_level] == :resources
- args.push(options)
- return nested { match(*args) }
- elsif @scope[:scope_level] == :resource
- args.push(options)
- return member { match(*args) }
+ paths.each { |_path| decomposed_match(_path, options.dup) }
+ self
+ end
+
+ def decomposed_match(path, options) # :nodoc:
+ if on = options.delete(:on)
+ send(on) { decomposed_match(path, options) }
+ else
+ case @scope[:scope_level]
+ when :resources
+ nested { decomposed_match(path, options) }
+ when :resource
+ member { decomposed_match(path, options) }
+ else
+ add_route(path, options)
+ end
end
+ end
- action = args.first
+ def add_route(action, options) # :nodoc:
path = path_for_action(action, options.delete(:path))
if action.to_s =~ /^[\w\/]+$/
@@ -1277,13 +1298,15 @@ module ActionDispatch
action = nil
end
- if options.key?(:as) && !options[:as]
+ if !options.fetch(:as, true)
options.delete(:as)
else
options[:as] = name_for_action(options[:as], action)
end
- super(path, options)
+ mapping = Mapping.new(@set, @scope, path, options)
+ app, conditions, requirements, defaults, as, anchor = mapping.to_route
+ @set.add_route(app, conditions, requirements, defaults, as, anchor)
end
def root(options={})
@@ -1339,7 +1362,7 @@ module ActionDispatch
end
def scope_action_options? #:nodoc:
- @scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
+ @scope[:options] && (@scope[:options][:only] || @scope[:options][:except])
end
def scope_action_options #:nodoc:
@@ -1347,11 +1370,11 @@ module ActionDispatch
end
def resource_scope? #:nodoc:
- @scope[:scope_level].in?([:resource, :resources])
+ [:resource, :resources].include? @scope[:scope_level]
end
def resource_method_scope? #:nodoc:
- @scope[:scope_level].in?([:collection, :member, :new])
+ [:collection, :member, :new].include? @scope[:scope_level]
end
def with_exclusive_scope
@@ -1376,8 +1399,8 @@ module ActionDispatch
@scope[:scope_level_resource] = old_resource
end
- def resource_scope(resource) #:nodoc:
- with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
+ def resource_scope(kind, resource) #:nodoc:
+ with_scope_level(kind, resource) do
scope(parent_resource.resource_scope) do
yield
end
@@ -1385,10 +1408,12 @@ module ActionDispatch
end
def nested_options #:nodoc:
- {}.tap do |options|
- options[:as] = parent_resource.member_name
- options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
- end
+ options = { :as => parent_resource.member_name }
+ options[:constraints] = {
+ :"#{parent_resource.singular}_id" => id_constraint
+ } if id_constraint?
+
+ options
end
def id_constraint? #:nodoc:
@@ -1419,8 +1444,7 @@ module ActionDispatch
end
def action_path(name, path = nil) #:nodoc:
- # Ruby 1.8 can't transform empty strings to symbols
- name = name.to_sym if name.is_a?(String) && !name.empty?
+ name = name.to_sym if name.is_a?(String)
path || @scope[:path_names][name] || name.to_s
end
@@ -1459,22 +1483,17 @@ module ActionDispatch
[name_prefix, member_name, prefix]
end
- candidate = name.select(&:present?).join("_").presence
- candidate unless as.nil? && @set.routes.find { |r| r.name == candidate }
- end
- end
-
- module Shorthand #:nodoc:
- def match(path, *rest)
- if rest.empty? && Hash === path
- options = path
- path, to = options.find { |name, value| name.is_a?(String) }
- options.merge!(:to => to).delete(path)
- super(path, options)
- else
- super
+ if candidate = name.select(&:present?).join("_").presence
+ # If a name was not explicitly given, we check if it is valid
+ # and return nil in case it isn't. Otherwise, we pass the invalid name
+ # forward so the underlying router engine treats it and raises an exception.
+ if as.nil?
+ candidate unless @set.routes.find { |r| r.name == candidate } || candidate !~ /\A[_a-z]/i
+ else
+ candidate
+ end
+ end
end
- end
end
def initialize(set) #:nodoc:
@@ -1487,7 +1506,6 @@ module ActionDispatch
include Redirection
include Scoping
include Resources
- include Shorthand
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index e989a38d8b..013cf93dbc 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -165,7 +165,7 @@ module ActionDispatch
if parent.is_a?(Symbol) || parent.is_a?(String)
parent
else
- ActiveModel::Naming.route_key(parent).singularize
+ ActiveModel::Naming.singular_route_key(parent)
end
end
else
@@ -176,9 +176,11 @@ module ActionDispatch
if record.is_a?(Symbol) || record.is_a?(String)
route << record
elsif record
- route << ActiveModel::Naming.route_key(record)
- route = [route.join("_").singularize] if inflection == :singular
- route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
+ if inflection == :singular
+ route << ActiveModel::Naming.singular_route_key(record)
+ else
+ route << ActiveModel::Naming.route_key(record)
+ end
else
raise ArgumentError, "Nil location provided. Can't build URI."
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 804991ad5f..617b24b46a 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -2,6 +2,54 @@ require 'action_dispatch/http/request'
module ActionDispatch
module Routing
+ class Redirect # :nodoc:
+ attr_reader :status, :block
+
+ def initialize(status, block)
+ @status = status
+ @block = block
+ end
+
+ def call(env)
+ req = Request.new(env)
+
+ uri = URI.parse(path(req.symbolized_path_parameters, req))
+ uri.scheme ||= req.scheme
+ uri.host ||= req.host
+ uri.port ||= req.port unless req.standard_port?
+
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
+
+ headers = {
+ 'Location' => uri.to_s,
+ 'Content-Type' => 'text/html',
+ 'Content-Length' => body.length.to_s
+ }
+
+ [ status, headers, [body] ]
+ end
+
+ def path(params, request)
+ block.call params, request
+ end
+ end
+
+ class OptionRedirect < Redirect # :nodoc:
+ alias :options :block
+
+ def path(params, request)
+ url_options = {
+ :protocol => request.protocol,
+ :host => request.host,
+ :port => request.optional_port,
+ :path => request.path,
+ :params => request.query_parameters
+ }.merge options
+
+ ActionDispatch::Http::URL.url_for url_options
+ end
+ end
+
module Redirection
# Redirect any path to another path:
@@ -40,71 +88,18 @@ module ActionDispatch
options = args.last.is_a?(Hash) ? args.pop : {}
status = options.delete(:status) || 301
- path = args.shift
+ return OptionRedirect.new(status, options) if options.any?
- path_proc = if path.is_a?(String)
- proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
- elsif options.any?
- options_proc(options)
- elsif path.respond_to?(:call)
- proc { |params, request| path.call(params, request) }
- elsif block
- block
- else
- raise ArgumentError, "redirection argument not supported"
- end
-
- redirection_proc(status, path_proc)
- end
+ path = args.shift
- private
-
- def options_proc(options)
- proc do |params, request|
- path = if options[:path].nil?
- request.path
- elsif params.empty? || !options[:path].match(/%\{\w*\}/)
- options.delete(:path)
- else
- (options.delete(:path) % params)
- end
-
- default_options = {
- :protocol => request.protocol,
- :host => request.host,
- :port => request.optional_port,
- :path => path,
- :params => request.query_parameters
- }
-
- ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options))
- end
- end
-
- def redirection_proc(status, path_proc)
- lambda do |env|
- req = Request.new(env)
-
- params = [req.symbolized_path_parameters]
- params << req if path_proc.arity > 1
-
- uri = URI.parse(path_proc.call(*params))
- uri.scheme ||= req.scheme
- uri.host ||= req.host
- uri.port ||= req.port unless req.standard_port?
-
- body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
- headers = {
- 'Location' => uri.to_s,
- 'Content-Type' => 'text/html',
- 'Content-Length' => body.length.to_s
- }
-
- [ status, headers, [body] ]
- end
- end
+ block = lambda { |params, request|
+ (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params)
+ } if String === path
+ block = path if path.respond_to? :call
+ raise ArgumentError, "redirection argument not supported" unless block
+ Redirect.new status, block
+ end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index e7bc431783..6c189fdba6 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -31,13 +31,14 @@ module ActionDispatch
end
def prepare_params!(params)
+ normalize_controller!(params)
merge_default_action!(params)
split_glob_param!(params) if @glob_param
end
# If this is a default_controller (i.e. a controller specified by the user)
# we should raise an error in case it's not found, because it usually means
- # an user error. However, if the controller was retrieved through a dynamic
+ # a user error. However, if the controller was retrieved through a dynamic
# segment, as in :controller(/:action), we should simply return nil and
# delegate the control back to Rack cascade. Besides, if this is not a default
# controller, it means we should respect the @scope[:module] parameter.
@@ -66,6 +67,10 @@ module ActionDispatch
controller.action(action).call(env)
end
+ def normalize_controller!(params)
+ params[:controller] = params[:controller].underscore if params.key?(:controller)
+ end
+
def merge_default_action!(params)
params[:action] ||= 'index'
end
@@ -83,7 +88,9 @@ module ActionDispatch
attr_reader :routes, :helpers, :module
def initialize
- clear!
+ @routes = {}
+ @helpers = []
+ @module = Module.new
end
def helper_names
@@ -91,12 +98,8 @@ module ActionDispatch
end
def clear!
- @routes = {}
- @helpers = []
-
- @module ||= Module.new do
- instance_methods.each { |selector| remove_method(selector) }
- end
+ @routes.clear
+ @helpers.clear
end
def add(name, route)
@@ -125,21 +128,6 @@ module ActionDispatch
routes.length
end
- def reset!
- old_routes = routes.dup
- clear!
- old_routes.each do |name, route|
- add(name, route)
- end
- end
-
- def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
- reset! if regenerate
- Array(destinations).each do |dest|
- dest.__send__(:include, @module)
- end
- end
-
private
def url_helper_name(name, kind = :url)
:"#{name}_#{kind}"
@@ -160,22 +148,23 @@ module ActionDispatch
def define_hash_access(route, name, kind, options)
selector = hash_access_name(name, kind)
- # We use module_eval to avoid leaks
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
- remove_possible_method :#{selector}
- def #{selector}(*args)
- options = args.extract_options!
- result = #{options.inspect}
+ @module.module_eval do
+ remove_possible_method selector
+
+ define_method(selector) do |*args|
+ inner_options = args.extract_options!
+ result = options.dup
if args.any?
result[:_positional_args] = args
- result[:_positional_keys] = #{route.segment_keys.inspect}
+ result[:_positional_keys] = route.segment_keys
end
- result.merge(options)
+ result.merge(inner_options)
end
- protected :#{selector}
- END_EVAL
+
+ protected selector
+ end
helpers << selector
end
@@ -287,14 +276,15 @@ module ActionDispatch
@prepend.each { |blk| eval_block(blk) }
end
- def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
- Array(destinations).each { |d| d.module_eval { include Helpers } }
- named_routes.install(destinations, regenerate_code)
- end
-
- module MountedHelpers
+ module MountedHelpers #:nodoc:
+ extend ActiveSupport::Concern
+ include UrlFor
end
+ # Contains all the mounted helpers accross different
+ # engines and the `main_app` helper for the application.
+ # You can include this in your classes if you want to
+ # access routes for other engines.
def mounted_helpers
MountedHelpers
end
@@ -305,7 +295,7 @@ module ActionDispatch
routes = self
MountedHelpers.class_eval do
define_method "_#{name}" do
- RoutesProxy.new(routes, self._routes_context)
+ RoutesProxy.new(routes, _routes_context)
end
end
@@ -320,28 +310,36 @@ module ActionDispatch
@url_helpers ||= begin
routes = self
- helpers = Module.new do
+ Module.new do
extend ActiveSupport::Concern
include UrlFor
+ # Define url_for in the singleton level so one can do:
+ # Rails.application.routes.url_helpers.url_for(args)
@_routes = routes
class << self
delegate :url_for, :to => '@_routes'
end
+
+ # Make named_routes available in the module singleton
+ # as well, so one can do:
+ # Rails.application.routes.url_helpers.posts_path
extend routes.named_routes.module
- # ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
- # we can include?
- # Yes plz - JP
+ # Any class that includes this module will get all
+ # named routes...
+ include routes.named_routes.module
+
+ # plus a singleton class method called _routes ...
included do
- routes.install_helpers(self)
singleton_class.send(:redefine_method, :_routes) { routes }
end
+ # And an instance method _routes. Note that
+ # UrlFor (included in this module) add extra
+ # conveniences for working with @_routes.
define_method(:_routes) { @_routes || routes }
end
-
- helpers
end
end
@@ -356,7 +354,7 @@ module ActionDispatch
conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
route = @set.add_route(app, path, conditions, defaults, name)
- named_routes[name] = route if name
+ named_routes[name] = route if name && !named_routes[name]
route
end
@@ -367,7 +365,26 @@ module ActionDispatch
SEPARATORS,
anchor)
- Journey::Path::Pattern.new(strexp)
+ pattern = Journey::Path::Pattern.new(strexp)
+
+ builder = Journey::GTG::Builder.new pattern.spec
+
+ # Get all the symbol nodes followed by literals that are not the
+ # dummy node.
+ symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
+ builder.followpos(n).first.literal?
+ }
+
+ # Get all the symbol nodes preceded by literals.
+ symbols.concat pattern.spec.find_all(&:literal?).map { |n|
+ builder.followpos(n).first
+ }.find_all(&:symbol?)
+
+ symbols.each { |x|
+ x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
+ }
+
+ pattern
end
private :build_path
@@ -470,7 +487,7 @@ module ActionDispatch
# if the current controller is "foo/bar/baz" and :controller => "baz/bat"
# is specified, the controller becomes "foo/baz/bat"
def use_relative_controller!
- if !named_route && different_controller?
+ if !named_route && different_controller? && !controller.start_with?("/")
old_parts = current_controller.split('/')
size = controller.count("/") + 1
parts = old_parts[0...-size] << controller
@@ -540,7 +557,6 @@ module ActionDispatch
end
def url_for(options)
- finalize!
options = (options || {}).reverse_merge!(default_url_options)
handle_positional_args(options)
@@ -556,8 +572,9 @@ module ActionDispatch
path_addition, params = generate(path_options, path_segments || {})
path << path_addition
+ params.merge!(options[:params] || {})
- ActionDispatch::Http::URL.url_for(options.merge({
+ ActionDispatch::Http::URL.url_for(options.merge!({
:path => path,
:params => params,
:user => user,
@@ -566,7 +583,6 @@ module ActionDispatch
end
def call(env)
- finalize!
@router.call(env)
end
@@ -584,11 +600,12 @@ module ActionDispatch
@router.recognize(req) do |route, matches, params|
params.each do |key, value|
if value.is_a?(String)
- value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
+ value = value.dup.force_encoding(Encoding::BINARY)
params[key] = URI.parser.unescape(value)
end
end
-
+ old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
dispatcher = route.app
while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
dispatcher = dispatcher.app
diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb
index f7d5f6397d..73af5920ed 100644
--- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb
+++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb
@@ -16,6 +16,10 @@ module ActionDispatch
end
end
+ def respond_to?(method, include_private = false)
+ super || routes.url_helpers.respond_to?(method)
+ end
+
def method_missing(method, *args)
if routes.url_helpers.respond_to?(method)
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index 8fc8dc191b..ee6616c5d3 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -8,7 +8,8 @@ module ActionDispatch
#
# <b>Tip:</b> If you need to generate URLs from your models or some other place,
# then ActionController::UrlFor is what you're looking for. Read on for
- # an introduction.
+ # an introduction. In general, this module should not be included on its own,
+ # as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
#
# == URL generation from parameters
#
@@ -42,7 +43,7 @@ module ActionDispatch
# url_for(:controller => 'users',
# :action => 'new',
# :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
+ # :host => 'www.example.com')
# # => "http://www.example.com/users/new?message=Welcome%21"
#
# By default, all controllers and views have access to a special version of url_for,
@@ -52,7 +53,7 @@ module ActionDispatch
#
# For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
# So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
+ # in full. However, mailers don't have hostname information, and that's why you'll still
# have to specify the <tt>:host</tt> argument when generating URLs in mailers.
#
#
@@ -84,14 +85,12 @@ module ActionDispatch
include PolymorphicRoutes
included do
- # TODO: with_routing extends @controller with url_helpers, trickling down to including this module which overrides its default_url_options
unless method_defined?(:default_url_options)
# Including in a class uses an inheritable hash. Modules get a plain hash.
if respond_to?(:class_attribute)
class_attribute :default_url_options
else
- mattr_accessor :default_url_options
- remove_method :default_url_options
+ mattr_writer :default_url_options
end
self.default_url_options = {}
@@ -145,23 +144,24 @@ module ActionDispatch
when String
options
when nil, Hash
- _routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys)
+ _routes.url_for((options || {}).symbolize_keys.reverse_merge!(url_options))
else
polymorphic_url(options)
end
end
protected
- def _with_routes(routes)
- old_routes, @_routes = @_routes, routes
- yield
- ensure
- @_routes = old_routes
- end
- def _routes_context
- self
- end
+ def _with_routes(routes)
+ old_routes, @_routes = @_routes, routes
+ yield
+ ensure
+ @_routes = old_routes
+ end
+
+ def _routes_context
+ self
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/dom.rb b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
index 47c84742aa..edea6dab39 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/dom.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
@@ -13,9 +13,7 @@ module ActionDispatch
def assert_dom_equal(expected, actual, message = "")
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 }
+ assert_equal expected_dom, actual_dom
end
# The negated form of +assert_dom_equivalent+.
@@ -28,9 +26,7 @@ module ActionDispatch
def assert_dom_not_equal(expected, actual, message = "")
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 }
+ refute_equal expected_dom, actual_dom
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 7381617dd7..094cfbfc76 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -26,16 +26,17 @@ module ActionDispatch
# assert_response 401
#
def assert_response(type, message = nil)
- validate_request!
+ message ||= "Expected response to be a <#{type}>, but was <#{@response.response_code}>"
- if type.in?([:success, :missing, :redirect, :error]) && @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 == Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
- assert_block("") { true } # to count the assertion
+ if Symbol === type
+ if [:success, :missing, :redirect, :error].include?(type)
+ assert @response.send("#{type}?"), message
+ else
+ code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
+ assert_equal @response.response_code, code, message
+ end
else
- flunk(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code))
+ assert_equal type, @response.response_code, message
end
end
@@ -61,9 +62,8 @@ module ActionDispatch
redirect_is = normalize_argument_to_redirection(@response.location)
redirect_expected = normalize_argument_to_redirection(options)
- if redirect_is != redirect_expected
- flunk "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
- end
+ message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
+ assert_equal redirect_expected, redirect_is, message
end
private
@@ -85,12 +85,6 @@ module ActionDispatch
@controller.url_for(fragment)
end.gsub(/[\r\n]/, '')
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_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index b10aab9029..1552676fbb 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -45,9 +45,11 @@ module ActionDispatch
extras.each_key { |key| expected_options.delete key } unless extras.nil?
expected_options.stringify_keys!
- msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
+
+ # FIXME: minitest does object diffs, do we need to have our own?
+ message ||= sprintf("The recognized options <%s> did not match <%s>, difference: <%s>",
request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
- assert_equal(expected_options, request.path_parameters, msg)
+ assert_equal(expected_options, request.path_parameters, message)
end
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
@@ -84,10 +86,10 @@ module ActionDispatch
generated_path, extra_keys = @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)
+ msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
assert_equal(extras, found_extras, msg)
- msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
+ msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
expected_path)
assert_equal(expected_path, generated_path, msg)
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index b4555f4f59..8eed85bce2 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -270,7 +270,7 @@ module ActionDispatch
end
text.strip! unless NO_STRIP.include?(match.name)
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
- content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, text)
+ content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, text)
true
end
end
@@ -279,7 +279,7 @@ module ActionDispatch
html = match.children.map(&:to_s).join
html.strip! unless NO_STRIP.include?(match.name)
unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
- content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, html)
+ content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, html)
true
end
end
@@ -289,12 +289,15 @@ module ActionDispatch
message ||= content_mismatch if matches.empty?
# Test minimum/maximum occurrence.
min, max, count = equals[:minimum], equals[:maximum], equals[:count]
+
+ # FIXME: minitest provides messaging when we use assert_operator,
+ # so is this custom message really needed?
message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size}.)
if count
- assert matches.size == count, message
+ assert_equal matches.size, count, message
else
- assert matches.size >= min, message if min
- assert matches.size <= max, message if max
+ assert_operator matches.size, :>=, min, message if min
+ assert_operator matches.size, :<=, max, message if max
end
# If a block is given call that block. Set @selected to allow
@@ -337,8 +340,8 @@ module ActionDispatch
# element +encoded+. It then calls the block with all un-encoded elements.
#
# ==== Examples
- # # Selects all bold tags from within the title of an ATOM feed's entries (perhaps to nab a section name prefix)
- # assert_select_feed :atom, 1.0 do
+ # # Selects all bold tags from within the title of an Atom feed's entries (perhaps to nab a section name prefix)
+ # assert_select "feed[xmlns='http://www.w3.org/2005/Atom']" do
# # Select each entry item and then the title item
# assert_select "entry>title" do
# # Run assertions on the encoded title elements
@@ -350,7 +353,7 @@ module ActionDispatch
#
#
# # Selects all paragraph tags from within the description of an RSS feed
- # assert_select_feed :rss, 2.0 do
+ # assert_select "rss[version=2.0]" do
# # Select description element of each feed item.
# assert_select "channel>item>description" do
# # Run assertions on the encoded elements.
@@ -414,8 +417,8 @@ module ActionDispatch
deliveries = ActionMailer::Base.deliveries
assert !deliveries.empty?, "No e-mail in delivery list"
- for delivery in deliveries
- for part in (delivery.parts.empty? ? [delivery] : delivery.parts)
+ deliveries.each do |delivery|
+ (delivery.parts.empty? ? [delivery] : delivery.parts).each do |part|
if part["Content-Type"].to_s =~ /^text\/html\W/
root = HTML::Document.new(part.body.to_s).root
assert_select root, ":root", &block
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 0f1bb9f260..08b7ff49c2 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -4,7 +4,6 @@ require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/object/inclusion'
require 'active_support/core_ext/object/try'
require 'rack/test'
-require 'test/unit/assertions'
module ActionDispatch
module Integration #:nodoc:
@@ -127,7 +126,7 @@ module ActionDispatch
class Session
DEFAULT_HOST = "www.example.com"
- include Test::Unit::Assertions
+ include MiniTest::Assertions
include TestProcess, RequestHelpers, Assertions
%w( status status_message headers body redirect? ).each do |method|
@@ -463,9 +462,12 @@ module ActionDispatch
@@app = nil
def self.app
- # DEPRECATE Rails application fallback
- # This should be set by the initializer
- @@app || (defined?(Rails.application) && Rails.application) || nil
+ if !@@app && !ActionDispatch.test_app
+ ActiveSupport::Deprecation.warn "Rails application fallback is deprecated " \
+ "and no longer works, please set ActionDispatch.test_app", caller
+ end
+
+ @@app || ActionDispatch.test_app
end
def self.app=(app)
diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb
index 914b13dbfb..39c7faf740 100644
--- a/actionpack/lib/action_pack.rb
+++ b/actionpack/lib/action_pack.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2011 David Heinemeier Hansson
+# Copyright (c) 2004-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index add6b56425..94cbc8e478 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -1,7 +1,7 @@
module ActionPack
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index d7229419a9..349a3fcc6e 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2011 David Heinemeier Hansson
+# Copyright (c) 2004-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -33,6 +33,7 @@ module ActionView
autoload :AssetPaths
autoload :Base
autoload :Context
+ autoload :CompiledTemplates, "action_view/context"
autoload :Helpers
autoload :LookupContext
autoload :PathSet
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
index 1d16e34df6..2a28e780bf 100644
--- a/actionpack/lib/action_view/asset_paths.rb
+++ b/actionpack/lib/action_view/asset_paths.rb
@@ -4,6 +4,8 @@ require 'action_controller/metal/exceptions'
module ActionView
class AssetPaths #:nodoc:
+ URI_REGEXP = %r{^[-a-z]+://|^cid:|^//}
+
attr_reader :config, :controller
def initialize(config, controller = nil)
@@ -37,7 +39,7 @@ module ActionView
end
def is_uri?(path)
- path =~ %r{^[-a-z]+://|^cid:|^//}
+ path =~ URI_REGEXP
end
private
@@ -103,8 +105,8 @@ module ActionView
if host.respond_to?(:call)
args = [source]
arity = arity_of(host)
- if arity > 1 && !has_request?
- invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.")
+ if (arity > 1 || arity < -2) && !has_request?
+ invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.")
end
args << current_request if (arity > 1 || arity < 0) && has_request?
host.call(*args)
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 36c49d9c91..23329d7f35 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -1,16 +1,14 @@
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/array/wrap'
require 'active_support/ordered_options'
require 'action_view/log_subscriber'
-require 'active_support/core_ext/module/deprecation'
module ActionView #:nodoc:
# = Action View Base
#
- # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
- # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
+ # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERb
+ # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used.
#
# == ERB
#
@@ -94,10 +92,10 @@ module ActionView #:nodoc:
#
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
#
- # xml.div {
+ # xml.div do
# xml.h1(@person.name)
# xml.p(@person.bio)
- # }
+ # end
#
# would produce something like:
#
@@ -145,10 +143,10 @@ module ActionView #:nodoc:
class_attribute :helpers
class_attribute :_routes
+ class_attribute :logger
class << self
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
- delegate :logger, :to => 'ActionController::Base', :allow_nil => true
def cache_template_loading
ActionView::Resolver.caching?
@@ -158,12 +156,6 @@ module ActionView #:nodoc:
ActionView::Resolver.caching = value
end
- def process_view_paths(value)
- value.is_a?(PathSet) ?
- value.dup : ActionView::PathSet.new(Array.wrap(value))
- end
- deprecate :process_view_paths
-
def xss_safe? #:nodoc:
true
end
diff --git a/actionpack/lib/action_view/buffers.rb b/actionpack/lib/action_view/buffers.rb
index be7f65c2ce..2372d3c433 100644
--- a/actionpack/lib/action_view/buffers.rb
+++ b/actionpack/lib/action_view/buffers.rb
@@ -4,7 +4,7 @@ module ActionView
class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
def initialize(*)
super
- encode! if encoding_aware?
+ encode!
end
def <<(value)
diff --git a/actionpack/lib/action_view/flows.rb b/actionpack/lib/action_view/flows.rb
index a8f740713f..c0e458cd41 100644
--- a/actionpack/lib/action_view/flows.rb
+++ b/actionpack/lib/action_view/flows.rb
@@ -22,11 +22,8 @@ module ActionView
def append(key, value)
@content[key] << value
end
+ alias_method :append!, :append
- # Called by provide
- def append!(key, value)
- @content[key] << value
- end
end
class StreamingFlow < OutputFlow #:nodoc:
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index 262e0f1010..f2a3a494bc 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -1,5 +1,3 @@
-require 'active_support/benchmarkable'
-
module ActionView #:nodoc:
module Helpers #:nodoc:
extend ActiveSupport::Autoload
@@ -7,6 +5,7 @@ module ActionView #:nodoc:
autoload :ActiveModelHelper
autoload :AssetTagHelper
autoload :AtomFeedHelper
+ autoload :BenchmarkHelper
autoload :CacheHelper
autoload :CaptureHelper
autoload :ControllerHelper
@@ -33,10 +32,10 @@ module ActionView #:nodoc:
extend SanitizeHelper::ClassMethods
end
- include ActiveSupport::Benchmarkable
include ActiveModelHelper
include AssetTagHelper
include AtomFeedHelper
+ include BenchmarkHelper
include CacheHelper
include CaptureHelper
include ControllerHelper
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index 96c3eec337..e27111012d 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -1,4 +1,3 @@
-require 'action_view/helpers/form_helper'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/object/blank'
@@ -17,8 +16,8 @@ module ActionView
end
end
- %w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
- module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__
+ def content_tag(*)
+ error_wrapping(super)
end
def tag(type, options, *)
@@ -40,16 +39,12 @@ module ActionView
private
def object_has_errors?
- object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && error_message.any?
+ object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
end
def tag_generate_errors?(options)
options['type'] != 'hidden'
end
end
-
- class InstanceTag
- include ActiveModelInstanceTag
- end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb
deleted file mode 100644
index fae2e4fc1c..0000000000
--- a/actionpack/lib/action_view/helpers/asset_paths.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-ActiveSupport::Deprecation.warn "ActionView::Helpers::AssetPaths is deprecated. Please use ActionView::AssetPaths instead."
-
-module ActionView
- module Helpers
- AssetPaths = ::ActionView::AssetPaths
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 7d01e5ddb8..662adbe183 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/hash/keys'
require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers'
require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers'
require 'action_view/helpers/asset_tag_helpers/asset_paths'
@@ -196,7 +198,7 @@ module ActionView
include JavascriptTagHelpers
include StylesheetTagHelpers
# Returns a link tag that browsers and news readers can use to auto-detect
- # an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or
+ # an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
# +url_options+. You can modify the LINK tag itself in +tag_options+.
#
@@ -228,23 +230,19 @@ module ActionView
)
end
- # Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document
- # root of your application and it changes later, clients that have it in their cache
- # won't see the update. Using this helper prevents that because it appends an asset ID:
- #
# <%= favicon_link_tag %>
#
# generates
#
- # <link href="/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ # <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# You may specify a different file in the first argument:
#
- # <%= favicon_link_tag 'favicon.ico' %>
+ # <%= favicon_link_tag '/myicon.ico' %>
#
# That's passed to +path_to_image+ as is, so it gives
#
- # <link href="/images/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ # <link href="/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# The helper accepts an additional options hash where you can override "rel" and "type".
#
@@ -254,7 +252,7 @@ module ActionView
#
# <%= favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png' %>
#
- def favicon_link_tag(source='/favicon.ico', options={})
+ def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
:type => 'image/vnd.microsoft.icon',
@@ -280,6 +278,13 @@ module ActionView
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
+ # Computes the full URL to an image asset in the public images directory.
+ # This will use +image_path+ internally, so most of their behaviors will be the same.
+ def image_url(source)
+ URI.join(current_host, path_to_image(source)).to_s
+ end
+ alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
+
# Computes the path to a video asset in the public videos directory.
# Full paths from the document root will be passed through.
# Used internally by +video_tag+ to build the video path.
@@ -295,6 +300,13 @@ module ActionView
end
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
+ # Computes the full URL to a video asset in the public videos directory.
+ # This will use +video_path+ internally, so most of their behaviors will be the same.
+ def video_url(source)
+ URI.join(current_host, path_to_video(source)).to_s
+ end
+ alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
+
# Computes the path to an audio asset in the public audios directory.
# Full paths from the document root will be passed through.
# Used internally by +audio_tag+ to build the audio path.
@@ -310,6 +322,34 @@ module ActionView
end
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
+ # Computes the full URL to a audio asset in the public audios directory.
+ # This will use +audio_path+ internally, so most of their behaviors will be the same.
+ def audio_url(source)
+ URI.join(current_host, path_to_audio(source)).to_s
+ end
+ alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
+
+ # Computes the path to a font asset in the public fonts directory.
+ # Full paths from the document root will be passed through.
+ #
+ # ==== Examples
+ # font_path("font") # => /fonts/font
+ # font_path("font.ttf") # => /fonts/font.ttf
+ # font_path("dir/font.ttf") # => /fonts/dir/font.ttf
+ # font_path("/dir/font.ttf") # => /dir/font.ttf
+ # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
+ def font_path(source)
+ asset_paths.compute_public_path(source, 'fonts')
+ end
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
+
+ # Computes the full URL to a font asset in the public fonts directory.
+ # This will use +font_path+ internally, so most of their behaviors will be the same.
+ def font_url(source)
+ URI.join(current_host, path_to_font(source)).to_s
+ end
+ alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
+
# Returns an html image tag for the +source+. The +source+ can be a full
# path or a file that exists in your public images directory.
#
@@ -343,8 +383,8 @@ module ActionView
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
# image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
- def image_tag(source, options = {})
- options.symbolize_keys!
+ def image_tag(source, options={})
+ options = options.dup.symbolize_keys!
src = options[:src] = path_to_image(source)
@@ -397,26 +437,19 @@ module ActionView
# <video src="/trailers/hd.avi" width="16" height="16" />
# video_tag("/trailers/hd.avi", :height => '32', :width => '32') # =>
# <video height="32" src="/trailers/hd.avi" width="32" />
+ # video_tag("trailer.ogg", "trailer.flv") # =>
+ # <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
# video_tag(["trailer.ogg", "trailer.flv"]) # =>
- # <video><source src="trailer.ogg" /><source src="trailer.ogg" /><source src="trailer.flv" /></video>
+ # <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
# video_tag(["trailer.ogg", "trailer.flv"] :size => "160x120") # =>
- # <video height="120" width="160"><source src="trailer.ogg" /><source src="trailer.flv" /></video>
- def video_tag(sources, options = {})
- options.symbolize_keys!
-
- options[:poster] = path_to_image(options[:poster]) if options[:poster]
+ # <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
+ def video_tag(*sources)
+ multiple_sources_tag('video', sources) do |options|
+ options[:poster] = path_to_image(options[:poster]) if options[:poster]
- if size = options.delete(:size)
- options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
- end
-
- if sources.is_a?(Array)
- content_tag("video", options) do
- sources.map { |source| tag("source", :src => source) }.join.html_safe
+ if size = options.delete(:size)
+ options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
end
- else
- options[:src] = path_to_video(sources)
- tag("video", options)
end
end
@@ -431,10 +464,10 @@ module ActionView
# <audio src="/audios/sound.wav" />
# audio_tag("sound.wav", :autoplay => true, :controls => true) # =>
# <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
- def audio_tag(source, options = {})
- options.symbolize_keys!
- options[:src] = path_to_audio(source)
- tag("audio", options)
+ # audio_tag("sound.wav", "sound.mid") # =>
+ # <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
+ def audio_tag(*sources)
+ multiple_sources_tag('audio', sources)
end
private
@@ -442,6 +475,26 @@ module ActionView
def asset_paths
@asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller)
end
+
+ def multiple_sources_tag(type, sources)
+ options = sources.extract_options!.dup.symbolize_keys!
+ sources.flatten!
+
+ yield options if block_given?
+
+ if sources.size > 1
+ content_tag(type, options) do
+ safe_join sources.map { |source| tag("source", :src => send("path_to_#{type}", source)) }
+ end
+ else
+ options[:src] = send("path_to_#{type}", sources.first)
+ content_tag(type, nil, options)
+ end
+ end
+
+ def current_host
+ url_for(:only_path => false)
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index 09700bd0c5..c67f81dcf4 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -87,6 +87,13 @@ module ActionView
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
+ # Computes the full URL to a javascript asset in the public javascripts directory.
+ # This will use +javascript_path+ internally, so most of their behaviors will be the same.
+ def javascript_url(source)
+ URI.join(current_host, path_to_javascript(source)).to_s
+ end
+ alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
+
# Returns an HTML script tag for each of the +sources+ provided.
#
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
@@ -119,10 +126,10 @@ module ActionView
# # <script type="text/javascript" src="/elsewhere/cools.js?1423139606"></script>
#
# javascript_include_tag "http://www.example.com/xmlhr"
- # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script>
+ # # => <script type="text/javascript" src="http://www.example.com/xmlhr"></script>
#
# javascript_include_tag "http://www.example.com/xmlhr.js"
- # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script>
+ # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js"></script>
#
# javascript_include_tag :defaults
# # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
index 343153c8c5..2584b67548 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
@@ -65,9 +65,19 @@ module ActionView
end
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
+ # Computes the full URL to a stylesheet asset in the public stylesheets directory.
+ # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
+ def stylesheet_url(source)
+ URI.join(current_host, path_to_stylesheet(source)).to_s
+ end
+ alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
+
# Returns a stylesheet link tag for the sources specified as arguments. If
# you don't specify an extension, <tt>.css</tt> will be appended automatically.
# You can modify the link attributes by passing a hash as the last argument.
+ # For historical reasons, the 'media' attribute will always be present and defaults
+ # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
+ # apply to all media types.
#
# ==== Examples
# stylesheet_link_tag "style" # =>
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index 39c37b25dc..73824dc1f8 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -32,7 +32,7 @@ module ActionView
# app/views/posts/index.atom.builder:
# atom_feed do |feed|
# feed.title("My great blog!")
- # feed.updated(@posts.first.created_at)
+ # feed.updated(@posts[0].created_at) if @posts.length > 0
#
# @posts.each do |post|
# feed.entry(post) do |entry|
diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb
new file mode 100644
index 0000000000..dfdd5a786d
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb
@@ -0,0 +1,13 @@
+require 'active_support/benchmarkable'
+
+module ActionView
+ module Helpers
+ module BenchmarkHelper
+ include ActiveSupport::Benchmarkable
+
+ def benchmark(*)
+ capture { super }
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 8abd85c3a3..17bbfe2efd 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -81,8 +81,8 @@ module ActionView
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
# <head>
- # <title>My Website</title>
- # <%= yield :script %>
+ # <title>My Website</title>
+ # <%= yield :script %>
# </head>
# <body>
# <%= yield %>
@@ -110,7 +110,7 @@ module ActionView
# That will place +script+ tags for your default set of JavaScript files on the page;
# this technique is useful if you'll only be using these scripts in a few views.
#
- # Note that content_for concatenates the blocks it is given for a particular
+ # Note that content_for concatenates (default) the blocks it is given for a particular
# identifier in order. For example:
#
# <% content_for :navigation do %>
@@ -127,16 +127,37 @@ module ActionView
#
# <ul><%= content_for :navigation %></ul>
#
+ # If the flush parameter is true content_for replaces the blocks it is given. For example:
+ #
+ # <% content_for :navigation do %>
+ # <li><%= link_to 'Home', :action => 'index' %></li>
+ # <% end %>
+ #
+ # <%# Add some other content, or use a different template: %>
+ #
+ # <% content_for :navigation, true do %>
+ # <li><%= link_to 'Login', :action => 'login' %></li>
+ # <% end %>
+ #
+ # Then, in another template or layout, this code would render only the last link:
+ #
+ # <ul><%= content_for :navigation %></ul>
+ #
# Lastly, simple content can be passed as a parameter:
#
# <% content_for :script, javascript_include_tag(:defaults) %>
#
# WARNING: content_for is ignored in caches. So you shouldn't use it
# for elements that will be fragment cached.
- def content_for(name, content = nil, &block)
+ def content_for(name, content = nil, flush = false, &block)
if content || block_given?
- content = capture(&block) if block_given?
- @view_flow.append(name, content) if content
+ if block_given?
+ flush = content if content
+ content = capture(&block)
+ end
+ if content
+ flush ? @view_flow.set(name, content) : @view_flow.append(name, content)
+ end
nil
else
@view_flow.get(name)
@@ -164,8 +185,8 @@ module ActionView
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
# <head>
- # <title>My Website</title>
- # <%= yield :script %>
+ # <title>My Website</title>
+ # <%= yield :script %>
# </head>
# <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
# <%= yield %>
@@ -181,7 +202,7 @@ module ActionView
def with_output_buffer(buf = nil) #:nodoc:
unless buf
buf = ActionView::OutputBuffer.new
- buf.force_encoding(output_buffer.encoding) if output_buffer.respond_to?(:encoding) && buf.respond_to?(:force_encoding)
+ buf.force_encoding(output_buffer.encoding) if output_buffer
end
self.output_buffer, old_buffer = buf, output_buffer
yield
diff --git a/actionpack/lib/action_view/helpers/csrf_helper.rb b/actionpack/lib/action_view/helpers/csrf_helper.rb
index 1f2bc28cac..eeb0ed94b9 100644
--- a/actionpack/lib/action_view/helpers/csrf_helper.rb
+++ b/actionpack/lib/action_view/helpers/csrf_helper.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/string/strip'
-
module ActionView
# = Action View CSRF Helper
module Helpers
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 4deb87180c..2d37923825 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -142,6 +142,8 @@ module ActionView
# ==== Options
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
# "2" instead of "February").
+ # * <tt>:use_two_digit_numbers</tt> - Set to true if you want to display two digit month and day numbers (e.g.
+ # "02" instead of "February" and "08" instead of "8").
# * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
# month names (e.g. "Feb" instead of "February").
# * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
@@ -189,6 +191,10 @@ module ActionView
# date_select("article", "written_on", :start_year => 1995, :use_month_numbers => true,
# :discard_day => true, :include_blank => true)
#
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
+ # # with two digit numbers used for months and days.
+ # date_select("article", "written_on", :use_two_digit_numbers => true)
+ #
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
# # with the fields ordered as day, month, year rather than month, day, year.
# date_select("article", "written_on", :order => [:day, :month, :year])
@@ -213,7 +219,7 @@ module ActionView
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
# all month choices are valid.
def date_select(object_name, method, options = {}, html_options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
+ Tags::DateSelect.new(object_name, method, self, options, html_options).render
end
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
@@ -251,7 +257,7 @@ module ActionView
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
# all month choices are valid.
def time_select(object_name, method, options = {}, html_options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options)
+ Tags::TimeSelect.new(object_name, method, self, options, html_options).render
end
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
@@ -287,7 +293,7 @@ module ActionView
#
# The selects are prepared for multi-parameter assignment to an Active Record object.
def datetime_select(object_name, method, options = {}, html_options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
+ Tags::DatetimeSelect.new(object_name, method, self, options, html_options).render
end
# Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
@@ -422,7 +428,7 @@ module ActionView
end
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
- # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
#
# ==== Examples
@@ -448,7 +454,7 @@ module ActionView
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
- # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
+ # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
#
# ==== Examples
@@ -473,7 +479,7 @@ module ActionView
end
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
- # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
#
# ==== Examples
@@ -502,6 +508,7 @@ module ActionView
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
# The <tt>date</tt> can also be substituted for a day number.
+ # If you want to display days with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
# Override the field name using the <tt>:field_name</tt> option, 'day' by default.
#
# ==== Examples
@@ -513,6 +520,9 @@ module ActionView
# # Generates a select field for days that defaults to the number given.
# select_day(5)
#
+ # # Generates a select field for days that defaults to the number given, but displays it with two digits.
+ # select_day(5, :use_two_digit_numbers => true)
+ #
# # Generates a select field for days that defaults to the day for the date in my_date
# # that is named 'due' rather than 'day'.
# select_day(my_time, :field_name => 'due')
@@ -532,6 +542,7 @@ module ActionView
# want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
# to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
# to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
+ # If you want to display months with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
# Override the field name using the <tt>:field_name</tt> option, 'month' by default.
#
# ==== Examples
@@ -559,6 +570,10 @@ module ActionView
# # will use keys like "Januar", "Marts."
# select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
#
+ # # Generates a select field for months that defaults to the current month that
+ # # will use keys with two digit numbers like "01", "03".
+ # select_month(Date.today, :use_two_digit_numbers => true)
+ #
# # Generates a select field for months with a custom prompt. Use <tt>:prompt => true</tt> for a
# # generic prompt.
# select_month(14, :prompt => 'Choose month')
@@ -734,15 +749,15 @@ module ActionView
def select_day
if @options[:use_hidden] || @options[:discard_day]
- build_hidden(:day, day)
+ build_hidden(:day, day || 1)
else
- build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false)
+ build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers])
end
end
def select_month
if @options[:use_hidden] || @options[:discard_month]
- build_hidden(:month, month)
+ build_hidden(:month, month || 1)
else
month_options = []
1.upto(12) do |month_number|
@@ -756,7 +771,7 @@ module ActionView
def select_year
if !@datetime || @datetime == 0
- val = ''
+ val = '1'
middle_year = Date.today.year
else
val = middle_year = year
@@ -817,11 +832,16 @@ module ActionView
# If <tt>:use_month_numbers</tt> option is passed
# month_name(1) => 1
#
+ # If <tt>:use_two_month_numbers</tt> option is passed
+ # month_name(1) => '01'
+ #
# If <tt>:add_month_numbers</tt> option is passed
# month_name(1) => "1 - January"
def month_name(number)
if @options[:use_month_numbers]
number
+ elsif @options[:use_two_digit_numbers]
+ sprintf "%02d", number
elsif @options[:add_month_numbers]
"#{number} - #{month_names[number]}"
else
@@ -834,7 +854,15 @@ module ActionView
end
def translated_date_order
- I18n.translate(:'date.order', :locale => @options[:locale]) || []
+ date_order = I18n.translate(:'date.order', :locale => @options[:locale], :default => [])
+
+ forbidden_elements = date_order - [:year, :month, :day]
+ if forbidden_elements.any?
+ raise StandardError,
+ "#{@options[:locale]}.date.order only accepts :year, :month and :day"
+ end
+
+ date_order
end
# Build full select tag from date type and options.
@@ -848,6 +876,12 @@ module ActionView
# <option value="2">2</option>
# <option value="3">3</option>..."
#
+ # If <tt>:use_two_digit_numbers => true</tt> option is passed
+ # build_options(15, :start => 1, :end => 31, :use_two_digit_numbers => true)
+ # => "<option value="1">01</option>
+ # <option value="2">02</option>
+ # <option value="3">03</option>..."
+ #
# If <tt>:step</tt> options is passed
# build_options(15, :start => 1, :end => 31, :step => 2)
# => "<option value="1">1</option>
@@ -857,7 +891,7 @@ module ActionView
start = options.delete(:start) || 0
stop = options.delete(:end) || 59
step = options.delete(:step) || 1
- options.reverse_merge!({:leading_zeros => true, :ampm => false})
+ options.reverse_merge!({:leading_zeros => true, :ampm => false, :use_two_digit_numbers => false})
leading_zeros = options.delete(:leading_zeros)
select_options = []
@@ -865,7 +899,8 @@ module ActionView
value = leading_zeros ? sprintf("%02d", i) : i
tag_options = { :value => value }
tag_options[:selected] = "selected" if selected == i
- text = options[:ampm] ? AMPM_TRANSLATION[i] : value
+ text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
+ text = options[:ampm] ? AMPM_TRANSLATION[i] : text
select_options << content_tag(:option, text, tag_options)
end
(select_options.join("\n") + "\n").html_safe
@@ -944,8 +979,9 @@ module ActionView
# and join them with their appropriate separators.
def build_selects_from_types(order)
select = ''
+ first_visible = order.find { |type| !@options[:"discard_#{type}"] }
order.reverse.each do |type|
- separator = separator(type) unless type == order.first # don't add on last field
+ separator = separator(type) unless type == first_visible # don't add before first visible field
select.insert(0, separator.to_s + send("select_#{type}").to_s)
end
select.html_safe
@@ -954,74 +990,12 @@ module ActionView
# Returns the separator for a given datetime component.
def separator(type)
case type
- when :year
- @options[:discard_year] ? "" : @options[:date_separator]
- when :month
- @options[:discard_month] ? "" : @options[:date_separator]
- when :day
- @options[:discard_day] ? "" : @options[:date_separator]
+ when :year, :month, :day
+ @options[:"discard_#{type}"] ? "" : @options[:date_separator]
when :hour
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
- when :minute
- @options[:discard_minute] ? "" : @options[:time_separator]
- when :second
- @options[:include_seconds] ? @options[:time_separator] : ""
- end
- end
- end
-
- class InstanceTag #:nodoc:
- def to_date_select_tag(options = {}, html_options = {})
- datetime_selector(options, html_options).select_date.html_safe
- end
-
- def to_time_select_tag(options = {}, html_options = {})
- datetime_selector(options, html_options).select_time.html_safe
- end
-
- def to_datetime_select_tag(options = {}, html_options = {})
- datetime_selector(options, html_options).select_datetime.html_safe
- end
-
- private
- def datetime_selector(options, html_options)
- datetime = value(object) || default_datetime(options)
- @auto_index ||= nil
-
- options = options.dup
- options[:field_name] = @method_name
- options[:include_position] = true
- options[:prefix] ||= @object_name
- options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
-
- DateTimeSelector.new(datetime, options, html_options)
- end
-
- def default_datetime(options)
- return if options[:include_blank] || options[:prompt]
-
- case options[:default]
- when nil
- Time.current
- when Date, Time
- options[:default]
- else
- default = options[:default].dup
-
- # Rename :minute and :second to :min and :sec
- default[:min] ||= default[:minute]
- default[:sec] ||= default[:second]
-
- time = Time.current
-
- [:year, :month, :day, :hour, :min, :sec].each do |key|
- default[key] ||= time.send(key)
- end
-
- Time.utc_time(
- default[:year], default[:month], default[:day],
- default[:hour], default[:min], default[:sec]
- )
+ when :minute, :second
+ @options[:"discard_#{type}"] ? "" : @options[:time_separator]
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index f22c466666..44e24fecd1 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -2,9 +2,10 @@ require 'cgi'
require 'action_view/helpers/date_helper'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/form_tag_helper'
+require 'action_view/helpers/active_model_helper'
+require 'action_view/helpers/tags'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/slice'
-require 'active_support/core_ext/module/method_names'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/array/extract_options'
@@ -158,6 +159,9 @@ module ActionView
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same
# fields you pass to +url_for+ or +link_to+. In particular you may pass
# here a named route directly as well. Defaults to the current action.
+ # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
+ # id attributes on form elements. The namespace attribute will be prefixed
+ # with underscore on the generated HTML id.
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
#
# Also note that +form_for+ doesn't create an exclusive scope. It's still
@@ -362,34 +366,33 @@ module ActionView
else
object = record.is_a?(Array) ? record.last : record
object_name = options[:as] || ActiveModel::Naming.param_key(object)
- apply_form_for_options!(record, options)
+ apply_form_for_options!(record, object, options)
end
options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
options[:html][:method] = options.delete(:method) if options.has_key?(:method)
options[:html][:authenticity_token] = options.delete(:authenticity_token)
- builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &proc)
+ builder = options[:parent_builder] = instantiate_builder(object_name, object, options)
fields_for = fields_for(object_name, object, options, &proc)
default_options = builder.multipart? ? { :multipart => true } : {}
- output = form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html)))
- output << fields_for
- output.safe_concat('</form>')
+ default_options.merge!(options.delete(:html))
+
+ form_tag(options.delete(:url) || {}, default_options) { fields_for }
end
- def apply_form_for_options!(object_or_array, options) #:nodoc:
- object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array
+ def apply_form_for_options!(record, object, options) #:nodoc:
object = convert_to_model(object)
as = options[:as]
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post]
options[:html].reverse_merge!(
- :class => as ? "#{as}_#{action}" : dom_class(object, action),
- :id => as ? "#{as}_#{action}" : dom_id(object, action),
+ :class => as ? "#{action}_#{as}" : dom_class(object, action),
+ :id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
:method => method
)
- options[:url] ||= polymorphic_path(object_or_array, :format => options.delete(:format))
+ options[:url] ||= polymorphic_path(record, :format => options.delete(:format))
end
private :apply_form_for_options!
@@ -598,7 +601,7 @@ module ActionView
# ...
# <% end %>
def fields_for(record_name, record_object = nil, options = {}, &block)
- builder = instantiate_builder(record_name, record_object, options, &block)
+ builder = instantiate_builder(record_name, record_object, options)
output = capture(builder, &block)
output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id?
output
@@ -649,19 +652,10 @@ module ActionView
# # => <label for="post_privacy_public">Public Post</label>
#
# label(:post, :terms) do
- # 'Accept <a href="/terms">Terms</a>.'
+ # 'Accept <a href="/terms">Terms</a>.'.html_safe
# end
def label(object_name, method, content_or_options = nil, options = nil, &block)
- content_is_options = content_or_options.is_a?(Hash)
- if content_is_options || block_given?
- options = content_or_options if content_is_options
- text = nil
- else
- text = content_or_options
- end
-
- options ||= {}
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
+ Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
end
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -683,7 +677,7 @@ module ActionView
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
#
def text_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options)
+ Tags::TextField.new(object_name, method, self, options).render
end
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -705,7 +699,7 @@ module ActionView
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
#
def password_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", { :value => nil }.merge!(options))
+ Tags::PasswordField.new(object_name, method, self, options).render
end
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
@@ -723,7 +717,7 @@ module ActionView
# hidden_field(:user, :token)
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
def hidden_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options)
+ Tags::HiddenField.new(object_name, method, self, options).render
end
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
@@ -738,13 +732,13 @@ module ActionView
# # => <input type="file" id="user_avatar" name="user[avatar]" />
#
# file_field(:post, :attached, :accept => 'text/html')
- # # => <input type="file" id="post_attached" name="post[attached]" />
+ # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
#
# file_field(:attachment, :file, :class => 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
#
def file_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options.update({:size => nil}))
+ Tags::FileField.new(object_name, method, self, options).render
end
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
@@ -772,7 +766,7 @@ module ActionView
# # #{@entry.body}
# # </textarea>
def text_area(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
+ Tags::TextArea.new(object_name, method, self, options).render
end
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
@@ -834,7 +828,7 @@ module ActionView
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
#
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
+ Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
end
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
@@ -856,7 +850,7 @@ module ActionView
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
def radio_button(object_name, method, tag_value, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
+ Tags::RadioButton.new(object_name, method, self, tag_value, options).render
end
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
@@ -882,20 +876,7 @@ module ActionView
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
#
def search_field(object_name, method, options = {})
- options = options.stringify_keys
-
- if options["autosave"]
- if options["autosave"] == true
- options["autosave"] = request.host.split(".").reverse.join(".")
- end
- options["results"] ||= 10
- end
-
- if options["onsearch"]
- options["incremental"] = true unless options.has_key?("incremental")
- end
-
- InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
+ Tags::SearchField.new(object_name, method, self, options).render
end
# Returns a text_field of type "tel".
@@ -904,17 +885,35 @@ module ActionView
# # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
#
def telephone_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options)
+ Tags::TelField.new(object_name, method, self, options).render
end
alias phone_field telephone_field
+ # Returns a text_field of type "date".
+ #
+ # date_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="date" />
+ #
+ # The default value is generated by trying to call "to_date"
+ # on the object's value, which makes it behave as expected for instances
+ # of DateTime and ActiveSupport::TimeWithZone. You can still override that
+ # by passing the "value" option explicitly, e.g.
+ #
+ # @user.born_on = Date.new(1984, 1, 27)
+ # date_field("user", "born_on", value: "1984-05-12")
+ # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" />
+ #
+ def date_field(object_name, method, options = {})
+ Tags::DateField.new(object_name, method, self, options).render
+ end
+
# Returns a text_field of type "url".
#
# url_field("user", "homepage")
# # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
#
def url_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options)
+ Tags::UrlField.new(object_name, method, self, options).render
end
# Returns a text_field of type "email".
@@ -923,7 +922,7 @@ module ActionView
# # => <input id="user_address" size="30" name="user[address]" type="email" />
#
def email_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options)
+ Tags::EmailField.new(object_name, method, self, options).render
end
# Returns an input tag of type "number".
@@ -931,7 +930,7 @@ module ActionView
# ==== Options
# * Accepts same options as number_field_tag
def number_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("number", options)
+ Tags::NumberField.new(object_name, method, self, options).render
end
# Returns an input tag of type "range".
@@ -939,12 +938,12 @@ module ActionView
# ==== Options
# * Accepts same options as range_field_tag
def range_field(object_name, method, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("range", options)
+ Tags::RangeField.new(object_name, method, self, options).render
end
private
- def instantiate_builder(record_name, record_object, options, &block)
+ def instantiate_builder(record_name, record_object, options)
case record_name
when String, Symbol
object = record_object
@@ -955,267 +954,14 @@ module ActionView
end
builder = options[:builder] || ActionView::Base.default_form_builder
- builder.new(object_name, object, self, options, block)
- end
- end
-
- class InstanceTag
- include Helpers::CaptureHelper, Context, Helpers::TagHelper, Helpers::FormTagHelper
-
- attr_reader :object, :method_name, :object_name
-
- DEFAULT_FIELD_OPTIONS = { "size" => 30 }
- DEFAULT_RADIO_OPTIONS = { }
- DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }
-
- def initialize(object_name, method_name, template_object, object = nil)
- @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
- @template_object = template_object
- @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
- @object = retrieve_object(object)
- @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
- end
-
- def to_label_tag(text = nil, options = {}, &block)
- options = options.stringify_keys
- tag_value = options.delete("value")
- name_and_id = options.dup
-
- if name_and_id["for"]
- name_and_id["id"] = name_and_id["for"]
- else
- name_and_id.delete("id")
- end
-
- add_default_name_and_id_for_value(tag_value, name_and_id)
- options.delete("index")
- options["for"] ||= name_and_id["id"]
-
- if block_given?
- label_tag(name_and_id["id"], options, &block)
- else
- content = if text.blank?
- method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name
- I18n.t("helpers.label.#{object_name}.#{method_and_value}", :default => "").presence
- else
- text.to_s
- end
-
- content ||= if object && object.class.respond_to?(:human_attribute_name)
- object.class.human_attribute_name(method_name)
- end
-
- content ||= method_name.humanize
-
- label_tag(name_and_id["id"], content, options)
- end
- end
-
- def to_input_field_tag(field_type, options = {})
- options = options.stringify_keys
- options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
- options = DEFAULT_FIELD_OPTIONS.merge(options)
- if field_type == "hidden"
- options.delete("size")
- end
- options["type"] ||= field_type
- options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
- options["value"] &&= ERB::Util.html_escape(options["value"])
- add_default_name_and_id(options)
- tag("input", options)
- end
-
- def to_number_field_tag(field_type, options = {})
- options = options.stringify_keys
- if range = options.delete("in") || options.delete("within")
- options.update("min" => range.min, "max" => range.max)
- end
- to_input_field_tag(field_type, options)
- end
-
- def to_radio_button_tag(tag_value, options = {})
- options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
- options["type"] = "radio"
- options["value"] = tag_value
- if options.has_key?("checked")
- cv = options.delete "checked"
- checked = cv == true || cv == "checked"
- else
- checked = self.class.radio_button_checked?(value(object), tag_value)
- end
- options["checked"] = "checked" if checked
- add_default_name_and_id_for_value(tag_value, options)
- tag("input", options)
- end
-
- def to_text_area_tag(options = {})
- options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
- add_default_name_and_id(options)
-
- if size = options.delete("size")
- options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
- end
-
- content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options)
- end
-
- def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
- options = options.stringify_keys
- options["type"] = "checkbox"
- options["value"] = checked_value
- if options.has_key?("checked")
- cv = options.delete "checked"
- checked = cv == true || cv == "checked"
- else
- checked = self.class.check_box_checked?(value(object), checked_value)
- end
- options["checked"] = "checked" if checked
- if options["multiple"]
- add_default_name_and_id_for_value(checked_value, options)
- options.delete("multiple")
- else
- add_default_name_and_id(options)
- end
- hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
- checkbox = tag("input", options)
- (hidden + checkbox).html_safe
- end
-
- def to_boolean_select_tag(options = {})
- options = options.stringify_keys
- add_default_name_and_id(options)
- value = value(object)
- tag_text = "<select"
- tag_text << tag_options(options)
- tag_text << "><option value=\"false\""
- tag_text << " selected" if value == false
- tag_text << ">False</option><option value=\"true\""
- tag_text << " selected" if value
- tag_text << ">True</option></select>"
- end
-
- def to_content_tag(tag_name, options = {})
- content_tag(tag_name, value(object), options)
- end
-
- def retrieve_object(object)
- if object
- object
- elsif @template_object.instance_variable_defined?("@#{@object_name}")
- @template_object.instance_variable_get("@#{@object_name}")
- end
- rescue NameError
- # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
- nil
- end
-
- def retrieve_autoindex(pre_match)
- object = self.object || @template_object.instance_variable_get("@#{pre_match}")
- if object && object.respond_to?(:to_param)
- object.to_param
- else
- raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
- end
- end
-
- def value(object)
- self.class.value(object, @method_name)
- end
-
- def value_before_type_cast(object)
- self.class.value_before_type_cast(object, @method_name)
- end
-
- class << self
- def value(object, method_name)
- object.send method_name if object
- end
-
- def value_before_type_cast(object, method_name)
- unless object.nil?
- object.respond_to?(method_name + "_before_type_cast") ?
- object.send(method_name + "_before_type_cast") :
- object.send(method_name)
- end
- end
-
- def check_box_checked?(value, checked_value)
- case value
- when TrueClass, FalseClass
- value
- when NilClass
- false
- when Integer
- value != 0
- when String
- value == checked_value
- when Array
- value.include?(checked_value)
- else
- value.to_i != 0
- end
- end
-
- def radio_button_checked?(value, checked_value)
- value.to_s == checked_value.to_s
- end
- end
-
- private
- def add_default_name_and_id_for_value(tag_value, options)
- unless tag_value.nil?
- pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
- specified_id = options["id"]
- add_default_name_and_id(options)
- options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present?
- else
- add_default_name_and_id(options)
- end
- end
-
- def add_default_name_and_id(options)
- if options.has_key?("index")
- options["name"] ||= tag_name_with_index(options["index"])
- options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
- options.delete("index")
- elsif defined?(@auto_index)
- options["name"] ||= tag_name_with_index(@auto_index)
- options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
- else
- options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
- options["id"] = options.fetch("id"){ tag_id }
- end
- end
-
- def tag_name
- "#{@object_name}[#{sanitized_method_name}]"
- end
-
- def tag_name_with_index(index)
- "#{@object_name}[#{index}][#{sanitized_method_name}]"
- end
-
- def tag_id
- "#{sanitized_object_name}_#{sanitized_method_name}"
- end
-
- def tag_id_with_index(index)
- "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
- end
-
- def sanitized_object_name
- @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
- end
-
- def sanitized_method_name
- @sanitized_method_name ||= @method_name.sub(/\?$/,"")
+ builder.new(object_name, object, self, options)
end
end
class FormBuilder
# The methods which wrap a form helper call.
class_attribute :field_helpers
- self.field_helpers = FormHelper.instance_method_names - %w(form_for convert_to_model)
+ self.field_helpers = FormHelper.instance_methods - [:form_for, :convert_to_model]
attr_accessor :object_name, :object, :options
@@ -1239,11 +985,11 @@ module ActionView
self
end
- def initialize(object_name, object, template, options, proc)
+ def initialize(object_name, object, template, options)
@nested_child_index = {}
- @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
+ @object_name, @object, @template, @options = object_name, object, template, options
@parent_builder = options[:parent_builder]
- @default_options = @options ? @options.slice(:index) : {}
+ @default_options = @options ? @options.slice(:index, :namespace) : {}
if @object_name.to_s.match(/\[\]$/)
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
@auto_index = object.to_param
@@ -1254,7 +1000,7 @@ module ActionView
@multipart = nil
end
- (field_helpers - %w(label check_box radio_button fields_for hidden_field file_field)).each do |selector|
+ (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
@@ -1270,6 +1016,7 @@ module ActionView
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
fields_options[:parent_builder] = self
+ fields_options[:namespace] = fields_options[:parent_builder].options[:namespace]
case record_name
when String, Symbol
@@ -1347,6 +1094,39 @@ module ActionView
@template.submit_tag(value, options)
end
+ # Add the submit button for the given form. When no value is given, it checks
+ # if the object is a new resource or not to create the proper label:
+ #
+ # <%= form_for @post do |f| %>
+ # <%= f.button %>
+ # <% end %>
+ #
+ # In the example above, if @post is a new record, it will use "Create Post" as
+ # submit button label, otherwise, it uses "Update Post".
+ #
+ # Those labels can be customized using I18n, under the helpers.submit key and accept
+ # the %{model} as translation interpolation:
+ #
+ # en:
+ # helpers:
+ # button:
+ # create: "Create a %{model}"
+ # update: "Confirm changes to %{model}"
+ #
+ # It also searches for a key specific for the given object:
+ #
+ # en:
+ # helpers:
+ # button:
+ # post:
+ # create: "Add %{model}"
+ #
+ def button(value=nil, options={})
+ value, options = nil, value if value.is_a?(Hash)
+ value ||= submit_default_value
+ @template.button_tag(value, options)
+ end
+
def emitted_hidden_id?
@emitted_hidden_id ||= nil
end
@@ -1421,9 +1201,6 @@ module ActionView
end
ActiveSupport.on_load(:action_view) do
- class ActionView::Base
- cattr_accessor :default_form_builder
- @@default_form_builder = ::ActionView::Helpers::FormBuilder
- end
+ cattr_accessor(:default_form_builder) { ::ActionView::Helpers::FormBuilder }
end
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index f895cad058..bc03a1cf83 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -134,7 +134,7 @@ module ActionView
#
# ==== Gotcha
#
- # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
+ # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
# if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
@@ -154,7 +154,7 @@ module ActionView
# key in the query string, that works for ordinary forms.
#
def select(object, method, choices, options = {}, html_options = {})
- InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
+ Tags::Select.new(object, method, self, choices, options, html_options).render
end
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
@@ -164,7 +164,9 @@ module ActionView
#
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
# of +collection+. The return values are used as the +value+ attribute and contents of each
- # <tt><option></tt> tag, respectively.
+ # <tt><option></tt> tag, respectively. They can also be any object that responds to +call+, such
+ # as a +proc+, that will be called for each member of the +collection+ to
+ # retrieve the value/text.
#
# Example object structure for use with this method:
# class Post < ActiveRecord::Base
@@ -188,10 +190,9 @@ module ActionView
# <option value="3">M. Clark</option>
# </select>
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
- InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
+ Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render
end
-
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
@@ -240,7 +241,7 @@ module ActionView
# </select>
#
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
- InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
+ Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
end
# Return select and option tags for the given object and method, using
@@ -274,7 +275,7 @@ module ActionView
#
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, :model => ActiveSupport::TimeZone)
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
- InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
+ Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
end
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
@@ -322,8 +323,8 @@ module ActionView
def options_for_select(container, selected = nil)
return container if String === container
- selected, disabled = extract_selected_and_disabled(selected).map do | r |
- Array.wrap(r).map { |item| item.to_s }
+ selected, disabled = extract_selected_and_disabled(selected).map do |r|
+ Array(r).map { |item| item.to_s }
end
container.map do |element|
@@ -333,10 +334,9 @@ module ActionView
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
%(<option value="#{ERB::Util.html_escape(value)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
end.join("\n").html_safe
-
end
- # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
+ # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
# Example:
# options_from_collection_for_select(@people, 'id', 'name')
@@ -362,12 +362,13 @@ module ActionView
# should produce the desired results.
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
options = collection.map do |element|
- [element.send(text_method), element.send(value_method)]
+ [value_for_collection(element, text_method), value_for_collection(element, value_method)]
end
selected, disabled = extract_selected_and_disabled(selected)
- select_deselect = {}
- select_deselect[:selected] = extract_values_from_collection(collection, value_method, selected)
- select_deselect[:disabled] = extract_values_from_collection(collection, value_method, disabled)
+ select_deselect = {
+ :selected => extract_values_from_collection(collection, value_method, selected),
+ :disabled => extract_values_from_collection(collection, value_method, disabled)
+ }
options_for_select(options, select_deselect)
end
@@ -420,10 +421,10 @@ module ActionView
# wrap the output in an appropriate <tt><select></tt> tag.
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
collection.map do |group|
- group_label_string = eval("group.#{group_label_method}")
- "<optgroup label=\"#{ERB::Util.html_escape(group_label_string)}\">" +
- options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key) +
- '</optgroup>'
+ option_tags = options_from_collection_for_select(
+ group.send(group_method), option_key_method, option_value_method, selected_key)
+
+ content_tag(:optgroup, option_tags, :label => group.send(group_label_method))
end.join.html_safe
end
@@ -508,9 +509,9 @@ module ActionView
convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
if priority_zones
- if priority_zones.is_a?(Regexp)
+ if priority_zones.is_a?(Regexp)
priority_zones = model.all.find_all {|z| z =~ priority_zones}
- end
+ end
zone_options += options_for_select(convert_zones[priority_zones], selected)
zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
@@ -521,14 +522,138 @@ module ActionView
zone_options.html_safe
end
+ # Returns radio button tags for the collection of existing return values
+ # of +method+ for +object+'s class. The value returned from calling
+ # +method+ on the instance +object+ will be selected. If calling +method+
+ # returns +nil+, no selection is made.
+ #
+ # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
+ # methods to be called on each member of +collection+. The return values
+ # are used as the +value+ attribute and contents of each radio button tag,
+ # respectively. They can also be any object that responds to +call+, such
+ # as a +proc+, that will be called for each member of the +collection+ to
+ # retrieve the value/text.
+ #
+ # Example object structure for use with this method:
+ # class Post < ActiveRecord::Base
+ # belongs_to :author
+ # end
+ # class Author < ActiveRecord::Base
+ # has_many :posts
+ # def name_with_initial
+ # "#{first_name.first}. #{last_name}"
+ # end
+ # end
+ #
+ # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
+ # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
+ #
+ # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
+ # <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
+ # <label for="post_author_id_1">D. Heinemeier Hansson</label>
+ # <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
+ # <label for="post_author_id_2">D. Thomas</label>
+ # <input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
+ # <label for="post_author_id_3">M. Clark</label>
+ #
+ # It is also possible to customize the way the elements will be shown by
+ # giving a block to the method:
+ # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
+ # b.label { b.radio_button }
+ # end
+ #
+ # The argument passed to the block is a special kind of builder for this
+ # collection, which has the ability to generate the label and radio button
+ # for the current item in the collection, with proper text and value.
+ # Using it, you can change the label and radio button display order or
+ # even use the label as wrapper, as in the example above.
+ #
+ # The builder methods <tt>label</tt> and <tt>radio_button</tt> also accept
+ # extra html options:
+ # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
+ # b.label(:class => "radio_button") { b.radio_button(:class => "radio_button") }
+ # end
+ #
+ # There are also two special methods available: <tt>text</tt> and
+ # <tt>value</tt>, which are the current text and value methods for the
+ # item being rendered, respectively. You can use them like this:
+ # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
+ # b.label(:"data-value" => b.value) { b.radio_button + b.text }
+ # end
+ def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
+ Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
+ end
+
+ # Returns check box tags for the collection of existing return values of
+ # +method+ for +object+'s class. The value returned from calling +method+
+ # on the instance +object+ will be selected. If calling +method+ returns
+ # +nil+, no selection is made.
+ #
+ # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
+ # methods to be called on each member of +collection+. The return values
+ # are used as the +value+ attribute and contents of each check box tag,
+ # respectively. They can also be any object that responds to +call+, such
+ # as a +proc+, that will be called for each member of the +collection+ to
+ # retrieve the value/text.
+ #
+ # Example object structure for use with this method:
+ # class Post < ActiveRecord::Base
+ # has_and_belongs_to_many :author
+ # end
+ # class Author < ActiveRecord::Base
+ # has_and_belongs_to_many :posts
+ # def name_with_initial
+ # "#{first_name.first}. #{last_name}"
+ # end
+ # end
+ #
+ # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
+ # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
+ #
+ # If <tt>@post.author_ids</tt> is already <tt>[1]</tt>, this would return:
+ # <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
+ # <label for="post_author_ids_1">D. Heinemeier Hansson</label>
+ # <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
+ # <label for="post_author_ids_2">D. Thomas</label>
+ # <input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
+ # <label for="post_author_ids_3">M. Clark</label>
+ # <input name="post[author_ids][]" type="hidden" value="" />
+ #
+ # It is also possible to customize the way the elements will be shown by
+ # giving a block to the method:
+ # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
+ # b.label { b.check_box }
+ # end
+ #
+ # The argument passed to the block is a special kind of builder for this
+ # collection, which has the ability to generate the label and check box
+ # for the current item in the collection, with proper text and value.
+ # Using it, you can change the label and check box display order or even
+ # use the label as wrapper, as in the example above.
+ #
+ # The builder methods <tt>label</tt> and <tt>check_box</tt> also accept
+ # extra html options:
+ # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
+ # b.label(:class => "check_box") { b.check_box(:class => "check_box") }
+ # end
+ #
+ # There are also two special methods available: <tt>text</tt> and
+ # <tt>value</tt>, which are the current text and value methods for the
+ # item being rendered, respectively. You can use them like this:
+ # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
+ # b.label(:"data-value" => b.value) { b.check_box + b.text }
+ # end
+ def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
+ Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
+ end
+
private
def option_html_attributes(element)
return "" unless Array === element
- html_attributes = []
- element.select { |e| Hash === e }.reduce({}, :merge).each do |k, v|
- html_attributes << " #{k}=\"#{ERB::Util.html_escape(v.to_s)}\""
- end
- html_attributes.join
+
+ element.select { |e| Hash === e }.reduce({}, :merge).map do |k, v|
+ " #{k}=\"#{ERB::Util.html_escape(v.to_s)}\""
+ end.join
end
def option_text_and_value(option)
@@ -554,11 +679,12 @@ module ActionView
def extract_selected_and_disabled(selected)
if selected.is_a?(Proc)
- [ selected, nil ]
+ [selected, nil]
else
selected = Array.wrap(selected)
options = selected.extract_options!.symbolize_keys
- [ options.include?(:selected) ? options[:selected] : selected, options[:disabled] ]
+ selected_items = options.fetch(:selected, selected)
+ [selected_items, options[:disabled]]
end
end
@@ -571,68 +697,9 @@ module ActionView
selected
end
end
- end
-
- class InstanceTag #:nodoc:
- include FormOptionsHelper
-
- def to_select_tag(choices, options, html_options)
- selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
-
- # Grouped choices look like this:
- #
- # [nil, []]
- # { nil => [] }
- #
- if !choices.empty? && Array === choices.first.last
- option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
- else
- option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
- end
-
- select_content_tag(option_tags, options, html_options)
- end
-
- def to_collection_select_tag(collection, value_method, text_method, options, html_options)
- selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
- select_content_tag(
- options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options
- )
- end
-
- def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
- select_content_tag(
- option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options
- )
- end
-
- def to_time_zone_select_tag(priority_zones, options, html_options)
- select_content_tag(
- time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options
- )
- end
-
- private
- def add_options(option_tags, options, value = nil)
- if options[:include_blank]
- option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
- end
- if value.blank? && options[:prompt]
- prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
- option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags
- end
- option_tags.html_safe
- end
- def select_content_tag(option_tags, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
- if html_options["multiple"]
- tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
- else
- select
- end
+ def value_for_collection(item, value)
+ value.respond_to?(:call) ? value.call(item) : item.send(value)
end
end
@@ -652,6 +719,14 @@ module ActionView
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
end
+
+ def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {})
+ @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
+ end
+
+ def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {})
+ @template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 1424a3584d..53fd189c39 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -47,7 +47,7 @@ module ActionView
# <% end -%>
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
#
- # <%= form_tag('/posts', :remote => true) %>
+ # <%= form_tag('/posts', :remote => true) %>
# # => <form action="/posts" method="post" data-remote="true">
#
# form_tag('http://far.away.com/form', :authenticity_token => false)
@@ -393,21 +393,17 @@ module ActionView
# submit_tag "Save edits", :disabled => true
# # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
#
- #
# submit_tag "Complete sale", :disable_with => "Please wait..."
- # # => <input name="commit" data-disable-with="Please wait..."
- # # type="submit" value="Complete sale" />
+ # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
#
# submit_tag nil, :class => "form_submit"
# # => <input class="form_submit" name="commit" type="submit" />
#
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button"
- # # => <input class="edit_button" data-disable_with="Editing..."
- # # name="commit" type="submit" value="Edit" />
+ # # => <input class="edit_button" data-disable_with="Editing..." name="commit" type="submit" value="Edit" />
#
# submit_tag "Save", :confirm => "Are you sure?"
- # # => <input name='commit' type='submit' value='Save'
- # data-confirm="Are you sure?" />
+ # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
#
def submit_tag(value = "Save changes", options = {})
options = options.stringify_keys
@@ -451,12 +447,11 @@ module ActionView
# content_tag(:strong, 'Ask me!')
# end
# # => <button name="button" type="button">
- # <strong>Ask me!</strong>
- # </button>
+ # # <strong>Ask me!</strong>
+ # # </button>
#
# button_tag "Checkout", :disable_with => "Please wait..."
- # # => <button data-disable-with="Please wait..." name="button"
- # type="submit">Checkout</button>
+ # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
#
def button_tag(content_or_options = nil, options = nil, &block)
options = content_or_options if block_given? && content_or_options.is_a?(Hash)
@@ -554,6 +549,14 @@ module ActionView
end
alias phone_field_tag telephone_field_tag
+ # Creates a text field of type "date".
+ #
+ # ==== Options
+ # * Accepts the same options as text_field_tag.
+ def date_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
+ end
+
# Creates a text field of type "url".
#
# ==== Options
@@ -582,7 +585,7 @@ module ActionView
#
# ==== Examples
# number_field_tag 'quantity', nil, :in => 1...10
- # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
+ # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
def number_field_tag(name, value = nil, options = {})
options = options.stringify_keys
options["type"] ||= "number"
@@ -632,7 +635,7 @@ module ActionView
token_tag(authenticity_token)
else
html_options["method"] = "post"
- tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
+ method_tag(method) + token_tag(authenticity_token)
end
tags = utf8_enforcer_tag << method_tag
@@ -641,26 +644,16 @@ module ActionView
def form_tag_html(html_options)
extra_tags = extra_tags_for_form(html_options)
- (tag(:form, html_options, true) + extra_tags).html_safe
+ tag(:form, html_options, true) + extra_tags
end
def form_tag_in_block(html_options, &block)
content = capture(&block)
- output = ActiveSupport::SafeBuffer.new
- output.safe_concat(form_tag_html(html_options))
+ output = form_tag_html(html_options)
output << content
output.safe_concat("</form>")
end
- def token_tag(token)
- if token == false || !protect_against_forgery?
- ''
- else
- token ||= form_authenticity_token
- tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
- end
- end
-
# see http://www.w3.org/TR/html4/types.html#type-name
def sanitize_to_id(name)
name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 1adcd716f8..ac9e530f01 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -1,5 +1,4 @@
require 'action_view/helpers/tag_helper'
-require 'active_support/core_ext/string/encoding'
module ActionView
module Helpers
@@ -14,11 +13,7 @@ module ActionView
"'" => "\\'"
}
- if "ruby".encoding_aware?
- JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '&#x2028;'
- else
- JS_ESCAPE_MAP["\342\200\250"] = '&#x2028;'
- end
+ JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '&#x2028;'
# Escapes carriage returns and single and double quotes for JavaScript segments.
#
@@ -94,7 +89,7 @@ module ActionView
# If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
# the JavaScript is set, the helper appends "; return false;".
#
- # The +href+ attribute of the tag is set to "#" unles +html_options+ has one.
+ # The +href+ attribute of the tag is set to "#" unless +html_options+ has one.
#
# link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link"
# # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 7031694af4..b0860f87c4 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -1,7 +1,6 @@
# encoding: utf-8
require 'active_support/core_ext/big_decimal/conversions'
-require 'active_support/core_ext/float/rounding'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
@@ -33,33 +32,36 @@ module ActionView
# in the +options+ hash.
#
# ==== Options
- # * <tt>:area_code</tt> - Adds parentheses around the area code.
- # * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
- # * <tt>:extension</tt> - Specifies an extension to add to the end of the
- # generated number.
+ #
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
+ # * <tt>:extension</tt> - Specifies an extension to add to the end of the
+ # generated number.
# * <tt>:country_code</tt> - Sets the country code for the phone number.
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# ==== Examples
+ #
# number_to_phone(5551234) # => 555-1234
+ # number_to_phone("5551234") # => 555-1234
# number_to_phone(1235551234) # => 123-555-1234
# number_to_phone(1235551234, :area_code => true) # => (123) 555-1234
# number_to_phone(1235551234, :delimiter => " ") # => 123 555 1234
# number_to_phone(1235551234, :area_code => true, :extension => 555) # => (123) 555-1234 x 555
# number_to_phone(1235551234, :country_code => 1) # => +1-123-555-1234
+ # number_to_phone("123a456") # => 123a456
+ #
+ # number_to_phone("1234a567", :raise => true) # => InvalidNumberError
#
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
- # => +1.123.555.1234 x 1343
+ # # => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
return unless number
+ options = options.symbolize_keys
- begin
- Float(number)
- rescue ArgumentError, TypeError
- raise InvalidNumberError, number
- end if options[:raise]
+ parse_float(number, true) if options[:raise]
number = number.to_s.strip
- options = options.symbolize_keys
area_code = options[:area_code]
delimiter = options[:delimiter] || "-"
extension = options[:extension]
@@ -69,7 +71,7 @@ module ActionView
number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
else
number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
- number.slice!(0, 1) if number.starts_with?(delimiter) && !delimiter.blank?
+ number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
end
str = []
@@ -83,6 +85,7 @@ module ActionView
# in the +options+ hash.
#
# ==== Options
+ #
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
# * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
# * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
@@ -95,28 +98,32 @@ module ActionView
# an hyphen to the formatted number given by <tt>:format</tt>).
# Accepts the same fields than <tt>:format</tt>, except
# <tt>%n</tt> is here the absolute value of the number.
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# ==== Examples
+ #
# number_to_currency(1234567890.50) # => $1,234,567,890.50
# number_to_currency(1234567890.506) # => $1,234,567,890.51
# number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
- # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,506 €
+ # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,51 €
+ # number_to_currency("123a456") # => $123a456
+ #
+ # number_to_currency("123a456", :raise => true) # => InvalidNumberError
#
# number_to_currency(-1234567890.50, :negative_format => "(%u%n)")
- # # => ($1,234,567,890.51)
+ # # => ($1,234,567,890.50)
# number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "")
# # => &pound;1234567890,50
# number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "", :format => "%n %u")
# # => 1234567890,50 &pound;
def number_to_currency(number, options = {})
return unless number
+ options = options.symbolize_keys
- options.symbolize_keys!
+ currency = translations_for('currency', options[:locale])
+ currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
- currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
-
- defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
+ defaults = DEFAULT_CURRENCY_VALUES.merge(defaults_translations(options[:locale])).merge!(currency)
defaults[:negative_format] = "-" + options[:format] if options[:format]
options = defaults.merge!(options)
@@ -139,44 +146,56 @@ module ActionView
e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
end
end
-
end
- # Formats a +number+ as a percentage string (e.g., 65%). You can customize the
- # format in the +options+ hash.
+ # Formats a +number+ as a percentage string (e.g., 65%). You can customize the format in the +options+ hash.
#
# ==== Options
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
+ #
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current
+ # locale).
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+,
+ # the # of fractional digits (defaults to +false+).
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults
+ # to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator
+ # (defaults to +false+).
+ # * <tt>:format</tt> - Specifies the format of the percentage string
+ # The number field is <tt>%n</tt> (defaults to "%n%").
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# ==== Examples
+ #
# number_to_percentage(100) # => 100.000%
+ # number_to_percentage("98") # => 98.000%
# number_to_percentage(100, :precision => 0) # => 100%
# number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
# number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
# number_to_percentage(1000, :locale => :fr) # => 1 000,000%
+ # number_to_percentage("98a") # => 98a%
+ # number_to_percentage(100, :format => "%n %") # => 100 %
+ #
+ # number_to_percentage("98a", :raise => true) # => InvalidNumberError
def number_to_percentage(number, options = {})
return unless number
+ options = options.symbolize_keys
- options.symbolize_keys!
-
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
- percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :default => {})
- defaults = defaults.merge(percentage)
+ defaults = format_translations('percentage', options[:locale])
+ options = defaults.merge!(options)
- options = options.reverse_merge(defaults)
+ format = options[:format] || "%n%"
begin
- "#{number_with_precision(number, options.merge(:raise => true))}%".html_safe
+ value = number_with_precision(number, options.merge(:raise => true))
+ format.gsub(/%n/, value).html_safe
rescue InvalidNumberError => e
if options[:raise]
raise
else
- e.number.to_s.html_safe? ? "#{e.number}%".html_safe : "#{e.number}%"
+ formatted_number = format.gsub(/%n/, e.number)
+ e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
end
end
end
@@ -185,38 +204,36 @@ module ActionView
# customize the format in the +options+ hash.
#
# ==== Options
+ #
# * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
# * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# ==== Examples
+ #
# number_with_delimiter(12345678) # => 12,345,678
+ # number_with_delimiter("123456") # => 123,456
# number_with_delimiter(12345678.05) # => 12,345,678.05
# number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
- # number_with_delimiter(12345678, :separator => ",") # => 12,345,678
+ # number_with_delimiter(12345678, :delimiter => ",") # => 12,345,678
+ # number_with_delimiter(12345678.05, :separator => " ") # => 12,345,678 05
# number_with_delimiter(12345678.05, :locale => :fr) # => 12 345 678,05
+ # number_with_delimiter("112a") # => 112a
# number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
# # => 98 765 432,98
+ #
+ # number_with_delimiter("112a", :raise => true) # => raise InvalidNumberError
def number_with_delimiter(number, options = {})
- options.symbolize_keys!
+ options = options.symbolize_keys
- begin
- Float(number)
- rescue ArgumentError, TypeError
- if options[:raise]
- raise InvalidNumberError, number
- else
- return number
- end
- end
+ parse_float(number, options[:raise]) or return number
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
- options = options.reverse_merge(defaults)
+ options = defaults_translations(options[:locale]).merge(options)
parts = number.to_s.to_str.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
parts.join(options[:separator]).html_safe
-
end
# Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
@@ -224,12 +241,16 @@ module ActionView
# You can customize the format in the +options+ hash.
#
# ==== Options
- # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
- # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
- # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
- # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
- # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale).
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+,
+ # the # of fractional digits (defaults to +false+).
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults
+ # to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator
+ # (defaults to +false+).
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# ==== Examples
# number_with_precision(111.2345) # => 111.235
@@ -240,29 +261,21 @@ module ActionView
# number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
# number_with_precision(13, :precision => 5, :significant => true) # => 13.000
# number_with_precision(111.234, :locale => :fr) # => 111,234
+ #
# number_with_precision(13, :precision => 5, :significant => true, :strip_insignificant_zeros => true)
# # => 13
+ #
# number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3
# number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
# # => 1.111,23
def number_with_precision(number, options = {})
- options.symbolize_keys!
+ options = options.symbolize_keys
- number = begin
- Float(number)
- rescue ArgumentError, TypeError
- if options[:raise]
- raise InvalidNumberError, number
- else
- return number
- end
- end
+ number = (parse_float(number, options[:raise]) or return number)
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
- precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale], :default => {})
- defaults = defaults.merge(precision_defaults)
+ defaults = format_translations('precision', options[:locale])
+ options = defaults.merge!(options)
- options = options.reverse_merge(defaults) # Allow the user to unset default values: Eg.: :significant => false
precision = options.delete :precision
significant = options.delete :significant
strip_insignificant_zeros = options.delete :strip_insignificant_zeros
@@ -287,7 +300,6 @@ module ActionView
else
formatted_number
end
-
end
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
@@ -307,6 +319,7 @@ module ActionView
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI prefix (defaults to :binary)
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
# ==== Examples
# number_to_human_size(123) # => 123 Bytes
# number_to_human_size(1234) # => 1.21 KB
@@ -323,23 +336,13 @@ module ActionView
# number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB"
# number_to_human_size(524288000, :precision => 5) # => "500 MB"
def number_to_human_size(number, options = {})
- options.symbolize_keys!
+ options = options.symbolize_keys
- number = begin
- Float(number)
- rescue ArgumentError, TypeError
- if options[:raise]
- raise InvalidNumberError, number
- else
- return number
- end
- end
+ number = (parse_float(number, options[:raise]) or return number)
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
- human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
- defaults = defaults.merge(human)
+ defaults = format_translations('human', options[:locale])
+ options = defaults.merge!(options)
- options = options.reverse_merge(defaults)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
@@ -388,6 +391,7 @@ module ActionView
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>, <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
# * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
#
# %u The quantifier (ex.: 'thousand')
# %n The number
@@ -442,23 +446,13 @@ module ActionView
# number_to_human(0.34, :units => :distance) # => "34 centimeters"
#
def number_to_human(number, options = {})
- options.symbolize_keys!
+ options = options.symbolize_keys
- number = begin
- Float(number)
- rescue ArgumentError, TypeError
- if options[:raise]
- raise InvalidNumberError, number
- else
- return number
- end
- end
+ number = (parse_float(number, options[:raise]) or return number)
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
- human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
- defaults = defaults.merge(human)
+ defaults = format_translations('human', options[:locale])
+ options = defaults.merge!(options)
- options = options.reverse_merge(defaults)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
@@ -494,6 +488,25 @@ module ActionView
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip.html_safe
end
+ private
+
+ def format_translations(namespace, locale)
+ defaults_translations(locale).merge(translations_for(namespace, locale))
+ end
+
+ def defaults_translations(locale)
+ I18n.translate(:'number.format', :locale => locale, :default => {})
+ end
+
+ def translations_for(namespace, locale)
+ I18n.translate(:"number.#{namespace}.format", :locale => locale, :default => {})
+ end
+
+ def parse_float(number, raise_error)
+ Float(number)
+ rescue ArgumentError, TypeError
+ raise InvalidNumberError, number if raise_error
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/output_safety_helper.rb b/actionpack/lib/action_view/helpers/output_safety_helper.rb
index a035dd70ad..2e7e9dc50c 100644
--- a/actionpack/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionpack/lib/action_view/helpers/output_safety_helper.rb
@@ -28,11 +28,10 @@ module ActionView #:nodoc:
# # => "<p>foo</p><br /><p>bar</p>"
#
def safe_join(array, sep=$,)
- sep ||= "".html_safe
sep = ERB::Util.html_escape(sep)
array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index b351302d01..1a15459406 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -81,13 +81,11 @@ module ActionView
# <li id="person_123" class="person bar">...
#
def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block)
- if single_or_multiple_records.respond_to?(:to_ary)
- single_or_multiple_records.to_ary.map do |single_record|
- capture { content_tag_for_single_record(tag_name, single_record, prefix, options, &block) }
- end.join("\n").html_safe
- else
- content_tag_for_single_record(tag_name, single_or_multiple_records, prefix, options, &block)
- end
+ options, prefix = prefix, nil if prefix.is_a?(Hash)
+
+ Array(single_or_multiple_records).map do |single_record|
+ content_tag_for_single_record(tag_name, single_record, prefix, options, &block)
+ end.join("\n").html_safe
end
private
@@ -95,14 +93,11 @@ module ActionView
# Called by <tt>content_tag_for</tt> internally to render a content tag
# for each record.
def content_tag_for_single_record(tag_name, record, prefix, options, &block)
- options, prefix = prefix, nil if prefix.is_a?(Hash)
- options ||= {}
- options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
- if block.arity == 0
- content_tag(tag_name, capture(&block), options)
- else
- content_tag(tag_name, capture(record, &block), options)
- end
+ options = options ? options.dup : {}
+ options.merge!(:class => "#{dom_class(record, prefix)} #{options[:class]}".rstrip, :id => dom_id(record, prefix))
+
+ content = block.arity == 0 ? capture(&block) : capture(record, &block)
+ content_tag(tag_name, content, options)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index bcc8f6fbcb..7768c8c151 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/object/try'
require 'action_controller/vendor/html-scanner'
-require 'action_view/helpers/tag_helper'
module ActionView
# = Action View Sanitize Helpers
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 8c33ef09fa..ecd26891d6 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -118,38 +118,49 @@ module ActionView
# escape_once("&lt;&lt; Accept & Checkout")
# # => "&lt;&lt; Accept &amp; Checkout"
def escape_once(html)
- ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
+ ERB::Util.html_escape_once(html)
end
private
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
- "<#{name}#{tag_options}>#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
+ content = ERB::Util.h(content) if escape
+ "<#{name}#{tag_options}>#{content}</#{name}>".html_safe
end
def tag_options(options, escape = true)
- unless options.blank?
- attrs = []
- options.each_pair do |key, value|
- if key.to_s == 'data' && value.is_a?(Hash)
- value.each do |k, v|
- if !v.is_a?(String) && !v.is_a?(Symbol)
- v = v.to_json
- end
- v = ERB::Util.html_escape(v) if escape
- attrs << %(data-#{k.to_s.dasherize}="#{v}")
- end
- elsif BOOLEAN_ATTRIBUTES.include?(key)
- attrs << %(#{key}="#{key}") if value
- elsif !value.nil?
- final_value = value.is_a?(Array) ? value.join(" ") : value
- final_value = ERB::Util.html_escape(final_value) if escape
- attrs << %(#{key}="#{final_value}")
+ return if options.blank?
+ attrs = []
+ options.each_pair do |key, value|
+ if key.to_s == 'data' && value.is_a?(Hash)
+ value.each_pair do |k, v|
+ attrs << data_tag_option(k, v, escape)
end
+ elsif BOOLEAN_ATTRIBUTES.include?(key)
+ attrs << boolean_tag_option(key) if value
+ elsif !value.nil?
+ attrs << tag_option(key, value, escape)
end
- " #{attrs.sort * ' '}".html_safe unless attrs.empty?
end
+ " #{attrs.sort * ' '}".html_safe unless attrs.empty?
+ end
+
+ def data_tag_option(key, value, escape)
+ key = "data-#{key.to_s.dasherize}"
+ value = value.to_json if !value.is_a?(String) && !value.is_a?(Symbol)
+
+ tag_option(key, value, escape)
+ end
+
+ def boolean_tag_option(key)
+ %(#{key}="#{key}")
+ end
+
+ def tag_option(key, value, escape)
+ value = value.join(" ") if value.is_a?(Array)
+ value = ERB::Util.h(value) if escape
+ %(#{key}="#{value}")
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags.rb b/actionpack/lib/action_view/helpers/tags.rb
new file mode 100644
index 0000000000..3cf762877f
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags.rb
@@ -0,0 +1,33 @@
+module ActionView
+ module Helpers
+ module Tags #:nodoc:
+ extend ActiveSupport::Autoload
+
+ autoload :Base
+ autoload :CheckBox
+ autoload :CollectionCheckBoxes
+ autoload :CollectionRadioButtons
+ autoload :CollectionSelect
+ autoload :DateField
+ autoload :DateSelect
+ autoload :DatetimeSelect
+ autoload :EmailField
+ autoload :FileField
+ autoload :GroupedCollectionSelect
+ autoload :HiddenField
+ autoload :Label
+ autoload :NumberField
+ autoload :PasswordField
+ autoload :RadioButton
+ autoload :RangeField
+ autoload :SearchField
+ autoload :Select
+ autoload :TelField
+ autoload :TextArea
+ autoload :TextField
+ autoload :TimeSelect
+ autoload :TimeZoneSelect
+ autoload :UrlField
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb
new file mode 100644
index 0000000000..1ece0ad2fc
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/base.rb
@@ -0,0 +1,147 @@
+module ActionView
+ module Helpers
+ module Tags
+ class Base #:nodoc:
+ include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
+ include FormOptionsHelper
+
+ DEFAULT_FIELD_OPTIONS = { "size" => 30 }
+
+ attr_reader :object
+
+ def initialize(object_name, method_name, template_object, options = {})
+ @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
+ @template_object = template_object
+
+ @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
+ @object = retrieve_object(options.delete(:object))
+ @options = options
+ @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
+ end
+
+ # This is what child classes implement.
+ def render
+ raise NotImplementedError, "Subclasses must implement a render method"
+ end
+
+ private
+
+ def value(object)
+ object.send @method_name if object
+ end
+
+ def value_before_type_cast(object)
+ unless object.nil?
+ method_before_type_cast = @method_name + "_before_type_cast"
+
+ object.respond_to?(method_before_type_cast) ?
+ object.send(method_before_type_cast) :
+ value(object)
+ end
+ end
+
+ def retrieve_object(object)
+ if object
+ object
+ elsif @template_object.instance_variable_defined?("@#{@object_name}")
+ @template_object.instance_variable_get("@#{@object_name}")
+ end
+ rescue NameError
+ # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
+ nil
+ end
+
+ def retrieve_autoindex(pre_match)
+ object = self.object || @template_object.instance_variable_get("@#{pre_match}")
+ if object && object.respond_to?(:to_param)
+ object.to_param
+ else
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
+ end
+ end
+
+ def add_default_name_and_id_for_value(tag_value, options)
+ if tag_value.nil?
+ add_default_name_and_id(options)
+ else
+ specified_id = options["id"]
+ add_default_name_and_id(options)
+
+ if specified_id.blank? && options["id"].present?
+ options["id"] += "_#{sanitized_value(tag_value)}"
+ end
+ end
+ end
+
+ def add_default_name_and_id(options)
+ if options.has_key?("index")
+ options["name"] ||= tag_name_with_index(options["index"])
+ options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
+ options.delete("index")
+ elsif defined?(@auto_index)
+ options["name"] ||= tag_name_with_index(@auto_index)
+ options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
+ else
+ options["name"] ||= options['multiple'] ? tag_name_multiple : tag_name
+ options["id"] = options.fetch("id"){ tag_id }
+ end
+ options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
+ end
+
+ def tag_name
+ "#{@object_name}[#{sanitized_method_name}]"
+ end
+
+ def tag_name_multiple
+ "#{tag_name}[]"
+ end
+
+ def tag_name_with_index(index)
+ "#{@object_name}[#{index}][#{sanitized_method_name}]"
+ end
+
+ def tag_id
+ "#{sanitized_object_name}_#{sanitized_method_name}"
+ end
+
+ def tag_id_with_index(index)
+ "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
+ end
+
+ def sanitized_object_name
+ @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
+ end
+
+ def sanitized_method_name
+ @sanitized_method_name ||= @method_name.sub(/\?$/,"")
+ end
+
+ def sanitized_value(value)
+ value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
+ end
+
+ def select_content_tag(option_tags, options, html_options)
+ html_options = html_options.stringify_keys
+ add_default_name_and_id(html_options)
+ select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
+ if html_options["multiple"]
+ tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
+ else
+ select
+ end
+ end
+
+ def add_options(option_tags, options, value = nil)
+ if options[:include_blank]
+ option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
+ end
+ if value.blank? && options[:prompt]
+ prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
+ option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags
+ end
+ option_tags.html_safe
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/check_box.rb b/actionpack/lib/action_view/helpers/tags/check_box.rb
new file mode 100644
index 0000000000..579cdb9fc9
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/check_box.rb
@@ -0,0 +1,58 @@
+require 'action_view/helpers/tags/checkable'
+
+module ActionView
+ module Helpers
+ module Tags
+ class CheckBox < Base #:nodoc:
+ include Checkable
+
+ def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
+ @checked_value = checked_value
+ @unchecked_value = unchecked_value
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ options = @options.stringify_keys
+ options["type"] = "checkbox"
+ options["value"] = @checked_value
+ options["checked"] = "checked" if input_checked?(object, options)
+
+ if options["multiple"]
+ add_default_name_and_id_for_value(@checked_value, options)
+ options.delete("multiple")
+ else
+ add_default_name_and_id(options)
+ end
+
+ hidden = hidden_field_for_checkbox(options)
+ checkbox = tag("input", options)
+ hidden + checkbox
+ end
+
+ private
+
+ def checked?(value)
+ case value
+ when TrueClass, FalseClass
+ value
+ when NilClass
+ false
+ when Integer
+ value != 0
+ when String
+ value == @checked_value
+ when Array
+ value.include?(@checked_value)
+ else
+ value.to_i != 0
+ end
+ end
+
+ def hidden_field_for_checkbox(options)
+ @unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/checkable.rb b/actionpack/lib/action_view/helpers/tags/checkable.rb
new file mode 100644
index 0000000000..b97c0c68d7
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/checkable.rb
@@ -0,0 +1,16 @@
+module ActionView
+ module Helpers
+ module Tags
+ module Checkable
+ def input_checked?(object, options)
+ if options.has_key?("checked")
+ checked = options.delete "checked"
+ checked == true || checked == "checked"
+ else
+ checked?(value(object))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
new file mode 100644
index 0000000000..e23f5113fb
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -0,0 +1,37 @@
+require 'action_view/helpers/tags/collection_helpers'
+
+module ActionView
+ module Helpers
+ module Tags
+ class CollectionCheckBoxes < Base
+ include CollectionHelpers
+
+ class CheckBoxBuilder < Builder
+ def check_box(extra_html_options={})
+ html_options = extra_html_options.merge(@input_html_options)
+ @template_object.check_box(@object_name, @method_name, html_options, @value, nil)
+ end
+ end
+
+ def render
+ rendered_collection = render_collection do |item, value, text, default_html_options|
+ default_html_options[:multiple] = true
+ builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options)
+
+ if block_given?
+ yield builder
+ else
+ builder.check_box + builder.label
+ end
+ end
+
+ # Append a hidden field to make sure something will be sent back to the
+ # server if all check boxes are unchecked.
+ hidden = @template_object.hidden_field_tag(tag_name_multiple, "", :id => nil)
+
+ rendered_collection + hidden
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
new file mode 100644
index 0000000000..6f950e552a
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
@@ -0,0 +1,81 @@
+module ActionView
+ module Helpers
+ module Tags
+ module CollectionHelpers
+ class Builder
+ attr_reader :object, :text, :value
+
+ def initialize(template_object, object_name, method_name, object,
+ sanitized_attribute_name, text, value, input_html_options)
+ @template_object = template_object
+ @object_name = object_name
+ @method_name = method_name
+ @object = object
+ @sanitized_attribute_name = sanitized_attribute_name
+ @text = text
+ @value = value
+ @input_html_options = input_html_options
+ end
+
+ def label(label_html_options={}, &block)
+ @template_object.label(@object_name, @sanitized_attribute_name, @text, label_html_options, &block)
+ end
+ end
+
+ def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
+ @collection = collection
+ @value_method = value_method
+ @text_method = text_method
+ @html_options = html_options
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ private
+
+ def instantiate_builder(builder_class, item, value, text, html_options)
+ builder_class.new(@template_object, @object_name, @method_name, item,
+ sanitize_attribute_name(value), text, value, html_options)
+ end
+
+ # Generate default options for collection helpers, such as :checked and
+ # :disabled.
+ def default_html_options_for_collection(item, value) #:nodoc:
+ html_options = @html_options.dup
+
+ [:checked, :selected, :disabled].each do |option|
+ next unless current_value = @options[option]
+
+ accept = if current_value.respond_to?(:call)
+ current_value.call(item)
+ else
+ Array(current_value).include?(value)
+ end
+
+ if accept
+ html_options[option] = true
+ elsif option == :checked
+ html_options[option] = false
+ end
+ end
+
+ html_options
+ end
+
+ def sanitize_attribute_name(value) #:nodoc:
+ "#{sanitized_method_name}_#{sanitized_value(value)}"
+ end
+
+ def render_collection #:nodoc:
+ @collection.map do |item|
+ value = value_for_collection(item, @value_method)
+ text = value_for_collection(item, @text_method)
+ default_html_options = default_html_options_for_collection(item, value)
+
+ yield item, value, text, default_html_options
+ end.join.html_safe
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
new file mode 100644
index 0000000000..ba2035f074
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -0,0 +1,30 @@
+require 'action_view/helpers/tags/collection_helpers'
+
+module ActionView
+ module Helpers
+ module Tags
+ class CollectionRadioButtons < Base
+ include CollectionHelpers
+
+ class RadioButtonBuilder < Builder
+ def radio_button(extra_html_options={})
+ html_options = extra_html_options.merge(@input_html_options)
+ @template_object.radio_button(@object_name, @method_name, @value, html_options)
+ end
+ end
+
+ def render
+ render_collection do |item, value, text, default_html_options|
+ builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options)
+
+ if block_given?
+ yield builder
+ else
+ builder.radio_button + builder.label
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/collection_select.rb b/actionpack/lib/action_view/helpers/tags/collection_select.rb
new file mode 100644
index 0000000000..ec78e6e5f9
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/collection_select.rb
@@ -0,0 +1,28 @@
+module ActionView
+ module Helpers
+ module Tags
+ class CollectionSelect < Base #:nodoc:
+ def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
+ @collection = collection
+ @value_method = value_method
+ @text_method = text_method
+ @html_options = html_options
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ option_tags_options = {
+ :selected => @options.fetch(:selected) { value(@object) },
+ :disabled => @options[:disabled]
+ }
+
+ select_content_tag(
+ options_from_collection_for_select(@collection, @value_method, @text_method, option_tags_options),
+ @options, @html_options
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/date_field.rb b/actionpack/lib/action_view/helpers/tags/date_field.rb
new file mode 100644
index 0000000000..bb968e9f39
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/date_field.rb
@@ -0,0 +1,15 @@
+module ActionView
+ module Helpers
+ module Tags
+ class DateField < TextField #:nodoc:
+ def render
+ options = @options.stringify_keys
+ options["value"] = @options.fetch("value") { value(object).try(:to_date) }
+ options["size"] = nil
+ @options = options
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/date_select.rb b/actionpack/lib/action_view/helpers/tags/date_select.rb
new file mode 100644
index 0000000000..5d706087b0
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/date_select.rb
@@ -0,0 +1,70 @@
+module ActionView
+ module Helpers
+ module Tags
+ class DateSelect < Base #:nodoc:
+ def initialize(object_name, method_name, template_object, options, html_options)
+ @html_options = html_options
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ error_wrapping(datetime_selector(@options, @html_options).send("select_#{select_type}").html_safe)
+ end
+
+ class << self
+ def select_type
+ @select_type ||= self.name.split("::").last.sub("Select", "").downcase
+ end
+ end
+
+ private
+
+ def select_type
+ self.class.select_type
+ end
+
+ def datetime_selector(options, html_options)
+ datetime = value(object) || default_datetime(options)
+ @auto_index ||= nil
+
+ options = options.dup
+ options[:field_name] = @method_name
+ options[:include_position] = true
+ options[:prefix] ||= @object_name
+ options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
+
+ DateTimeSelector.new(datetime, options, html_options)
+ end
+
+ def default_datetime(options)
+ return if options[:include_blank] || options[:prompt]
+
+ case options[:default]
+ when nil
+ Time.current
+ when Date, Time
+ options[:default]
+ else
+ default = options[:default].dup
+
+ # Rename :minute and :second to :min and :sec
+ default[:min] ||= default[:minute]
+ default[:sec] ||= default[:second]
+
+ time = Time.current
+
+ [:year, :month, :day, :hour, :min, :sec].each do |key|
+ default[key] ||= time.send(key)
+ end
+
+ Time.utc_time(
+ default[:year], default[:month], default[:day],
+ default[:hour], default[:min], default[:sec]
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/datetime_select.rb b/actionpack/lib/action_view/helpers/tags/datetime_select.rb
new file mode 100644
index 0000000000..a32c840bce
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/datetime_select.rb
@@ -0,0 +1,8 @@
+module ActionView
+ module Helpers
+ module Tags
+ class DatetimeSelect < DateSelect #:nodoc:
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/email_field.rb b/actionpack/lib/action_view/helpers/tags/email_field.rb
new file mode 100644
index 0000000000..45cde507d7
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/email_field.rb
@@ -0,0 +1,8 @@
+module ActionView
+ module Helpers
+ module Tags
+ class EmailField < TextField #:nodoc:
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/file_field.rb b/actionpack/lib/action_view/helpers/tags/file_field.rb
new file mode 100644
index 0000000000..56442e1c14
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/file_field.rb
@@ -0,0 +1,12 @@
+module ActionView
+ module Helpers
+ module Tags
+ class FileField < TextField #:nodoc:
+ def render
+ @options.update(:size => nil)
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb b/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb
new file mode 100644
index 0000000000..507466a57a
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb
@@ -0,0 +1,24 @@
+module ActionView
+ module Helpers
+ module Tags
+ class GroupedCollectionSelect < Base #:nodoc:
+ def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
+ @collection = collection
+ @group_method = group_method
+ @group_label_method = group_label_method
+ @option_key_method = option_key_method
+ @option_value_method = option_value_method
+ @html_options = html_options
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ select_content_tag(
+ option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, value(@object)), @options, @html_options
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/hidden_field.rb b/actionpack/lib/action_view/helpers/tags/hidden_field.rb
new file mode 100644
index 0000000000..ea86596e0b
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/hidden_field.rb
@@ -0,0 +1,12 @@
+module ActionView
+ module Helpers
+ module Tags
+ class HiddenField < TextField #:nodoc:
+ def render
+ @options.update(:size => nil)
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/label.rb b/actionpack/lib/action_view/helpers/tags/label.rb
new file mode 100644
index 0000000000..1bd71c2778
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/label.rb
@@ -0,0 +1,65 @@
+module ActionView
+ module Helpers
+ module Tags
+ class Label < Base #:nodoc:
+ def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
+ content_is_options = content_or_options.is_a?(Hash)
+ if content_is_options
+ options = content_or_options
+ @content = nil
+ else
+ @content = content_or_options
+ end
+
+ options ||= {}
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render(&block)
+ options = @options.stringify_keys
+ tag_value = options.delete("value")
+ name_and_id = options.dup
+
+ if name_and_id["for"]
+ name_and_id["id"] = name_and_id["for"]
+ else
+ name_and_id.delete("id")
+ end
+
+ add_default_name_and_id_for_value(tag_value, name_and_id)
+ options.delete("index")
+ options.delete("namespace")
+ options["for"] = name_and_id["id"] unless options.key?("for")
+
+ if block_given?
+ @template_object.label_tag(name_and_id["id"], options, &block)
+ else
+ content = if @content.blank?
+ @object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
+ method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
+
+ if object.respond_to?(:to_model)
+ key = object.class.model_name.i18n_key
+ i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
+ end
+
+ i18n_default ||= ""
+ I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
+ else
+ @content.to_s
+ end
+
+ content ||= if object && object.class.respond_to?(:human_attribute_name)
+ object.class.human_attribute_name(@method_name)
+ end
+
+ content ||= @method_name.humanize
+
+ label_tag(name_and_id["id"], content, options)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/number_field.rb b/actionpack/lib/action_view/helpers/tags/number_field.rb
new file mode 100644
index 0000000000..e89fdbec46
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/number_field.rb
@@ -0,0 +1,19 @@
+module ActionView
+ module Helpers
+ module Tags
+ class NumberField < TextField #:nodoc:
+ def render
+ options = @options.stringify_keys
+ options['size'] ||= nil
+
+ if range = options.delete("in") || options.delete("within")
+ options.update("min" => range.min, "max" => range.max)
+ end
+
+ @options = options
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/password_field.rb b/actionpack/lib/action_view/helpers/tags/password_field.rb
new file mode 100644
index 0000000000..6e7a4d3c36
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/password_field.rb
@@ -0,0 +1,12 @@
+module ActionView
+ module Helpers
+ module Tags
+ class PasswordField < TextField #:nodoc:
+ def render
+ @options = {:value => nil}.merge!(@options)
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/radio_button.rb b/actionpack/lib/action_view/helpers/tags/radio_button.rb
new file mode 100644
index 0000000000..8a0421f061
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/radio_button.rb
@@ -0,0 +1,31 @@
+require 'action_view/helpers/tags/checkable'
+
+module ActionView
+ module Helpers
+ module Tags
+ class RadioButton < Base #:nodoc:
+ include Checkable
+
+ def initialize(object_name, method_name, template_object, tag_value, options)
+ @tag_value = tag_value
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ options = @options.stringify_keys
+ options["type"] = "radio"
+ options["value"] = @tag_value
+ options["checked"] = "checked" if input_checked?(object, options)
+ add_default_name_and_id_for_value(@tag_value, options)
+ tag("input", options)
+ end
+
+ private
+
+ def checked?(value)
+ value.to_s == @tag_value.to_s
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/range_field.rb b/actionpack/lib/action_view/helpers/tags/range_field.rb
new file mode 100644
index 0000000000..47db4680e7
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/range_field.rb
@@ -0,0 +1,8 @@
+module ActionView
+ module Helpers
+ module Tags
+ class RangeField < NumberField #:nodoc:
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/search_field.rb b/actionpack/lib/action_view/helpers/tags/search_field.rb
new file mode 100644
index 0000000000..818fd4b887
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/search_field.rb
@@ -0,0 +1,24 @@
+module ActionView
+ module Helpers
+ module Tags
+ class SearchField < TextField #:nodoc:
+ def render
+ options = @options.stringify_keys
+
+ if options["autosave"]
+ if options["autosave"] == true
+ options["autosave"] = request.host.split(".").reverse.join(".")
+ end
+ options["results"] ||= 10
+ end
+
+ if options["onsearch"]
+ options["incremental"] = true unless options.has_key?("incremental")
+ end
+
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/select.rb b/actionpack/lib/action_view/helpers/tags/select.rb
new file mode 100644
index 0000000000..53a108b7e6
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/select.rb
@@ -0,0 +1,41 @@
+module ActionView
+ module Helpers
+ module Tags
+ class Select < Base #:nodoc:
+ def initialize(object_name, method_name, template_object, choices, options, html_options)
+ @choices = choices
+ @choices = @choices.to_a if @choices.is_a?(Range)
+ @html_options = html_options
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ option_tags_options = {
+ :selected => @options.fetch(:selected) { value(@object) },
+ :disabled => @options[:disabled]
+ }
+
+ option_tags = if grouped_choices?
+ grouped_options_for_select(@choices, option_tags_options)
+ else
+ options_for_select(@choices, option_tags_options)
+ end
+
+ select_content_tag(option_tags, @options, @html_options)
+ end
+
+ private
+
+ # Grouped choices look like this:
+ #
+ # [nil, []]
+ # { nil => [] }
+ #
+ def grouped_choices?
+ !@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/tel_field.rb b/actionpack/lib/action_view/helpers/tags/tel_field.rb
new file mode 100644
index 0000000000..87c1f6b6b6
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/tel_field.rb
@@ -0,0 +1,8 @@
+module ActionView
+ module Helpers
+ module Tags
+ class TelField < TextField #:nodoc:
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/text_area.rb b/actionpack/lib/action_view/helpers/tags/text_area.rb
new file mode 100644
index 0000000000..a7db8eb437
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/text_area.rb
@@ -0,0 +1,20 @@
+module ActionView
+ module Helpers
+ module Tags
+ class TextArea < Base #:nodoc:
+ DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }
+
+ def render
+ options = DEFAULT_TEXT_AREA_OPTIONS.merge(@options.stringify_keys)
+ add_default_name_and_id(options)
+
+ if size = options.delete("size")
+ options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
+ end
+
+ content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options)
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/text_field.rb b/actionpack/lib/action_view/helpers/tags/text_field.rb
new file mode 100644
index 0000000000..ce5182d20f
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/text_field.rb
@@ -0,0 +1,30 @@
+module ActionView
+ module Helpers
+ module Tags
+ class TextField < Base #:nodoc:
+ def render
+ options = @options.stringify_keys
+ options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
+ options = DEFAULT_FIELD_OPTIONS.merge(options)
+ options["type"] ||= field_type
+ options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
+ options["value"] &&= ERB::Util.html_escape(options["value"])
+ add_default_name_and_id(options)
+ tag("input", options)
+ end
+
+ class << self
+ def field_type
+ @field_type ||= self.name.split("::").last.sub("Field", "").downcase
+ end
+ end
+
+ private
+
+ def field_type
+ self.class.field_type
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/time_select.rb b/actionpack/lib/action_view/helpers/tags/time_select.rb
new file mode 100644
index 0000000000..9e97deb706
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/time_select.rb
@@ -0,0 +1,8 @@
+module ActionView
+ module Helpers
+ module Tags
+ class TimeSelect < DateSelect #:nodoc:
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/time_zone_select.rb b/actionpack/lib/action_view/helpers/tags/time_zone_select.rb
new file mode 100644
index 0000000000..0a176157c3
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/time_zone_select.rb
@@ -0,0 +1,20 @@
+module ActionView
+ module Helpers
+ module Tags
+ class TimeZoneSelect < Base #:nodoc:
+ def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
+ @priority_zones = priority_zones
+ @html_options = html_options
+
+ super(object_name, method_name, template_object, options)
+ end
+
+ def render
+ select_content_tag(
+ time_zone_options_for_select(value(@object) || @options[:default], @priority_zones, @options[:model] || ActiveSupport::TimeZone), @options, @html_options
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/url_field.rb b/actionpack/lib/action_view/helpers/tags/url_field.rb
new file mode 100644
index 0000000000..1ffdfe0b3c
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/url_field.rb
@@ -0,0 +1,8 @@
+module ActionView
+ module Helpers
+ module Tags
+ class UrlField < TextField #:nodoc:
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 21074efe86..3dc651501e 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/filters'
-require 'action_view/helpers/tag_helper'
module ActionView
# = Action View Text Helpers
@@ -31,6 +30,7 @@ module ActionView
extend ActiveSupport::Concern
include SanitizeHelper
+ include TagHelper
# The preferred method of outputting text in your views is to use the
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
# do not operate as expected in an eRuby code block. If you absolutely must
@@ -90,11 +90,11 @@ module ActionView
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
# as a single-quoted string with \1 where the phrase is to be inserted (defaults to
- # '<strong class="highlight">\1</strong>')
+ # '<mark>\1</mark>')
#
# ==== Examples
# highlight('You searched for: rails', 'rails')
- # # => You searched for: <strong class="highlight">rails</strong>
+ # # => You searched for: <mark>rails</mark>
#
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
# # => You searched for: ruby, rails, dhh
@@ -111,16 +111,16 @@ module ActionView
def highlight(text, phrases, *args)
options = args.extract_options!
unless args.empty?
- options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
+ options[:highlighter] = args[0] || '<mark>\1</mark>'
end
- options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
+ options.reverse_merge!(:highlighter => '<mark>\1</mark>')
text = sanitize(text) unless options[:sanitize] == false
if text.blank? || phrases.blank?
text
else
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
- text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
+ text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter])
end.html_safe
end
@@ -162,15 +162,15 @@ module ActionView
options.reverse_merge!(:radius => 100, :omission => "...")
phrase = Regexp.escape(phrase)
- return unless found_pos = text.mb_chars =~ /(#{phrase})/i
+ return unless found_pos = text =~ /(#{phrase})/i
start_pos = [ found_pos - options[:radius], 0 ].max
- end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
+ end_pos = [ [ found_pos + phrase.length + options[:radius] - 1, 0].max, text.length ].min
prefix = start_pos > 0 ? options[:omission] : ""
- postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
+ postfix = end_pos < text.length - 1 ? options[:omission] : ""
- prefix + text.mb_chars[start_pos..end_pos].strip + postfix
+ prefix + text[start_pos..end_pos].strip + postfix
end
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index be64dc823e..cc74eff53a 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -45,11 +45,18 @@ module ActionView
# you know what kind of output to expect when you call translate in a template.
def translate(key, options = {})
options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
- translation = I18n.translate(scope_key_by_partial(key), options)
- if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
- translation.html_safe
+ if html_safe_translation_key?(key)
+ html_safe_options = options.dup
+ options.except(*I18n::RESERVED_KEYS).each do |name, value|
+ unless name == :count && value.is_a?(Numeric)
+ html_safe_options[name] = ERB::Util.html_escape(value.to_s)
+ end
+ end
+ translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
+
+ translation.respond_to?(:html_safe) ? translation.html_safe : translation
else
- translation
+ I18n.translate(scope_key_by_partial(key), options)
end
end
alias :t :translate
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 0c2e1aa3a9..b5fc882e31 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -97,12 +97,12 @@ module ActionView
# <%= url_for(:back) %>
# # if request.env["HTTP_REFERER"] is not set or is blank
# # => javascript:history.back()
- def url_for(options = {})
- options ||= {}
+ def url_for(options = nil)
case options
when String
options
- when Hash
+ when nil, Hash
+ options ||= {}
options = options.symbolize_keys.reverse_merge!(:only_path => options[:host].nil?)
super
when :back
@@ -301,7 +301,7 @@ module ActionView
# # <div><input value="Create" type="submit" /></div>
# # </form>"
#
- #
+ #
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
# :confirm => "Are you sure?", :method => :delete %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
@@ -323,34 +323,25 @@ module ActionView
# #
def button_to(name, options = {}, html_options = {})
html_options = html_options.stringify_keys
- convert_boolean_attributes!(html_options, %w( disabled ))
+ convert_boolean_attributes!(html_options, %w(disabled))
- method_tag = ''
- if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
- method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
- end
+ url = options.is_a?(String) ? options : url_for(options)
+ remote = html_options.delete('remote')
+
+ method = html_options.delete('method').to_s
+ method_tag = %w{put delete}.include?(method) ? method_tag(method) : ""
- form_method = method.to_s == 'get' ? 'get' : 'post'
+ form_method = method == 'get' ? 'get' : 'post'
form_options = html_options.delete('form') || {}
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
-
- remote = html_options.delete('remote')
-
- request_token_tag = ''
- if form_method == 'post' && protect_against_forgery?
- request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
- end
+ form_options.merge!(:method => form_method, :action => url)
+ form_options.merge!("data-remote" => "true") if remote
- url = options.is_a?(String) ? options : self.url_for(options)
- name ||= url
+ request_token_tag = form_method == 'post' ? token_tag : ''
html_options = convert_options_to_data_attributes(options, html_options)
+ html_options.merge!("type" => "submit", "value" => name || url)
- html_options.merge!("type" => "submit", "value" => name)
-
- form_options.merge!(:method => form_method, :action => url)
- form_options.merge!("data-remote" => "true") if remote
-
"#{tag(:form, form_options, true)}<div>#{method_tag}#{tag("input", html_options)}#{request_token_tag}</div></form>".html_safe
end
@@ -476,7 +467,7 @@ module ActionView
# string given as the value.
# * <tt>:subject</tt> - Preset the subject line of the email.
# * <tt>:body</tt> - Preset the body of the email.
- # * <tt>:cc</tt> - Carbon Copy addition recipients on the email.
+ # * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
#
# ==== Examples
@@ -503,7 +494,7 @@ module ActionView
extras = %w{ cc bcc body subject }.map { |item|
option = html_options.delete(item) || next
- "#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}"
+ "#{item}=#{Rack::Utils.escape_path(option)}"
}.compact
extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
@@ -599,11 +590,7 @@ module ActionView
# We ignore any extra parameters in the request_uri if the
# submitted url doesn't have any either. This lets the function
# work with things like ?order=asc
- if url_string.index("?")
- request_uri = request.fullpath
- else
- request_uri = request.path
- end
+ request_uri = url_string.index("?") ? request.fullpath : request.path
if url_string =~ /^\w+:\/\//
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
@@ -624,7 +611,7 @@ module ActionView
html_options["data-disable-with"] = disable_with if disable_with
html_options["data-confirm"] = confirm if confirm
- add_method_to_attributes!(html_options, method) if method
+ add_method_to_attributes!(html_options, method) if method
html_options
else
@@ -633,32 +620,16 @@ module ActionView
end
def link_to_remote_options?(options)
- options.is_a?(Hash) && options.key?('remote') && options.delete('remote')
+ options.is_a?(Hash) && options.delete('remote')
end
def add_method_to_attributes!(html_options, method)
if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
- html_options["rel"] = "#{html_options["rel"]} nofollow".strip
+ html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
end
html_options["data-method"] = method
end
- def options_for_javascript(options)
- if options.empty?
- '{}'
- else
- "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
- end
- end
-
- def array_or_string_for_javascript(option)
- if option.kind_of?(Array)
- "['#{option.join('\',\'')}']"
- elsif !option.nil?
- "'#{option}'"
- end
- end
-
# Processes the +html_options+ hash, converting the boolean
# attributes from true/false form into the form required by
# HTML/XHTML. (An attribute is considered to be boolean if
@@ -686,6 +657,19 @@ module ActionView
bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
html_options
end
+
+ def token_tag(token=nil)
+ if token == false || !protect_against_forgery?
+ ''
+ else
+ token ||= form_authenticity_token
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
+ end
+ end
+
+ def method_tag(method)
+ tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
+ end
end
end
end
diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml
index eb816b9446..8e9db634fb 100644
--- a/actionpack/lib/action_view/locale/en.yml
+++ b/actionpack/lib/action_view/locale/en.yml
@@ -37,6 +37,7 @@
# precision:
# significant: false
# strip_insignificant_zeros: false
+ format: "%n%"
# Used in number_to_precision()
precision:
@@ -146,9 +147,8 @@
# Default value for :prompt => true in FormOptionsHelper
prompt: "Please select"
- # Default translation keys for submit FormHelper
+ # Default translation keys for submit and button FormHelper
submit:
create: 'Create %{model}'
update: 'Update %{model}'
submit: 'Save %{model}'
-
diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb
index bf90d012bf..cc3a871576 100644
--- a/actionpack/lib/action_view/log_subscriber.rb
+++ b/actionpack/lib/action_view/log_subscriber.rb
@@ -12,9 +12,8 @@ module ActionView
alias :render_partial :render_template
alias :render_collection :render_template
- # TODO: Ideally, ActionView should have its own logger so it does not depend on AC.logger
def logger
- ActionController::Base.logger if defined?(ActionController::Base)
+ ActionView::Base.logger
end
protected
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index fa4bf70f77..90c4a2759a 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/module/remove_method'
@@ -20,7 +19,7 @@ module ActionView
def self.register_detail(name, options = {}, &block)
self.registered_details << name
- initialize = registered_details.map { |n| "self.#{n} = details[:#{n}]" }
+ initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
Accessors.send :define_method, :"default_#{name}", &block
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -29,7 +28,7 @@ module ActionView
end
def #{name}=(value)
- value = Array.wrap(value.presence || default_#{name})
+ value = value.present? ? Array(value) : default_#{name}
_set_detail(:#{name}, value) if value != @details[:#{name}]
end
@@ -44,7 +43,7 @@ module ActionView
module Accessors #:nodoc:
end
- register_detail(:locale) { [I18n.locale, I18n.default_locale] }
+ register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq }
register_detail(:formats) { Mime::SET.symbols }
register_detail(:handlers){ Template::Handlers.extensions }
@@ -56,7 +55,11 @@ module ActionView
@details_keys = Hash.new
def self.get(details)
- @details_keys[details.freeze] ||= new
+ @details_keys[details] ||= new
+ end
+
+ def self.clear
+ @details_keys.clear
end
def initialize
@@ -85,9 +88,9 @@ module ActionView
protected
def _set_detail(key, value)
+ @details = @details.dup if @details_key
@details_key = nil
- @details = @details.dup if @details.frozen?
- @details[key] = value.freeze
+ @details[key] = value
end
end
@@ -98,7 +101,7 @@ module ActionView
# Whenever setting view paths, makes a copy so we can manipulate then in
# instance objects as we wish.
def view_paths=(paths)
- @view_paths = ActionView::PathSet.new(Array.wrap(paths))
+ @view_paths = ActionView::PathSet.new(Array(paths))
end
def find(name, prefixes = [], partial = false, keys = [], options = {})
@@ -147,27 +150,18 @@ module ActionView
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
def normalize_name(name, prefixes) #:nodoc:
- name = name.to_s.sub(handlers_regexp) do |match|
- ActiveSupport::Deprecation.warn "Passing a template handler in the template name is deprecated. " \
- "You can simply remove the handler name or pass render :handlers => [:#{match[1..-1]}] instead.", caller
- ""
- end
+ prefixes = nil if prefixes.blank?
+ parts = name.to_s.split('/')
+ parts.shift if parts.first.empty?
+ name = parts.pop
- parts = name.split('/')
- name = parts.pop
+ return name, prefixes || [""] if parts.empty?
- prefixes = if prefixes.blank?
- [parts.join('/')]
- else
- prefixes.map { |prefix| [prefix, *parts].compact.join('/') }
- end
+ parts = parts.join('/')
+ prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
return name, prefixes
end
-
- def handlers_regexp #:nodoc:
- @@handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/
- end
end
include Accessors
@@ -227,7 +221,6 @@ module ActionView
end
# A method which only uses the first format in the formats array for layout lookup.
- # This method plays straight with instance variables for performance reasons.
def with_layout_format
if formats.size == 1
yield
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 80391d72cc..43371a1c49 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -8,6 +8,10 @@ module ActionView
config.action_view.stylesheet_expansions = {}
config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
+ initializer "action_view.logger" do
+ ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
+ end
+
initializer "action_view.cache_asset_ids" do |app|
unless app.config.cache_classes
ActiveSupport.on_load(:action_view) do
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index c0936441ac..a588abcee3 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -12,27 +12,15 @@ module ActionView
end
protected
-
+
def extract_details(options)
details = {}
@lookup_context.registered_details.each do |key|
next unless value = options[key]
- details[key] = Array.wrap(value)
+ details[key] = Array(value)
end
details
end
-
- def extract_format(value, details)
- if value.is_a?(String) && value.sub!(formats_regexp, "")
- ActiveSupport::Deprecation.warn "Passing the format in the template name is deprecated. " \
- "Please pass render with :formats => [:#{$1}] instead.", caller
- details[:formats] ||= [$1.to_sym]
- end
- end
-
- def formats_regexp
- @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
- end
def instrument(name, options={})
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 15cb9d0f76..3033294883 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -82,16 +82,17 @@ module ActionView
#
# This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
#
- # == Rendering objects with the RecordIdentifier
+ # == Rendering objects that respond to `to_partial_path`
#
- # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if
- # you're following its conventions for RecordIdentifier#partial_path. Examples:
+ # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
+ # and pick the proper path by checking `to_partial_path` method.
#
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
+ # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
# # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
# <%= render :partial => @account %>
#
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
+ # # that's why we can replace:
# # <%= render :partial => "posts/post", :collection => @posts %>
# <%= render :partial => @posts %>
#
@@ -106,13 +107,14 @@ module ActionView
# # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
# <%= render "account", :account => @buyer %>
#
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
- # <%= render(@account) %>
+ # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
+ # # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
+ # <%= render @account %>
#
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
+ # # that's why we can replace:
# # <%= render :partial => "posts/post", :collection => @posts %>
- # <%= render(@posts) %>
+ # <%= render @posts %>
#
# == Rendering partials with layouts
#
@@ -205,7 +207,7 @@ module ActionView
# Deadline: <%= user.deadline %>
# <%- end -%>
# <% end %>
- class PartialRenderer < AbstractRenderer #:nodoc:
+ class PartialRenderer < AbstractRenderer
PARTIAL_NAMES = Hash.new { |h,k| h[k] = {} }
def initialize(*)
@@ -297,7 +299,6 @@ module ActionView
"and is followed by any combinations of letters, numbers, or underscores.")
end
- extract_format(@path, @details)
self
end
@@ -366,27 +367,26 @@ module ActionView
path = if object.respond_to?(:to_partial_path)
object.to_partial_path
else
- ActiveSupport::Deprecation.warn "ActiveModel-compatible objects whose classes return a #model_name that responds to #partial_path are deprecated. Please respond to #to_partial_path directly instead."
- object.class.model_name.partial_path
+ raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
end
- @partial_names[path] ||= path.dup.tap do |object_path|
- merge_prefix_into_object_path(@context_prefix, object_path)
- end
+ @partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
end
def merge_prefix_into_object_path(prefix, object_path)
if prefix.include?(?/) && object_path.include?(?/)
- overlap = []
+ prefixes = []
prefix_array = File.dirname(prefix).split('/')
object_path_array = object_path.split('/')[0..-3] # skip model dir & partial
prefix_array.each_with_index do |dir, index|
- overlap << dir if dir == object_path_array[index]
+ break if dir == object_path_array[index]
+ prefixes << dir
end
- object_path.gsub!(/^#{overlap.join('/')}\//,'')
- object_path.insert(0, "#{File.dirname(prefix)}/")
+ (prefixes << object_path).join("/")
+ else
+ object_path
end
end
diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
index 1ccf5a8ddb..f46aabd2be 100644
--- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
@@ -1,7 +1,4 @@
-# 1.9 ships with Fibers but we need to require the extra
-# methods explicitly. We only load those extra methods if
-# Fiber is available in the first place.
-require 'fiber' if defined?(Fiber)
+require 'fiber'
module ActionView
# == TODO
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index ac91d333ba..f3abc6d533 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -1,12 +1,10 @@
require 'active_support/core_ext/object/try'
-require 'active_support/core_ext/array/wrap'
module ActionView
class TemplateRenderer < AbstractRenderer #:nodoc:
def render(context, options)
@view = context
@details = extract_details(options)
- extract_format(options[:file] || options[:template], @details)
template = determine_template(options)
freeze_formats(template.formats, true)
render_template(template, options[:layout], options[:locals])
@@ -26,6 +24,8 @@ module ActionView
elsif options.key?(:template)
options[:template].respond_to?(:render) ?
options[:template] : find_template(options[:template], options[:prefixes], false, keys, @details)
+ else
+ raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option."
end
end
@@ -58,14 +58,28 @@ module ActionView
# context object. If no layout is found, it checks if at least a layout with
# the given name exists across all details before raising the error.
def find_layout(layout, keys)
- begin
- with_layout_format do
- layout =~ /^\// ?
- with_fallbacks { find_template(layout, nil, false, keys, @details) } : find_template(layout, nil, false, keys, @details)
+ with_layout_format { resolve_layout(layout, keys) }
+ end
+
+ def resolve_layout(layout, keys)
+ case layout
+ when String
+ begin
+ if layout =~ /^\//
+ with_fallbacks { find_template(layout, nil, false, keys, @details) }
+ else
+ find_template(layout, nil, false, keys, @details)
+ end
+ rescue ActionView::MissingTemplate
+ all_details = @details.merge(:formats => @lookup_context.default_formats)
+ raise unless template_exists?(layout, nil, false, keys, all_details)
end
- rescue ActionView::MissingTemplate
- all_details = @details.merge(:formats => @lookup_context.default_formats)
- raise unless template_exists?(layout, nil, false, keys, all_details)
+ when Proc
+ resolve_layout(layout.call, keys)
+ when FalseClass
+ nil
+ else
+ layout
end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 10797c010f..edb3d427d5 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/kernel/singleton_class'
@@ -122,7 +121,7 @@ module ActionView
@locals = details[:locals] || []
@virtual_path = details[:virtual_path]
@updated_at = details[:updated_at] || Time.now
- @formats = Array.wrap(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
+ @formats = Array(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
end
# Returns if the underlying handler supports streaming. If so,
@@ -173,6 +172,50 @@ module ActionView
@inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
end
+ # This method is responsible for properly setting the encoding of the
+ # source. Until this point, we assume that the source is BINARY data.
+ # If no additional information is supplied, we assume the encoding is
+ # the same as <tt>Encoding.default_external</tt>.
+ #
+ # The user can also specify the encoding via a comment on the first
+ # line of the template (# encoding: NAME-OF-ENCODING). This will work
+ # with any template engine, as we process out the encoding comment
+ # before passing the source on to the template engine, leaving a
+ # blank line in its stead.
+ def encode!
+ return unless source.encoding == Encoding::BINARY
+
+ # Look for # encoding: *. If we find one, we'll encode the
+ # String in that encoding, otherwise, we'll use the
+ # default external encoding.
+ if source.sub!(/\A#{ENCODING_FLAG}/, '')
+ encoding = magic_encoding = $1
+ else
+ encoding = Encoding.default_external
+ end
+
+ # Tag the source with the default external encoding
+ # or the encoding specified in the file
+ source.force_encoding(encoding)
+
+ # If the user didn't specify an encoding, and the handler
+ # handles encodings, we simply pass the String as is to
+ # the handler (with the default_external tag)
+ if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
+ source
+ # Otherwise, if the String is valid in the encoding,
+ # encode immediately to default_internal. This means
+ # that if a handler doesn't handle encodings, it will
+ # always get Strings in the default_internal
+ elsif source.valid_encoding?
+ source.encode!
+ # Otherwise, since the String is invalid in the encoding
+ # specified, raise an exception
+ else
+ raise WrongEncodingError.new(source, encoding)
+ end
+ end
+
protected
# Compile a template. This method ensures a template is compiled
@@ -195,15 +238,7 @@ module ActionView
end
# Among other things, this method is responsible for properly setting
- # the encoding of the source. Until this point, we assume that the
- # source is BINARY data. If no additional information is supplied,
- # we assume the encoding is the same as <tt>Encoding.default_external</tt>.
- #
- # The user can also specify the encoding via a comment on the first
- # line of the template (# encoding: NAME-OF-ENCODING). This will work
- # with any template engine, as we process out the encoding comment
- # before passing the source on to the template engine, leaving a
- # blank line in its stead.
+ # the encoding of the compiled template.
#
# If the template engine handles encodings, we send the encoded
# String to the engine without further processing. This allows
@@ -215,40 +250,8 @@ module ActionView
# In general, this means that templates will be UTF-8 inside of Rails,
# regardless of the original source encoding.
def compile(view, mod) #:nodoc:
+ encode!
method_name = self.method_name
-
- if source.encoding_aware?
- # Look for # encoding: *. If we find one, we'll encode the
- # String in that encoding, otherwise, we'll use the
- # default external encoding.
- if source.sub!(/\A#{ENCODING_FLAG}/, '')
- encoding = magic_encoding = $1
- else
- encoding = Encoding.default_external
- end
-
- # Tag the source with the default external encoding
- # or the encoding specified in the file
- source.force_encoding(encoding)
-
- # If the user didn't specify an encoding, and the handler
- # handles encodings, we simply pass the String as is to
- # the handler (with the default_external tag)
- if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
- source
- # Otherwise, if the String is valid in the encoding,
- # encode immediately to default_internal. This means
- # that if a handler doesn't handle encodings, it will
- # always get Strings in the default_internal
- elsif source.valid_encoding?
- source.encode!
- # Otherwise, since the String is invalid in the encoding
- # specified, raise an exception
- else
- raise WrongEncodingError.new(source, encoding)
- end
- end
-
code = @handler.call(self)
# Make sure that the resulting String to be evalled is in the
@@ -261,20 +264,18 @@ module ActionView
end
end_src
- if source.encoding_aware?
- # Make sure the source is in the encoding of the returned code
- source.force_encoding(code.encoding)
+ # Make sure the source is in the encoding of the returned code
+ source.force_encoding(code.encoding)
- # In case we get back a String from a handler that is not in
- # BINARY or the default_internal, encode it to the default_internal
- source.encode!
+ # In case we get back a String from a handler that is not in
+ # BINARY or the default_internal, encode it to the default_internal
+ source.encode!
- # Now, validate that the source we got back from the template
- # handler is valid in the default_internal. This is for handlers
- # that handle encoding but screw up
- unless source.valid_encoding?
- raise WrongEncodingError.new(@source, Encoding.default_internal)
- end
+ # Now, validate that the source we got back from the template
+ # handler is valid in the default_internal. This is for handlers
+ # that handle encoding but screw up
+ unless source.valid_encoding?
+ raise WrongEncodingError.new(@source, Encoding.default_internal)
end
begin
@@ -287,7 +288,7 @@ module ActionView
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
end
- raise ActionView::Template::Error.new(self, {}, e)
+ raise ActionView::Template::Error.new(self, e)
end
end
@@ -296,9 +297,12 @@ module ActionView
e.sub_template_of(self)
raise e
else
- assigns = view.respond_to?(:assigns) ? view.assigns : {}
- template = @virtual_path ? refresh(view) : self
- raise Template::Error.new(template, assigns, e)
+ template = self
+ unless template.source
+ template = refresh(view)
+ template.encode!
+ end
+ raise Template::Error.new(template, e)
end
end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index 587e37a84f..d8258f7b11 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/array/wrap"
require "active_support/core_ext/enumerable"
module ActionView
@@ -30,7 +29,7 @@ module ActionView
def initialize(paths, path, prefixes, partial, details, *)
@path = path
- prefixes = Array.wrap(prefixes)
+ prefixes = Array(prefixes)
template_type = if partial
"partial"
elsif path =~ /layouts/i
@@ -56,9 +55,9 @@ module ActionView
attr_reader :original_exception, :backtrace
- def initialize(template, assigns, original_exception)
+ def initialize(template, original_exception)
super(original_exception.message)
- @template, @assigns, @original_exception = template, assigns.dup, original_exception
+ @template, @original_exception = template, original_exception
@sub_templates = nil
@backtrace = original_exception.backtrace
end
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index aa693335e3..67978ada7e 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -17,15 +17,12 @@ module ActionView #:nodoc:
@@template_extensions ||= @@template_handlers.keys
end
- # Register a class that knows how to handle template files with the given
+ # Register an object that knows how to handle template files with the given
# extension. This can be used to implement new template types.
- # The constructor for the class must take the ActiveView::Base instance
- # as a parameter, and the class must implement a +render+ method that
- # takes the contents of the template to render as well as the Hash of
- # local assigns available to the template. The +render+ method ought to
- # return the rendered template as a string.
- def register_template_handler(extension, klass)
- @@template_handlers[extension.to_sym] = klass
+ # The handler must respond to `:call`, which will be passed the template
+ # and should return the rendered template as a String.
+ def register_template_handler(extension, handler)
+ @@template_handlers[extension.to_sym] = handler
end
def template_handler_extensions
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
index 2c52cfd90e..34397c3bcf 100644
--- a/actionpack/lib/action_view/template/handlers/builder.rb
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -6,12 +6,21 @@ module ActionView
self.default_format = Mime::XML
def call(template)
- require 'builder'
+ require_engine
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
"self.output_buffer = xml.target!;" +
template.source +
";xml.target!;"
end
+
+ protected
+
+ def require_engine
+ @required ||= begin
+ require "builder"
+ true
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 77720e2bc8..323df67c97 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -1,5 +1,5 @@
require 'action_dispatch/http/mime_type'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/attribute'
require 'erubis'
module ActionView
@@ -67,23 +67,19 @@ module ActionView
end
def call(template)
- if template.source.encoding_aware?
- # First, convert to BINARY, so in case the encoding is
- # wrong, we can still find an encoding tag
- # (<%# encoding %>) inside the String using a regular
- # expression
- template_source = template.source.dup.force_encoding("BINARY")
+ # First, convert to BINARY, so in case the encoding is
+ # wrong, we can still find an encoding tag
+ # (<%# encoding %>) inside the String using a regular
+ # expression
+ template_source = template.source.dup.force_encoding("BINARY")
- erb = template_source.gsub(ENCODING_TAG, '')
- encoding = $2
+ erb = template_source.gsub(ENCODING_TAG, '')
+ encoding = $2
- erb.force_encoding valid_encoding(template.source.dup, encoding)
+ erb.force_encoding valid_encoding(template.source.dup, encoding)
- # Always make sure we return a String in the default_internal
- erb.encode!
- else
- erb = template.source.dup
- end
+ # Always make sure we return a String in the default_internal
+ erb.encode!
self.class.erb_implementation.new(
erb,
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index f855ea257c..7fa86866a7 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -1,6 +1,5 @@
require "pathname"
require "active_support/core_ext/class"
-require "active_support/core_ext/io"
require "action_view/template"
module ActionView
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 9ebe498192..fece499c94 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -59,8 +59,10 @@ module ActionView
end
def determine_default_helper_class(name)
- mod = name.sub(/Test$/, '').safe_constantize
+ mod = name.sub(/Test$/, '').constantize
mod.is_a?(Class) ? nil : mod
+ rescue NameError
+ nil
end
def helper_method(*methods)
@@ -183,32 +185,32 @@ module ActionView
alias_method :_view, :view
- INTERNAL_IVARS = %w{
- @__name__
- @__io__
- @_assertion_wrapped
- @_assertions
- @_result
- @_routes
- @controller
- @layouts
- @locals
- @method_name
- @output_buffer
- @partials
- @passed
- @rendered
- @request
- @routes
- @templates
- @options
- @test_passed
- @view
- @view_context_class
- }
+ INTERNAL_IVARS = [
+ :@__name__,
+ :@__io__,
+ :@_assertion_wrapped,
+ :@_assertions,
+ :@_result,
+ :@_routes,
+ :@controller,
+ :@layouts,
+ :@locals,
+ :@method_name,
+ :@output_buffer,
+ :@partials,
+ :@passed,
+ :@rendered,
+ :@request,
+ :@routes,
+ :@templates,
+ :@options,
+ :@test_passed,
+ :@view,
+ :@view_context_class
+ ]
def _user_defined_ivars
- instance_variables.map(&:to_s) - INTERNAL_IVARS
+ instance_variables - INTERNAL_IVARS
end
# Returns a Hash of instance variables and their values, as defined by
@@ -216,8 +218,8 @@ module ActionView
# rendered. This is generally intended for internal use and extension
# frameworks.
def view_assigns
- Hash[_user_defined_ivars.map do |var|
- [var[1, var.length].to_sym, instance_variable_get(var)]
+ Hash[_user_defined_ivars.map do |ivar|
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
end]
end
@@ -233,10 +235,8 @@ module ActionView
super
end
end
-
end
include Behavior
-
end
end
diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake
index a61a121d55..2e92fe416b 100644
--- a/actionpack/lib/sprockets/assets.rake
+++ b/actionpack/lib/sprockets/assets.rake
@@ -1,12 +1,16 @@
require "fileutils"
namespace :assets do
- def ruby_rake_task(task)
+ def ruby_rake_task(task, fork = true)
env = ENV['RAILS_ENV'] || 'production'
groups = ENV['RAILS_GROUPS'] || 'assets'
args = [$0, task,"RAILS_ENV=#{env}","RAILS_GROUPS=#{groups}"]
args << "--trace" if Rake.application.options.trace
- ruby(*args)
+ if $0 =~ /rake\.bat\Z/i
+ Kernel.exec $0, *args
+ else
+ fork ? ruby(*args) : Kernel.exec(FileUtils::RUBY, *args)
+ end
end
# We are currently running with no explicit bundler group
@@ -59,14 +63,14 @@ namespace :assets do
# required in order to compile digestless assets as the
# environment has already cached the assets on the primary
# run.
- ruby_rake_task "assets:precompile:nondigest" if Rails.application.config.assets.digest
+ ruby_rake_task("assets:precompile:nondigest", false) if Rails.application.config.assets.digest
end
- task :primary => ["assets:environment", "tmp:cache:clear"] do
+ task :primary => ["assets:cache:clean"] do
internal_precompile
end
- task :nondigest => ["assets:environment", "tmp:cache:clear"] do
+ task :nondigest => ["assets:cache:clean"] do
internal_precompile(false)
end
end
@@ -77,13 +81,19 @@ namespace :assets do
end
namespace :clean do
- task :all => ["assets:environment", "tmp:cache:clear"] do
+ task :all => ["assets:cache:clean"] do
config = Rails.application.config
public_asset_path = File.join(Rails.public_path, config.assets.prefix)
rm_rf public_asset_path, :secure => true
end
end
+ namespace :cache do
+ task :clean => ["assets:environment"] do
+ Rails.application.assets.cache.clear
+ end
+ end
+
task :environment do
if Rails.application.config.assets.initialize_on_precompile
Rake::Task["environment"].invoke
diff --git a/actionpack/lib/sprockets/compressors.rb b/actionpack/lib/sprockets/compressors.rb
index cb3e13314b..8b728d6570 100644
--- a/actionpack/lib/sprockets/compressors.rb
+++ b/actionpack/lib/sprockets/compressors.rb
@@ -1,38 +1,28 @@
module Sprockets
module Compressors
+ extend self
+
@@css_compressors = {}
@@js_compressors = {}
@@default_css_compressor = nil
@@default_js_compressor = nil
- def self.register_css_compressor(name, klass, options = {})
+ def register_css_compressor(name, klass, options = {})
@@default_css_compressor = name.to_sym if options[:default] || @@default_css_compressor.nil?
- @@css_compressors[name.to_sym] = {:klass => klass.to_s, :require => options[:require]}
+ @@css_compressors[name.to_sym] = { :klass => klass.to_s, :require => options[:require] }
end
- def self.register_js_compressor(name, klass, options = {})
+ def register_js_compressor(name, klass, options = {})
@@default_js_compressor = name.to_sym if options[:default] || @@default_js_compressor.nil?
- @@js_compressors[name.to_sym] = {:klass => klass.to_s, :require => options[:require]}
+ @@js_compressors[name.to_sym] = { :klass => klass.to_s, :require => options[:require] }
end
- def self.registered_css_compressor(name)
- if name.respond_to?(:to_sym)
- compressor = @@css_compressors[name.to_sym] || @@css_compressors[@@default_css_compressor]
- require compressor[:require] if compressor[:require]
- compressor[:klass].constantize.new
- else
- name
- end
+ def registered_css_compressor(name)
+ find_registered_compressor name, @@css_compressors, @@default_css_compressor
end
- def self.registered_js_compressor(name)
- if name.respond_to?(:to_sym)
- compressor = @@js_compressors[name.to_sym] || @@js_compressors[@@default_js_compressor]
- require compressor[:require] if compressor[:require]
- compressor[:klass].constantize.new
- else
- name
- end
+ def registered_js_compressor(name)
+ find_registered_compressor name, @@js_compressors, @@default_js_compressor
end
# The default compressors must be registered in default plugins (ex. Sass-Rails)
@@ -43,6 +33,18 @@ module Sprockets
register_css_compressor(:yui, 'YUI::CssCompressor', :require => 'yui/compressor')
register_js_compressor(:closure, 'Closure::Compiler', :require => 'closure-compiler')
register_js_compressor(:yui, 'YUI::JavaScriptCompressor', :require => 'yui/compressor')
+
+ private
+
+ def find_registered_compressor(name, compressors_hash, default_compressor_name)
+ if name.respond_to?(:to_sym)
+ compressor = compressors_hash[name.to_sym] || compressors_hash[default_compressor_name]
+ require compressor[:require] if compressor[:require]
+ compressor[:klass].constantize.new
+ else
+ name
+ end
+ end
end
# An asset compressor which does nothing.
diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb
index ddf9b08b54..976ae5a76d 100644
--- a/actionpack/lib/sprockets/helpers/rails_helper.rb
+++ b/actionpack/lib/sprockets/helpers/rails_helper.rb
@@ -26,10 +26,10 @@ module Sprockets
sources.collect do |source|
if debug && asset = asset_paths.asset_for(source, 'js')
asset.to_a.map { |dep|
- super(dep.to_s, { :src => asset_path(dep, :ext => 'js', :body => true, :digest => digest) }.merge!(options))
+ super(dep.pathname.to_s, { :src => path_to_asset(dep, :ext => 'js', :body => true, :digest => digest) }.merge!(options))
}
else
- super(source.to_s, { :src => asset_path(source, :ext => 'js', :body => body, :digest => digest) }.merge!(options))
+ super(source.to_s, { :src => path_to_asset(source, :ext => 'js', :body => body, :digest => digest) }.merge!(options))
end
end.join("\n").html_safe
end
@@ -43,10 +43,10 @@ module Sprockets
sources.collect do |source|
if debug && asset = asset_paths.asset_for(source, 'css')
asset.to_a.map { |dep|
- super(dep.to_s, { :href => asset_path(dep, :ext => 'css', :body => true, :protocol => :request, :digest => digest) }.merge!(options))
+ super(dep.pathname.to_s, { :href => path_to_asset(dep, :ext => 'css', :body => true, :protocol => :request, :digest => digest) }.merge!(options))
}
else
- super(source.to_s, { :href => asset_path(source, :ext => 'css', :body => body, :protocol => :request, :digest => digest) }.merge!(options))
+ super(source.to_s, { :href => path_to_asset(source, :ext => 'css', :body => body, :protocol => :request, :digest => digest) }.merge!(options))
end
end.join("\n").html_safe
end
@@ -56,19 +56,25 @@ module Sprockets
path = asset_paths.compute_public_path(source, asset_prefix, options.merge(:body => true))
options[:body] ? "#{path}?body=1" : path
end
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with an asset_path named route
def image_path(source)
- asset_path(source)
+ path_to_asset(source)
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
+ def font_path(source)
+ path_to_asset(source)
+ end
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
+
def javascript_path(source)
- asset_path(source)
+ path_to_asset(source, :ext => 'js')
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with an javascript_path named route
def stylesheet_path(source)
- asset_path(source)
+ path_to_asset(source, :ext => 'css')
end
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with an stylesheet_path named route
@@ -113,11 +119,6 @@ module Sprockets
class AssetNotPrecompiledError < StandardError; end
- # Return the filesystem path for the source
- def compute_source_path(source, ext)
- asset_for(source, ext)
- end
-
def asset_for(source, ext)
source = source.to_s
return nil if is_uri?(source)
@@ -154,7 +155,7 @@ module Sprockets
end
def rewrite_extension(source, dir, ext)
- if ext && File.extname(source).empty?
+ if ext && File.extname(source) != ".#{ext}"
"#{source}.#{ext}"
else
source
diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb
index 3d330bd91a..2bc482a39d 100644
--- a/actionpack/lib/sprockets/railtie.rb
+++ b/actionpack/lib/sprockets/railtie.rb
@@ -10,8 +10,6 @@ module Sprockets
# TODO: Get rid of config.assets.enabled
class Railtie < ::Rails::Railtie
- config.action_controller.default_asset_host_protocol = :relative
-
rake_tasks do
load "sprockets/assets.rake"
end
@@ -23,9 +21,12 @@ module Sprockets
require 'sprockets'
app.assets = Sprockets::Environment.new(app.root.to_s) do |env|
- env.logger = ::Rails.logger
env.version = ::Rails.env + "-#{config.assets.version}"
+ if config.assets.logger != false
+ env.logger = config.assets.logger || ::Rails.logger
+ end
+
if config.assets.cache_store != false
env.cache = ActiveSupport::Cache.lookup_store(config.assets.cache_store) || ::Rails.cache
end
diff --git a/actionpack/lib/sprockets/static_compiler.rb b/actionpack/lib/sprockets/static_compiler.rb
index 32a9d66e6e..9bbb464474 100644
--- a/actionpack/lib/sprockets/static_compiler.rb
+++ b/actionpack/lib/sprockets/static_compiler.rb
@@ -8,9 +8,10 @@ module Sprockets
@env = env
@target = target
@paths = paths
- @digest = options.key?(:digest) ? options.delete(:digest) : true
- @manifest = options.key?(:manifest) ? options.delete(:manifest) : true
+ @digest = options.fetch(:digest, true)
+ @manifest = options.fetch(:manifest, true)
@manifest_path = options.delete(:manifest_path) || target
+ @zip_files = options.delete(:zip_files) || /\.(?:css|html|js|svg|txt|xml)$/
end
def compile
@@ -36,7 +37,7 @@ module Sprockets
filename = File.join(target, path)
FileUtils.mkdir_p File.dirname(filename)
asset.write_to(filename)
- asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/
+ asset.write_to("#{filename}.gz") if filename.to_s =~ @zip_files
end
end
diff --git a/actionpack/test/abstract/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb
index 86208899f8..fc25718d9e 100644
--- a/actionpack/test/abstract/layouts_test.rb
+++ b/actionpack/test/abstract/layouts_test.rb
@@ -57,11 +57,8 @@ module AbstractControllerTests
layout "hello_override"
end
- class WithNilChild < WithString
- layout nil
- end
-
class WithStringImpliedChild < WithString
+ layout nil
end
class WithChildOfImplied < WithStringImpliedChild
@@ -141,6 +138,30 @@ module AbstractControllerTests
end
end
+ class WithOnlyConditional < WithStringImpliedChild
+ layout "overwrite", :only => :show
+
+ def index
+ render :template => ActionView::Template::Text.new("Hello index!")
+ end
+
+ def show
+ render :template => ActionView::Template::Text.new("Hello show!")
+ end
+ end
+
+ class WithExceptConditional < WithStringImpliedChild
+ layout "overwrite", :except => :show
+
+ def index
+ render :template => ActionView::Template::Text.new("Hello index!")
+ end
+
+ def show
+ render :template => ActionView::Template::Text.new("Hello show!")
+ end
+ end
+
class TestBase < ActiveSupport::TestCase
test "when no layout is specified, and no default is available, render without a layout" do
controller = Blank.new
@@ -238,12 +259,6 @@ module AbstractControllerTests
assert_equal "With Implied Hello string!", controller.response_body
end
- test "when a child controller specifies layout nil, do not use the parent layout" do
- controller = WithNilChild.new
- controller.process(:index)
- assert_equal "Hello string!", controller.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
controller = WithChildOfImplied.new
@@ -260,6 +275,30 @@ module AbstractControllerTests
end
end
end
+
+ test "when specify an :only option which match current action name" do
+ controller = WithOnlyConditional.new
+ controller.process(:show)
+ assert_equal "Overwrite Hello show!", controller.response_body
+ end
+
+ test "when specify an :only option which does not match current action name" do
+ controller = WithOnlyConditional.new
+ controller.process(:index)
+ assert_equal "With Implied Hello index!", controller.response_body
+ end
+
+ test "when specify an :except option which match current action name" do
+ controller = WithExceptConditional.new
+ controller.process(:show)
+ assert_equal "With Implied Hello show!", controller.response_body
+ end
+
+ test "when specify an :except option which does not match current action name" do
+ controller = WithExceptConditional.new
+ controller.process(:index)
+ assert_equal "Overwrite Hello index!", controller.response_body
+ end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index 8ec50fd57a..0194ee943f 100644
--- a/actionpack/test/abstract/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
# class TranslatingController < ActionController::Base
# end
-class TranslationControllerTest < Test::Unit::TestCase
+class TranslationControllerTest < ActiveSupport::TestCase
def setup
@controller = ActionController::Base.new
end
@@ -23,4 +23,4 @@ class TranslationControllerTest < Test::Unit::TestCase
def test_action_controller_base_responds_to_l
assert_respond_to @controller, :l
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 24d071df39..b1a5356ddd 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -14,17 +14,14 @@ ENV['TMPDIR'] = File.join(File.dirname(__FILE__), 'tmp')
require 'active_support/core_ext/kernel/reporting'
-require 'active_support/core_ext/string/encoding'
-if "ruby".encoding_aware?
- # These are the normal settings that will be set up by Railties
- # TODO: Have these tests support other combinations of these values
- silence_warnings do
- Encoding.default_internal = "UTF-8"
- Encoding.default_external = "UTF-8"
- end
+# These are the normal settings that will be set up by Railties
+# TODO: Have these tests support other combinations of these values
+silence_warnings do
+ Encoding.default_internal = "UTF-8"
+ Encoding.default_external = "UTF-8"
end
-require 'test/unit'
+require 'minitest/autorun'
require 'abstract_controller'
require 'action_controller'
require 'action_view'
@@ -73,7 +70,17 @@ module RackTestUtils
end
module RenderERBUtils
+ def view
+ @view ||= begin
+ path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
+ view_paths = ActionView::PathSet.new([path])
+ ActionView::Base.new(view_paths)
+ end
+ end
+
def render_erb(string)
+ @virtual_path = nil
+
template = ActionView::Template.new(
string.strip,
"test template",
@@ -164,7 +171,8 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
def self.build_app(routes = nil)
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
- middleware.use "ActionDispatch::ShowExceptions"
+ middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
+ middleware.use "ActionDispatch::DebugExceptions"
middleware.use "ActionDispatch::Callbacks"
middleware.use "ActionDispatch::ParamsParser"
middleware.use "ActionDispatch::Cookies"
@@ -246,7 +254,7 @@ class Rack::TestCase < ActionDispatch::IntegrationTest
end
def assert_body(body)
- assert_equal body, Array.wrap(response.body).join
+ assert_equal body, Array(response.body).join
end
def assert_status(code)
@@ -254,7 +262,7 @@ class Rack::TestCase < ActionDispatch::IntegrationTest
end
def assert_response(body, status = 200, headers = {})
- assert_body body
+ assert_body body
assert_status status
headers.each do |header, value|
assert_header header, value
@@ -326,18 +334,33 @@ class Workshop
end
module ActionDispatch
- class ShowExceptions
+ class DebugExceptions
private
- remove_method :public_path
- def public_path
- "#{FIXTURE_LOAD_PATH}/public"
- end
+ remove_method :stderr_logger
+ # Silence logger
+ def stderr_logger
+ nil
+ end
+ end
+end
- remove_method :logger
- # Silence logger
- def logger
- nil
- end
+module ActionDispatch
+ module RoutingVerbs
+ def get(uri_or_host, path = nil, port = nil)
+ host = uri_or_host.host unless path
+ path ||= uri_or_host.path
+
+ params = {'PATH_INFO' => path,
+ 'REQUEST_METHOD' => 'GET',
+ 'HTTP_HOST' => host}
+
+ routes.call(params)[2].join
+ end
end
end
+module RoutingTestHelpers
+ def url_for(set, options, recall = nil)
+ set.send(:url_for, options.merge(:only_path => true, :_path_segments => recall))
+ end
+end
diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb
index 768ac713ca..2fe7959f5a 100644
--- a/actionpack/test/activerecord/active_record_store_test.rb
+++ b/actionpack/test/activerecord/active_record_store_test.rb
@@ -33,8 +33,6 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
session[:foo] = "baz"
head :ok
end
-
- def rescue_action(e) raise end
end
def setup
@@ -225,16 +223,16 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
assert_equal session_id, cookies['_session_id']
end
end
-
+
def test_incoming_invalid_session_id_via_cookie_should_be_ignored
with_test_route_set do
open_session do |sess|
sess.cookies['_session_id'] = 'INVALID'
-
+
sess.get '/set_session_value'
new_session_id = sess.cookies['_session_id']
assert_not_equal 'INVALID', new_session_id
-
+
sess.get '/get_session_value'
new_session_id_2 = sess.cookies['_session_id']
assert_equal new_session_id, new_session_id_2
@@ -248,7 +246,7 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
sess.get '/set_session_value', :_session_id => 'INVALID'
new_session_id = sess.cookies['_session_id']
assert_not_equal 'INVALID', new_session_id
-
+
sess.get '/get_session_value'
new_session_id_2 = sess.cookies['_session_id']
assert_equal new_session_id, new_session_id_2
diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb
index 20d11377f6..90e7f4ae59 100644
--- a/actionpack/test/activerecord/polymorphic_routes_test.rb
+++ b/actionpack/test/activerecord/polymorphic_routes_test.rb
@@ -2,42 +2,40 @@ require 'active_record_unit'
require 'fixtures/project'
class Task < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
class Step < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
class Bid < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
class Tax < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
class Fax < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
class Series < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
module Blog
class Post < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
class Blog < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
end
- def self._railtie
- o = Object.new
- def o.railtie_name; "blog" end
- o
+ def self.use_relative_model_naming?
+ true
end
end
diff --git a/actionpack/test/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb
new file mode 100644
index 0000000000..ca1d58765d
--- /dev/null
+++ b/actionpack/test/assertions/response_assertions_test.rb
@@ -0,0 +1,55 @@
+require 'abstract_unit'
+require 'action_dispatch/testing/assertions/response'
+
+module ActionDispatch
+ module Assertions
+ class ResponseAssertionsTest < ActiveSupport::TestCase
+ include ResponseAssertions
+
+ FakeResponse = Struct.new(:response_code) do
+ [:success, :missing, :redirect, :error].each do |sym|
+ define_method("#{sym}?") do
+ sym == response_code
+ end
+ end
+ end
+
+ def test_assert_response_predicate_methods
+ [:success, :missing, :redirect, :error].each do |sym|
+ @response = FakeResponse.new sym
+ assert_response sym
+
+ assert_raises(MiniTest::Assertion) {
+ assert_response :unauthorized
+ }
+ end
+ end
+
+ def test_assert_response_fixnum
+ @response = FakeResponse.new 400
+ assert_response 400
+
+ assert_raises(MiniTest::Assertion) {
+ assert_response :unauthorized
+ }
+
+ assert_raises(MiniTest::Assertion) {
+ assert_response 500
+ }
+ end
+
+ def test_assert_response_sym_status
+ @response = FakeResponse.new 401
+ assert_response :unauthorized
+
+ assert_raises(MiniTest::Assertion) {
+ assert_response :ok
+ }
+
+ assert_raises(MiniTest::Assertion) {
+ assert_response :success
+ }
+ end
+ end
+ end
+end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index a714e8bbcc..01cafe1aca 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -71,6 +71,11 @@ class ActionPackAssertionsController < ActionController::Base
render :text => "Hello!", :content_type => Mime::RSS
end
+ def render_with_layout
+ @variable_for_layout = nil
+ render "test/hello_world", :layout => "layouts/standard"
+ end
+
def session_stuffing
session['xmas'] = 'turkey'
render :text => "ho ho ho"
@@ -154,20 +159,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
assert_equal @response.body, 'request method: GET'
end
- def test_redirect_to_named_route
- with_routing do |set|
- set.draw do
- match 'route_one', :to => 'action_pack_assertions#nothing', :as => :route_one
- match ':controller/:action'
- end
- set.install_helpers
-
- process :redirect_to_named_route
- assert_redirected_to 'http://test.host/route_one'
- assert_redirected_to route_one_url
- end
- end
-
def test_string_constraint
with_routing do |set|
set.draw do
@@ -347,7 +338,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
def test_render_based_on_parameters
- process :render_based_on_parameters, "name" => "David"
+ process :render_based_on_parameters, "GET", "name" => "David"
assert_equal "Mr. David", @response.body
end
@@ -471,6 +462,18 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_wrong_layout
+ get :render_with_layout
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template :layout => "application"
+ end
+ end
+
+ def test_passes_with_correct_layout
+ get :render_with_layout
+ assert_template :layout => "layouts/standard"
+ end
+
def test_assert_template_reset_between_requests
get :hello_world
assert_template 'test/hello_world'
diff --git a/actionpack/test/controller/addresses_render_test.rb b/actionpack/test/controller/addresses_render_test.rb
deleted file mode 100644
index c1cd22113d..0000000000
--- a/actionpack/test/controller/addresses_render_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'abstract_unit'
-require 'logger'
-require 'controller/fake_controllers'
-
-class Address
- def Address.count(conditions = nil, join = nil)
- nil
- end
-
- def Address.find_all(arg1, arg2, arg3, arg4)
- []
- end
-
- def self.find(*args)
- []
- end
-end
-
-class AddressesTest < ActionController::TestCase
- tests AddressesController
-
- def setup
- super
- # 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".
- @controller.logger = Logger.new(nil)
-
- @request.host = "www.nextangle.com"
- end
-
- def test_list
- get :list
- assert_equal "We only need to get this far!", @response.body.chomp
- end
-end
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 5eef8a32d7..d3359e79a6 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -47,10 +47,6 @@ class AssertSelectTest < ActionController::TestCase
render :text=>@content, :layout=>false, :content_type=>Mime::XML
@content = nil
end
-
- def rescue_action(e)
- raise e
- end
end
tests AssertSelectController
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 9c22a4e7e0..791edb9069 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -1,5 +1,5 @@
require 'abstract_unit'
-require 'logger'
+require 'active_support/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.
@@ -40,29 +40,6 @@ class NonEmptyController < ActionController::Base
end
end
-class MethodMissingController < ActionController::Base
- hide_action :shouldnt_be_called
- def shouldnt_be_called
- raise "NO WAY!"
- end
-
-protected
-
- def method_missing(selector)
- render :text => selector.to_s
- end
-end
-
-class AnotherMethodMissingController < ActionController::Base
- cattr_accessor :_exception
- rescue_from Exception, :with => :_exception=
-
- protected
- def method_missing(*attrs, &block)
- super
- end
-end
-
class DefaultUrlOptionsController < ActionController::Base
def from_view
render :inline => "<%= #{params[:route]} %>"
@@ -106,7 +83,7 @@ class ControllerClassTests < ActiveSupport::TestCase
end
end
-class ControllerInstanceTests < Test::Unit::TestCase
+class ControllerInstanceTests < ActiveSupport::TestCase
def setup
@empty = EmptyController.new
@contained = Submodule::ContainedEmptyController.new
@@ -116,6 +93,12 @@ class ControllerInstanceTests < Test::Unit::TestCase
Submodule::ContainedNonEmptyController.new]
end
+ def test_performed?
+ assert !@empty.performed?
+ @empty.response_body = ["sweet"]
+ assert @empty.performed?
+ end
+
def test_action_methods
@empty_controllers.each do |c|
assert_equal Set.new, c.class.action_methods, "#{c.controller_path} should be empty!"
@@ -142,7 +125,7 @@ class PerformActionTest < ActionController::TestCase
# 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".
- @controller.logger = Logger.new(nil)
+ @controller.logger = ActiveSupport::Logger.new(nil)
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@@ -159,32 +142,10 @@ class PerformActionTest < ActionController::TestCase
assert_equal exception.message, "The action 'non_existent' could not be found for EmptyController"
end
- def test_get_on_priv_should_show_selector
- use_controller MethodMissingController
- get :shouldnt_be_called
- assert_response :success
- assert_equal 'shouldnt_be_called', @response.body
- end
-
- def test_method_missing_is_not_an_action_name
- use_controller MethodMissingController
- assert !@controller.__send__(:action_method?, 'method_missing')
-
- get :method_missing
- assert_response :success
- assert_equal 'method_missing', @response.body
- end
-
- def test_method_missing_should_recieve_symbol
- use_controller AnotherMethodMissingController
- get :some_action
- assert_kind_of NameError, @controller._exception
- end
-
def test_get_on_hidden_should_fail
use_controller NonEmptyController
- assert_raise(ActionController::UnknownAction) { get :hidden_action }
- assert_raise(ActionController::UnknownAction) { get :another_hidden_action }
+ assert_raise(AbstractController::ActionNotFound) { get :hidden_action }
+ assert_raise(AbstractController::ActionNotFound) { get :another_hidden_action }
end
end
@@ -197,6 +158,22 @@ class UrlOptionsTest < ActionController::TestCase
rescue_action_in_public!
end
+ def test_url_for_query_params_included
+ rs = ActionDispatch::Routing::RouteSet.new
+ rs.draw do
+ match 'home' => 'pages#home'
+ end
+
+ options = {
+ :action => "home",
+ :controller => "pages",
+ :only_path => true,
+ :params => { "token" => "secret" }
+ }
+
+ assert_equal '/home?token=secret', rs.url_for(options)
+ end
+
def test_url_options_override
with_routing do |set|
set.draw do
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 618e7b77b2..10f73c3da3 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -14,10 +14,14 @@ class CachingController < ActionController::Base
end
class PageCachingTestController < CachingController
+ self.page_cache_compression = :best_compression
+
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
caches_page :found, :not_found
caches_page :about_me
-
+ caches_page :default_gzip
+ caches_page :no_gzip, :gzip => false
+ caches_page :gzip_level, :gzip => :best_speed
def ok
head :ok
@@ -40,6 +44,18 @@ class PageCachingTestController < CachingController
cache_page("Super soaker", "/index.html")
end
+ def default_gzip
+ render :text => "Text"
+ end
+
+ def no_gzip
+ render :text => "PNG"
+ end
+
+ def gzip_level
+ render :text => "Big text"
+ end
+
def expire_custom_path
expire_page("/index.html")
head :ok
@@ -115,6 +131,30 @@ class PageCachingTest < ActionController::TestCase
assert !File.exist?("#{FILE_STORE_PATH}/index.html")
end
+ def test_should_gzip_cache
+ get :custom_path
+ assert File.exist?("#{FILE_STORE_PATH}/index.html.gz")
+
+ get :expire_custom_path
+ assert !File.exist?("#{FILE_STORE_PATH}/index.html.gz")
+ end
+
+ def test_should_allow_to_disable_gzip
+ get :no_gzip
+ assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/no_gzip.html")
+ assert !File.exist?("#{FILE_STORE_PATH}/page_caching_test/no_gzip.html.gz")
+ end
+
+ def test_should_use_config_gzip_by_default
+ @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_COMPRESSION)
+ get :default_gzip
+ end
+
+ def test_should_set_gzip_level
+ @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_SPEED)
+ get :gzip_level
+ end
+
def test_should_cache_without_trailing_slash_on_url
@controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
@@ -194,8 +234,10 @@ class ActionCachingTestController < CachingController
caches_action :show, :cache_path => 'http://test.host/custom/show'
caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
caches_action :with_layout
+ caches_action :with_format_and_http_param, :cache_path => Proc.new { |c| { :key => 'value' } }
caches_action :layout_false, :layout => false
caches_action :record_not_found, :four_oh_four, :simple_runtime_error
+ caches_action :streaming
layout 'talk_from_action'
@@ -219,6 +261,11 @@ class ActionCachingTestController < CachingController
render :text => @cache_this, :layout => true
end
+ def with_format_and_http_param
+ @cache_this = MockTime.now.to_f.to_s
+ render :text => @cache_this
+ end
+
def record_not_found
raise ActiveRecord::RecordNotFound, "oops!"
end
@@ -245,6 +292,15 @@ class ActionCachingTestController < CachingController
expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'
render :nothing => true
end
+
+ def expire_with_url_string
+ expire_action url_for(:controller => 'action_caching_test', :action => 'index')
+ render :nothing => true
+ end
+
+ def streaming
+ render :text => "streaming", :stream => true
+ end
end
class MockTime < Time
@@ -359,6 +415,13 @@ class ActionCacheTest < ActionController::TestCase
assert !fragment_exist?('hostname.com/action_caching_test')
end
+ def test_action_cache_with_format_and_http_param
+ get :with_format_and_http_param, :format => 'json'
+ assert_response :success
+ assert !fragment_exist?('hostname.com/action_caching_test/with_format_and_http_param.json?key=value.json')
+ assert fragment_exist?('hostname.com/action_caching_test/with_format_and_http_param.json?key=value')
+ end
+
def test_action_cache_with_store_options
MockTime.expects(:now).returns(12345).once
@controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once
@@ -432,6 +495,21 @@ class ActionCacheTest < ActionController::TestCase
assert_not_equal cached_time, @response.body
end
+ def test_cache_expiration_with_url_string
+ get :index
+ cached_time = content_to_cache
+ reset!
+
+ @request.request_uri = "/action_caching_test/expire_with_url_string"
+ get :expire_with_url_string
+ assert_response :success
+ reset!
+
+ get :index
+ assert_response :success
+ assert_not_equal cached_time, @response.body
+ end
+
def test_cache_is_scoped_by_subdomain
@request.host = 'jamis.hostname.com'
get :index
@@ -574,6 +652,13 @@ class ActionCacheTest < ActionController::TestCase
assert_response 500
end
+ def test_action_caching_plus_streaming
+ get :streaming
+ assert_response :success
+ assert_match(/streaming/, @response.body)
+ assert fragment_exist?('hostname.com/action_caching_test/streaming')
+ end
+
private
def content_to_cache
assigns(:cache_this)
@@ -613,8 +698,6 @@ class FragmentCachingTest < ActionController::TestCase
@controller.params = @params
@controller.request = @request
@controller.response = @response
- @controller.send(:initialize_template_class, @response)
- @controller.send(:assign_shortcuts, @request, @response)
end
def test_fragment_cache_key
@@ -722,10 +805,6 @@ class FunctionalCachingController < CachingController
format.xml
end
end
-
- def rescue_action(e)
- raise e
- end
end
class FunctionalFragmentCachingTest < ActionController::TestCase
diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb
index d78acb8ce8..72263156d9 100644
--- a/actionpack/test/controller/capture_test.rb
+++ b/actionpack/test/controller/capture_test.rb
@@ -1,5 +1,5 @@
require 'abstract_unit'
-require 'logger'
+require 'active_support/logger'
class CaptureController < ActionController::Base
def self.controller_name; "test"; end
@@ -28,8 +28,6 @@ class CaptureController < ActionController::Base
def proper_block_detection
@todo = "some todo"
end
-
- def rescue_action(e) raise end
end
class CaptureTest < ActionController::TestCase
@@ -39,7 +37,7 @@ class CaptureTest < ActionController::TestCase
super
# 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".
- @controller.logger = Logger.new(nil)
+ @controller.logger = ActiveSupport::Logger.new(nil)
@request.host = "www.nextangle.com"
end
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index d51882066d..03d5d27cf4 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -48,8 +48,6 @@ class OldContentTypeController < ActionController::Base
format.rss { render :text => "hello world!", :content_type => Mime::XML }
end
end
-
- def rescue_action(e) raise end
end
class ContentTypeTest < ActionController::TestCase
@@ -59,7 +57,7 @@ class ContentTypeTest < ActionController::TestCase
super
# 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".
- @controller.logger = Logger.new(nil)
+ @controller.logger = ActiveSupport::Logger.new(nil)
end
# :ported:
@@ -70,12 +68,12 @@ class ContentTypeTest < ActionController::TestCase
end
def test_render_changed_charset_default
- OldContentTypeController.default_charset = "utf-16"
+ ActionDispatch::Response.default_charset = "utf-16"
get :render_defaults
assert_equal "utf-16", @response.charset
assert_equal Mime::HTML, @response.content_type
ensure
- OldContentTypeController.default_charset = "utf-8"
+ ActionDispatch::Response.default_charset = "utf-8"
end
# :ported:
@@ -107,12 +105,12 @@ class ContentTypeTest < ActionController::TestCase
end
def test_nil_default_for_erb
- OldContentTypeController.default_charset = nil
+ ActionDispatch::Response.default_charset = nil
get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_nil @response.charset, @response.headers.inspect
ensure
- OldContentTypeController.default_charset = "utf-8"
+ ActionDispatch::Response.default_charset = "utf-8"
end
def test_default_for_erb
diff --git a/actionpack/test/controller/default_url_options_with_filter_test.rb b/actionpack/test/controller/default_url_options_with_filter_test.rb
index 3bbb981040..ef028e8cdb 100644
--- a/actionpack/test/controller/default_url_options_with_filter_test.rb
+++ b/actionpack/test/controller/default_url_options_with_filter_test.rb
@@ -21,7 +21,7 @@ end
class ControllerWithBeforeFilterAndDefaultUrlOptionsTest < ActionController::TestCase
- # This test has it´s roots in issue #1872
+ # This test has its roots in issue #1872
test "should redirect with correct locale :de" do
get :redirect, :locale => "de"
assert_redirected_to "/controller_with_before_filter_and_default_url_options/target?locale=de"
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index d5e3da4d88..046396b37c 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -16,7 +16,7 @@ class ActionController::Base
def assigns(key = nil)
assigns = {}
- instance_variable_names.each do |ivar|
+ instance_variables.each do |ivar|
next if ActionController::Base.protected_instance_variables.include?(ivar)
assigns[ivar[1..-1]] = instance_variable_get(ivar)
end
@@ -165,8 +165,6 @@ class FilterTest < ActionController::TestCase
@ran_filter ||= []
@ran_filter << "clean_up_tmp"
end
-
- def rescue_action(e) raise(e) end
end
class ConditionalCollectionFilterController < ConditionalFilterController
@@ -454,11 +452,6 @@ class FilterTest < ActionController::TestCase
def show
raise ErrorToRescue.new("Something made the bad noise.")
end
-
- private
- def rescue_action(exception)
- raise exception
- end
end
class NonYieldingAroundFilterController < ActionController::Base
@@ -472,9 +465,6 @@ class FilterTest < ActionController::TestCase
render :inline => "index"
end
- #make sure the controller complains
- def rescue_action(e); raise e; end
-
private
def filter_one
@@ -825,11 +815,7 @@ class FilterTest < ActionController::TestCase
end
end
-
-
class PostsController < ActionController::Base
- def rescue_action(e); raise e; end
-
module AroundExceptions
class Error < StandardError ; end
class Before < Error ; end
diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb
index 9b69a2648e..ccca0dac17 100644
--- a/actionpack/test/controller/flash_hash_test.rb
+++ b/actionpack/test/controller/flash_hash_test.rb
@@ -75,6 +75,7 @@ module ActionDispatch
def test_discard_no_args
@hash['hello'] = 'world'
@hash.discard
+
@hash.sweep
assert_equal({}, @hash.to_hash)
end
@@ -83,8 +84,88 @@ module ActionDispatch
@hash['hello'] = 'world'
@hash['omg'] = 'world'
@hash.discard 'hello'
+
@hash.sweep
assert_equal({'omg' => 'world'}, @hash.to_hash)
end
+
+ def test_keep_sweep
+ @hash['hello'] = 'world'
+
+ @hash.sweep
+ assert_equal({'hello' => 'world'}, @hash.to_hash)
+ end
+
+ def test_update_sweep
+ @hash['hello'] = 'world'
+ @hash.update({'hi' => 'mom'})
+
+ @hash.sweep
+ assert_equal({'hello' => 'world', 'hi' => 'mom'}, @hash.to_hash)
+ end
+
+ def test_update_delete_sweep
+ @hash['hello'] = 'world'
+ @hash.delete 'hello'
+ @hash.update({'hello' => 'mom'})
+
+ @hash.sweep
+ assert_equal({'hello' => 'mom'}, @hash.to_hash)
+ end
+
+ def test_delete_sweep
+ @hash['hello'] = 'world'
+ @hash['hi'] = 'mom'
+ @hash.delete 'hi'
+
+ @hash.sweep
+ assert_equal({'hello' => 'world'}, @hash.to_hash)
+ end
+
+ def test_clear_sweep
+ @hash['hello'] = 'world'
+ @hash.clear
+
+ @hash.sweep
+ assert_equal({}, @hash.to_hash)
+ end
+
+ def test_replace_sweep
+ @hash['hello'] = 'world'
+ @hash.replace({'hi' => 'mom'})
+
+ @hash.sweep
+ assert_equal({'hi' => 'mom'}, @hash.to_hash)
+ end
+
+ def test_discard_then_add
+ @hash['hello'] = 'world'
+ @hash['omg'] = 'world'
+ @hash.discard 'hello'
+ @hash['hello'] = 'world'
+
+ @hash.sweep
+ assert_equal({'omg' => 'world', 'hello' => 'world'}, @hash.to_hash)
+ end
+
+ def test_keep_all_sweep
+ @hash['hello'] = 'world'
+ @hash['omg'] = 'world'
+ @hash.discard 'hello'
+ @hash.keep
+
+ @hash.sweep
+ assert_equal({'omg' => 'world', 'hello' => 'world'}, @hash.to_hash)
+ end
+
+ def test_double_sweep
+ @hash['hello'] = 'world'
+ @hash.sweep
+
+ assert_equal({'hello' => 'world'}, @hash.to_hash)
+
+ @hash.sweep
+ assert_equal({}, @hash.to_hash)
+ end
end
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index e19612eace..37ccc7a4a5 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -51,10 +51,6 @@ class FlashTest < ActionController::TestCase
render :inline => "hello"
end
- def rescue_action(e)
- raise unless ActionView::MissingTemplate === e
- end
-
# methods for test_sweep_after_halted_filter_chain
before_filter :halt_and_redir, :only => "filter_halting_action"
@@ -254,16 +250,6 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
end
end
- def test_setting_flash_raises_after_stream_back_to_client
- with_test_route_set do
- env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
- get '/set_flash', nil, env
- assert_raise(ActionDispatch::ClosedError) {
- @request.flash['alert'] = 'alert'
- }
- end
- end
-
def test_setting_flash_does_not_raise_in_following_requests
with_test_route_set do
env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
@@ -280,36 +266,6 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
end
end
- def test_setting_flash_raises_after_stream_back_to_client_even_with_an_empty_flash
- with_test_route_set do
- env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
- get '/dont_set_flash', nil, env
- assert_raise(ActionDispatch::ClosedError) {
- @request.flash['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_flash_now_raises_after_stream_back_to_client
- with_test_route_set do
- env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
- get '/set_flash_now', nil, env
- assert_raise(ActionDispatch::ClosedError) {
- @request.flash.now['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_flash_now_raises_after_stream_back_to_client_even_with_an_empty_flash
- with_test_route_set do
- env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
- get '/dont_set_flash', nil, env
- assert_raise(ActionDispatch::ClosedError) {
- @request.flash.now['alert'] = 'alert'
- }
- end
- end
-
private
# Overwrite get to send SessionSecret in env hash
diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb
index 43b20fdead..b681a19fe0 100644
--- a/actionpack/test/controller/force_ssl_test.rb
+++ b/actionpack/test/controller/force_ssl_test.rb
@@ -26,6 +26,21 @@ class ForceSSLExceptAction < ForceSSLController
force_ssl :except => :banana
end
+class ForceSSLFlash < ForceSSLController
+ force_ssl :except => [:banana, :set_flash, :use_flash]
+
+ def set_flash
+ flash["that"] = "hello"
+ redirect_to '/force_ssl_flash/cheeseburger'
+ end
+
+ def use_flash
+ @flash_copy = {}.update flash
+ @flashy = flash["that"]
+ render :inline => "hello"
+ end
+end
+
class ForceSSLControllerLevelTest < ActionController::TestCase
tests ForceSSLControllerLevel
@@ -35,6 +50,12 @@ class ForceSSLControllerLevelTest < ActionController::TestCase
assert_equal "https://test.host/force_ssl_controller_level/banana", redirect_to_url
end
+ def test_banana_redirects_to_https_with_extra_params
+ get :banana, :token => "secret"
+ assert_response 301
+ assert_equal "https://test.host/force_ssl_controller_level/banana?token=secret", redirect_to_url
+ end
+
def test_cheeseburger_redirects_to_https
get :cheeseburger
assert_response 301
@@ -50,7 +71,7 @@ class ForceSSLCustomDomainTest < ActionController::TestCase
assert_response 301
assert_equal "https://secure.test.host/force_ssl_custom_domain/banana", redirect_to_url
end
-
+
def test_cheeseburger_redirects_to_https_with_custom_host
get :cheeseburger
assert_response 301
@@ -101,3 +122,21 @@ class ForceSSLExcludeDevelopmentTest < ActionController::TestCase
assert_response 200
end
end
+
+class ForceSSLFlashTest < ActionController::TestCase
+ tests ForceSSLFlash
+
+ def test_cheeseburger_redirects_to_https
+ get :set_flash
+ assert_response 302
+ assert_equal "http://test.host/force_ssl_flash/cheeseburger", redirect_to_url
+
+ get :cheeseburger
+ assert_response 301
+ assert_equal "https://test.host/force_ssl_flash/cheeseburger", redirect_to_url
+
+ get :use_flash
+ assert_equal "hello", assigns["flash_copy"]["that"]
+ assert_equal "hello", assigns["flashy"]
+ end
+end
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index 35a87c1aae..757661d8d0 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -7,16 +7,12 @@ module Fun
def render_hello_world
render :inline => "hello: <%= stratego %>"
end
-
- def rescue_action(e) raise end
end
class PdfController < ActionController::Base
def test
render :inline => "test: <%= foobar %>"
end
-
- def rescue_action(e) raise end
end
end
@@ -60,7 +56,6 @@ class HelperTest < ActiveSupport::TestCase
class TestController < ActionController::Base
attr_accessor :delegate_attr
def delegate_method() end
- def rescue_action(e) raise end
end
def setup
@@ -201,8 +196,6 @@ class IsolatedHelpersTest < ActiveSupport::TestCase
def index
render :inline => '<%= shout %>'
end
-
- def rescue_action(e) raise end
end
class B < A
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index 364e96d4f6..7286b249c7 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -132,6 +132,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
private
def encode_credentials(username, password)
- "Basic #{ActiveSupport::Base64.encode64("#{username}:#{password}")}"
+ "Basic #{::Base64.encode64("#{username}:#{password}")}"
end
end
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index b011536717..a91e3cafa5 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -139,7 +139,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "authentication request with request-uri that doesn't match credentials digest-uri" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
- @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest/altered/uri"
+ @request.env['ORIGINAL_FULLPATH'] = "/http_digest_authentication_test/dummy_digest/altered/uri"
get :display
assert_response :unauthorized
@@ -208,6 +208,44 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret"){nil}
end
+ test "authentication request with request-uri ending in '/'" do
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest/"
+ @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
+
+ # simulate normalizing PATH_INFO
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest"
+ get :display
+
+ assert_response :success
+ assert_equal 'Definitely Maybe', @response.body
+ end
+
+ test "authentication request with request-uri ending in '?'" do
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest/?"
+ @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
+
+ # simulate normalizing PATH_INFO
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest"
+ get :display
+
+ assert_response :success
+ assert_equal 'Definitely Maybe', @response.body
+ end
+
+ test "authentication request with absolute uri in credentials (as in IE) ending with /" do
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest/"
+ @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:uri => "http://test.host/http_digest_authentication_test/dummy_digest/",
+ :username => 'pretty', :password => 'please')
+
+ # simulate normalizing PATH_INFO
+ @request.env['PATH_INFO'] = "/http_digest_authentication_test/dummy_digest"
+ get :display
+
+ assert_response :success
+ assert assigns(:logged_in)
+ assert_equal 'Definitely Maybe', @response.body
+ end
+
private
def encode_credentials(options)
@@ -228,7 +266,10 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
credentials = decode_credentials(@response.headers['WWW-Authenticate'])
credentials.merge!(options)
- credentials.merge!(:uri => @request.env['PATH_INFO'].to_s)
+ path_info = @request.env['PATH_INFO'].to_s
+ uri = options[:uri] || path_info
+ credentials.merge!(:uri => uri)
+ @request.env["ORIGINAL_FULLPATH"] = path_info
ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 2ad95f5c29..99e1dc7966 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'controller/fake_controllers'
require 'action_controller/vendor/html-scanner'
-class SessionTest < Test::Unit::TestCase
+class SessionTest < ActiveSupport::TestCase
StubApp = lambda { |env|
[200, {"Content-Type" => "text/html", "Content-Length" => "13"}, ["Hello, World!"]]
}
@@ -165,7 +165,7 @@ class SessionTest < Test::Unit::TestCase
end
end
-class IntegrationTestTest < Test::Unit::TestCase
+class IntegrationTestTest < ActiveSupport::TestCase
def setup
@test = ::ActionDispatch::IntegrationTest.new(:app)
@test.class.stubs(:fixture_table_names).returns([])
@@ -535,3 +535,36 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest
assert_equal old_env, env
end
end
+
+class EnvironmentFilterIntegrationTest < ActionDispatch::IntegrationTest
+ class TestController < ActionController::Base
+ def post
+ render :text => "Created", :status => 201
+ end
+ end
+
+ def self.call(env)
+ env["action_dispatch.parameter_filter"] = [:password]
+ routes.call(env)
+ end
+
+ def self.routes
+ @routes ||= ActionDispatch::Routing::RouteSet.new
+ end
+
+ routes.draw do
+ match '/post', :to => 'environment_filter_integration_test/test#post', :via => :post
+ end
+
+ def app
+ self.class
+ end
+
+ test "filters rack request form vars" do
+ post "/post", :username => 'cjolly', :password => 'secret'
+
+ assert_equal 'cjolly', request.filtered_parameters['username']
+ assert_equal '[FILTERED]', request.filtered_parameters['password']
+ assert_equal '[FILTERED]', request.filtered_env['rack.request.form_vars']
+ end
+end
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 25299eb8b8..bc171e201b 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -167,7 +167,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_is_picked_from_the_controller_instances_view_path
@controller = PrependsViewPathController.new
get :hello
- assert_template :layout => /layouts\/alt\.\w+/
+ assert_template :layout => /layouts\/alt/
end
def test_absolute_pathed_layout
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index ccdfcb0b2c..700fd788fa 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -13,6 +13,11 @@ module Another
head :status => 406
end
+ before_filter :redirector, :only => :never_executed
+
+ def never_executed
+ end
+
def show
render :nothing => true
end
@@ -49,7 +54,6 @@ module Another
def with_rescued_exception
raise SpecialException
end
-
end
end
@@ -86,6 +90,13 @@ class ACLogSubscriberTest < ActionController::TestCase
assert_equal "Processing by Another::LogSubscribersController#show as HTML", logs.first
end
+ def test_halted_callback
+ get :never_executed
+ wait
+ assert_equal 4, logs.size
+ assert_equal "Filter chain halted as :redirector rendered or redirected", logs.third
+ end
+
def test_process_action
get :show
wait
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 76a8c89e60..69a8f4f213 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -151,10 +151,6 @@ class RespondToController < ActionController::Base
end
end
- def rescue_action(e)
- raise
- end
-
protected
def set_layout
if action_name.in?(["all_types_with_layout", "iphone_with_html_response_type"])
@@ -498,7 +494,7 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
assert_equal "text/html", @response.content_type
end
-
+
def test_invalid_format
get :using_defaults, :format => "invalidformat"
assert_equal " ", @response.body
@@ -597,6 +593,19 @@ class RenderJsonRespondWithController < RespondWithController
format.json { render :json => RenderJsonTestException.new('boom') }
end
end
+
+ def create
+ resource = ValidatedCustomer.new(params[:name], 1)
+ respond_with(resource) do |format|
+ format.json do
+ if resource.errors.empty?
+ render :json => { :valid => true }
+ else
+ render :json => { :valid => false }
+ end
+ end
+ end
+ end
end
class EmptyRespondWithController < ActionController::Base
@@ -968,6 +977,18 @@ class RespondWithControllerTest < ActionController::TestCase
assert_match(/"error":"RenderJsonTestException"/, @response.body)
end
+ def test_api_response_with_valid_resource_respect_override_block
+ @controller = RenderJsonRespondWithController.new
+ post :create, :name => "sikachu", :format => :json
+ assert_equal '{"valid":true}', @response.body
+ end
+
+ def test_api_response_with_invalid_resource_respect_override_block
+ @controller = RenderJsonRespondWithController.new
+ post :create, :name => "david", :format => :json
+ assert_equal '{"valid":false}', @response.body
+ end
+
def test_no_double_render_is_raised
@request.accept = "text/html"
assert_raise ActionView::MissingTemplate do
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 3ca29f1bcf..c424dcbd8d 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -23,6 +23,12 @@ module BareMetalTest
assert_equal "Hello world", string
end
+
+ test "response_body value is wrapped in an array when the value is a String" do
+ controller = BareController.new
+ controller.index
+ assert_equal ["Hello world"], controller.response_body
+ end
end
class HeadController < ActionController::Metal
diff --git a/actionpack/test/controller/new_base/render_layout_test.rb b/actionpack/test/controller/new_base/render_layout_test.rb
index d3dcb5cad6..4ac40ca405 100644
--- a/actionpack/test/controller/new_base/render_layout_test.rb
+++ b/actionpack/test/controller/new_base/render_layout_test.rb
@@ -71,7 +71,8 @@ module ControllerLayouts
self.view_paths = [ActionView::FixtureResolver.new(
"layouts/application.html.erb" => "<html><%= yield %></html>",
"controller_layouts/mismatch_format/index.xml.builder" => "xml.instruct!",
- "controller_layouts/mismatch_format/implicit.builder" => "xml.instruct!"
+ "controller_layouts/mismatch_format/implicit.builder" => "xml.instruct!",
+ "controller_layouts/mismatch_format/explicit.js.erb" => "alert('foo');"
)]
def explicit
@@ -81,7 +82,7 @@ module ControllerLayouts
class MismatchFormatTest < Rack::TestCase
testing ControllerLayouts::MismatchFormatController
-
+
XML_INSTRUCT = %Q(<?xml version="1.0" encoding="UTF-8"?>\n)
test "if XML is selected, an HTML template is not also selected" do
@@ -94,10 +95,33 @@ module ControllerLayouts
assert_response XML_INSTRUCT
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
+ test "a layout for JS is ignored even if explicitly provided for HTML" do
+ get :explicit, { :format => "js" }
+ assert_response "alert('foo');"
+ end
+ end
+
+ class FalseLayoutMethodController < ::ApplicationController
+ self.view_paths = [ActionView::FixtureResolver.new(
+ "controller_layouts/false_layout_method/index.js.erb" => "alert('foo');"
+ )]
+
+ layout :which_layout?
+
+ def which_layout?
+ false
+ end
+
+ def index
+ end
+ end
+
+ class FalseLayoutMethodTest < Rack::TestCase
+ testing ControllerLayouts::FalseLayoutMethodController
+
+ test "access false layout returned by a method/proc" do
+ get :index, :format => "js"
+ assert_response "alert('foo');"
end
end
end
diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb
index 1a17e24914..61ea68e3f7 100644
--- a/actionpack/test/controller/new_base/render_streaming_test.rb
+++ b/actionpack/test/controller/new_base/render_streaming_test.rb
@@ -85,7 +85,7 @@ module RenderStreaming
test "rendering with template exception logs the exception" do
io = StringIO.new
- _old, ActionController::Base.logger = ActionController::Base.logger, Logger.new(io)
+ _old, ActionController::Base.logger = ActionController::Base.logger, ActiveSupport::Logger.new(io)
begin
get "/render_streaming/basic/template_exception"
@@ -111,4 +111,4 @@ module RenderStreaming
assert_equal cache, headers["Cache-Control"]
end
end
-end if defined?(Fiber)
+end
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index ba804421da..ade204c387 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -59,6 +59,12 @@ module RenderTemplate
def with_error
render :template => "test/with_error"
end
+
+ private
+
+ def show_detailed_exceptions?
+ request.local?
+ end
end
class TestWithoutLayout < Rack::TestCase
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index 7bef1e8d5d..c4d2614200 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -147,6 +147,7 @@ class ParamsWrapperTest < ActionController::TestCase
end
def test_derived_wrapped_keys_from_matching_model
+ User.expects(:respond_to?).with(:accessible_attributes).returns(false)
User.expects(:respond_to?).with(:attribute_names).returns(true)
User.expects(:attribute_names).twice.returns(["username"])
@@ -159,6 +160,7 @@ class ParamsWrapperTest < ActionController::TestCase
def test_derived_wrapped_keys_from_specified_model
with_default_wrapper_options do
+ Person.expects(:respond_to?).with(:accessible_attributes).returns(false)
Person.expects(:respond_to?).with(:attribute_names).returns(true)
Person.expects(:attribute_names).twice.returns(["username"])
@@ -169,8 +171,33 @@ class ParamsWrapperTest < ActionController::TestCase
assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }})
end
end
+
+ def test_accessible_wrapped_keys_from_matching_model
+ User.expects(:respond_to?).with(:accessible_attributes).returns(true)
+ User.expects(:accessible_attributes).twice.returns(["username"])
+
+ with_default_wrapper_options do
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :parse, { 'username' => 'sikachu', 'title' => 'Developer' }
+ assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu' }})
+ end
+ end
+
+ def test_accessible_wrapped_keys_from_specified_model
+ with_default_wrapper_options do
+ Person.expects(:respond_to?).with(:accessible_attributes).returns(true)
+ Person.expects(:accessible_attributes).twice.returns(["username"])
+
+ UsersController.wrap_parameters Person
+
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :parse, { 'username' => 'sikachu', 'title' => 'Developer' }
+ assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }})
+ end
+ end
def test_not_wrapping_abstract_model
+ User.expects(:respond_to?).with(:accessible_attributes).returns(false)
User.expects(:respond_to?).with(:attribute_names).returns(true)
User.expects(:attribute_names).returns([])
@@ -285,3 +312,38 @@ class AnonymousControllerParamsWrapperTest < ActionController::TestCase
end
end
end
+
+class IrregularInflectionParamsWrapperTest < ActionController::TestCase
+ include ParamsWrapperTestHelp
+
+ class ParamswrappernewsItem
+ def self.attribute_names
+ ['test_attr']
+ end
+ end
+
+ class ParamswrappernewsController < ActionController::Base
+ class << self
+ attr_accessor :last_parameters
+ end
+
+ def parse
+ self.class.last_parameters = request.params.except(:controller, :action)
+ head :ok
+ end
+ end
+
+ tests ParamswrappernewsController
+
+ def test_uses_model_attribute_names_with_irregular_inflection
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.irregular 'paramswrappernews_item', 'paramswrappernews'
+ end
+
+ with_default_wrapper_options do
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :parse, { 'username' => 'sikachu', 'test_attr' => 'test_value' }
+ assert_parameters({ 'username' => 'sikachu', 'test_attr' => 'test_value', 'paramswrappernews_item' => { 'test_attr' => 'test_value' }})
+ end
+ end
+end
diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb
index f3e5ff8a47..eb38b81e85 100644
--- a/actionpack/test/controller/record_identifier_test.rb
+++ b/actionpack/test/controller/record_identifier_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'controller/fake_models'
-class RecordIdentifierTest < Test::Unit::TestCase
+class RecordIdentifierTest < ActiveSupport::TestCase
include ActionController::RecordIdentifier
def setup
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 79041055bd..b1d76150f8 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -70,6 +70,10 @@ class RedirectController < ActionController::Base
redirect_to "x-test+scheme.complex:redirect"
end
+ def redirect_to_url_with_network_path_reference
+ redirect_to "//www.rubyonrails.org/"
+ end
+
def redirect_to_back
redirect_to :back
end
@@ -101,8 +105,6 @@ class RedirectController < ActionController::Base
def rescue_errors(e) raise e end
- def rescue_action(e) raise end
-
protected
def dashbord_url(id, message)
url_for :action => "dashboard", :params => { "id" => id, "message" => message }
@@ -216,6 +218,12 @@ class RedirectTest < ActionController::TestCase
assert_equal "x-test+scheme.complex:redirect", redirect_to_url
end
+ def test_redirect_to_url_with_network_path_reference
+ get :redirect_to_url_with_network_path_reference
+ assert_response :redirect
+ assert_equal "//www.rubyonrails.org/", redirect_to_url
+ end
+
def test_redirect_to_back
@request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
get :redirect_to_back
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
index fc604a2db3..75fed8e933 100644
--- a/actionpack/test/controller/render_json_test.rb
+++ b/actionpack/test/controller/render_json_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'controller/fake_models'
+require 'active_support/logger'
require 'pathname'
class RenderJsonTest < ActionController::TestCase
@@ -69,7 +70,7 @@ class RenderJsonTest < ActionController::TestCase
# 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)
+ @controller.logger = ActiveSupport::Logger.new(nil)
@request.host = "www.nextangle.com"
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 4ec72ece32..e040878b26 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -9,7 +9,7 @@ module Fun
end
def nested_partial_with_form_builder
- render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}, Proc.new {})
+ render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {})
end
end
end
@@ -25,6 +25,8 @@ end
class TestController < ActionController::Base
protect_from_forgery
+ before_filter :set_variable_for_layout
+
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
end
@@ -41,7 +43,7 @@ class TestController < ActionController::Base
end
def hello_world_file
- render :file => File.expand_path("../../fixtures/hello.html", __FILE__)
+ render :file => File.expand_path("../../fixtures/hello", __FILE__), :formats => [:html]
end
def conditional_hello
@@ -50,12 +52,28 @@ class TestController < ActionController::Base
end
end
+ def conditional_hello_with_record
+ record = Struct.new(:updated_at, :cache_key).new(Time.now.utc.beginning_of_day, "foo/123")
+
+ if stale?(record)
+ render :action => 'hello_world'
+ end
+ end
+
def conditional_hello_with_public_header
if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true)
render :action => 'hello_world'
end
end
+ def conditional_hello_with_public_header_with_record
+ record = Struct.new(:updated_at, :cache_key).new(Time.now.utc.beginning_of_day, "foo/123")
+
+ if stale?(record, :public => true)
+ render :action => 'hello_world'
+ end
+ end
+
def conditional_hello_with_public_header_and_expires_at
expires_in 1.minute
if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true)
@@ -73,6 +91,16 @@ class TestController < ActionController::Base
render :action => 'hello_world'
end
+ def conditional_hello_with_expires_in_with_must_revalidate
+ expires_in 1.minute, :must_revalidate => true
+ render :action => 'hello_world'
+ end
+
+ def conditional_hello_with_expires_in_with_public_and_must_revalidate
+ expires_in 1.minute, :public => true, :must_revalidate => true
+ render :action => 'hello_world'
+ end
+
def conditional_hello_with_expires_in_with_public_with_more_keys
expires_in 1.minute, :public => true, 'max-stale' => 5.hours
render :action => 'hello_world'
@@ -348,17 +376,14 @@ class TestController < ActionController::Base
end
def layout_test_with_different_layout
- @variable_for_layout = nil
render :action => "hello_world", :layout => "standard"
end
def layout_test_with_different_layout_and_string_action
- @variable_for_layout = nil
render "hello_world", :layout => "standard"
end
def layout_test_with_different_layout_and_symbol_action
- @variable_for_layout = nil
render :hello_world, :layout => "standard"
end
@@ -367,7 +392,6 @@ class TestController < ActionController::Base
end
def layout_overriding_layout
- @variable_for_layout = nil
render :action => "hello_world", :layout => "standard"
end
@@ -544,11 +568,11 @@ class TestController < ActionController::Base
end
def partial_with_form_builder
- render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}, Proc.new {})
+ render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {})
end
def partial_with_form_builder_subclass
- render :partial => LabellingFormBuilder.new(:post, nil, view_context, {}, Proc.new {})
+ render :partial => LabellingFormBuilder.new(:post, nil, view_context, {})
end
def partial_collection
@@ -635,10 +659,6 @@ class TestController < ActionController::Base
render :action => "calling_partial_with_layout", :layout => "layouts/partial_with_layout"
end
- def rescue_action(e)
- raise
- end
-
before_filter :only => :render_with_filters do
request.format = :xml
end
@@ -650,8 +670,11 @@ class TestController < ActionController::Base
private
+ def set_variable_for_layout
+ @variable_for_layout = nil
+ end
+
def determine_layout
- @variable_for_layout ||= nil
case action_name
when "hello_world", "layout_test", "rendering_without_layout",
"rendering_nothing_on_layout", "render_text_hello_world",
@@ -679,7 +702,8 @@ class RenderTest < ActionController::TestCase
# 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)
+ @controller.logger = ActiveSupport::Logger.new(nil)
+ ActionView::Base.logger = ActiveSupport::Logger.new(nil)
@request.host = "www.nextangle.com"
end
@@ -793,9 +817,7 @@ class RenderTest < ActionController::TestCase
end
def test_render_file
- assert_deprecated do
- get :hello_world_file
- end
+ get :hello_world_file
assert_equal "Hello world!", @response.body
end
@@ -876,12 +898,12 @@ class RenderTest < ActionController::TestCase
# :ported:
def test_attempt_to_access_object_method
- assert_raise(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
+ assert_raise(AbstractController::ActionNotFound, "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 }
+ assert_raise(AbstractController::ActionNotFound, "No action responded to [determine_layout]") { get :determine_layout }
end
# :ported:
@@ -892,7 +914,7 @@ class RenderTest < ActionController::TestCase
def test_access_to_logger_in_view
get :accessing_logger_in_template
- assert_equal "Logger", @response.body
+ assert_equal "ActiveSupport::Logger", @response.body
end
# :ported:
@@ -1081,15 +1103,15 @@ class RenderTest < ActionController::TestCase
# :ported:
def test_double_render
- assert_raise(ActionController::DoubleRenderError) { get :double_render }
+ assert_raise(AbstractController::DoubleRenderError) { get :double_render }
end
def test_double_redirect
- assert_raise(ActionController::DoubleRenderError) { get :double_redirect }
+ assert_raise(AbstractController::DoubleRenderError) { get :double_redirect }
end
def test_render_and_redirect
- assert_raise(ActionController::DoubleRenderError) { get :render_and_redirect }
+ assert_raise(AbstractController::DoubleRenderError) { get :render_and_redirect }
end
# specify the one exception to double render rule - render_to_string followed by render
@@ -1387,6 +1409,16 @@ class ExpiresInRenderTest < ActionController::TestCase
assert_equal "max-age=60, public", @response.headers["Cache-Control"]
end
+ def test_expires_in_header_with_must_revalidate
+ get :conditional_hello_with_expires_in_with_must_revalidate
+ assert_equal "max-age=60, private, must-revalidate", @response.headers["Cache-Control"]
+ end
+
+ def test_expires_in_header_with_public_and_must_revalidate
+ get :conditional_hello_with_expires_in_with_public_and_must_revalidate
+ assert_equal "max-age=60, public, must-revalidate", @response.headers["Cache-Control"]
+ end
+
def test_expires_in_header_with_additional_headers
get :conditional_hello_with_expires_in_with_public_with_more_keys
assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"]
@@ -1447,6 +1479,36 @@ class LastModifiedRenderTest < ActionController::TestCase
assert_equal @last_modified, @response.headers['Last-Modified']
end
+
+ def test_responds_with_last_modified_with_record
+ get :conditional_hello_with_record
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+ def test_request_not_modified_with_record
+ @request.if_modified_since = @last_modified
+ get :conditional_hello_with_record
+ assert_equal 304, @response.status.to_i
+ assert_blank @response.body
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+ def test_request_not_modified_but_etag_differs_with_record
+ @request.if_modified_since = @last_modified
+ @request.if_none_match = "234"
+ get :conditional_hello_with_record
+ assert_response :success
+ end
+
+ def test_request_modified_with_record
+ @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
+ get :conditional_hello_with_record
+ assert_equal 200, @response.status.to_i
+ assert_present @response.body
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+
def test_request_with_bang_gets_last_modified
get :conditional_hello_with_bangs
assert_equal @last_modified, @response.headers['Last-Modified']
diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb
index ec4dc848ff..8b4f2f5349 100644
--- a/actionpack/test/controller/render_xml_test.rb
+++ b/actionpack/test/controller/render_xml_test.rb
@@ -48,7 +48,7 @@ class RenderXmlTest < ActionController::TestCase
# 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)
+ @controller.logger = ActiveSupport::Logger.new(nil)
@request.host = "www.nextangle.com"
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index fd5a41a0bb..e6d3fa74f2 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -1,6 +1,5 @@
require 'abstract_unit'
require 'digest/sha1'
-require 'active_support/core_ext/string/strip'
require "active_support/log_subscriber/test_helper"
# common controller actions
@@ -36,8 +35,6 @@ module RequestForgeryProtectionActions
def form_for_without_protection
render :inline => "<%= form_for(:some_resource, :authenticity_token => false ) {} %>"
end
-
- def rescue_action(e) raise e end
end
# sample controllers
@@ -74,9 +71,7 @@ class CustomAuthenticityParamController < RequestForgeryProtectionController
end
end
-
# common test methods
-
module RequestForgeryProtectionTests
def setup
@token = "cf50faa3fe97702ca1ae"
@@ -248,10 +243,6 @@ class FreeCookieControllerTest < ActionController::TestCase
end
end
-
-
-
-
class CustomAuthenticityParamControllerTest < ActionController::TestCase
def setup
ActionController::Base.request_forgery_protection_token = :custom_token_name
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index c445285538..9c51db135b 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -137,11 +137,11 @@ class RescueController < ActionController::Base
end
def io_error_in_view
- raise ActionView::TemplateError.new(nil, {}, IOError.new('this is io error'))
+ raise ActionView::TemplateError.new(nil, IOError.new('this is io error'))
end
def zero_division_error_in_view
- raise ActionView::TemplateError.new(nil, {}, ZeroDivisionError.new('this is zero division error'))
+ raise ActionView::TemplateError.new(nil, ZeroDivisionError.new('this is zero division error'))
end
protected
@@ -338,23 +338,8 @@ class RescueTest < ActionDispatch::IntegrationTest
end
end
- test 'rescue routing exceptions' do
- raiser = proc { |env| raise ActionController::RoutingError, "Did not handle the request" }
- @app = ActionDispatch::Rescue.new(raiser) do
- rescue_from ActionController::RoutingError, lambda { |env| [200, {"Content-Type" => "text/html"}, ["Gotcha!"]] }
- end
-
- get '/b00m'
- assert_equal "Gotcha!", response.body
- end
-
- test 'unrescued exception' do
- raiser = proc { |env| raise ActionController::RoutingError, "Did not handle the request" }
- @app = ActionDispatch::Rescue.new(raiser)
- assert_raise(ActionController::RoutingError) { get '/b00m' }
- end
-
private
+
def with_test_routing
with_routing do |set|
set.draw do
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 6b8a8f6161..73d72fe4d6 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -6,7 +6,6 @@ require 'active_support/core_ext/object/inclusion'
class ResourcesController < ActionController::Base
def index() render :nothing => true end
alias_method :show, :index
- def rescue_action(e) raise e end
end
class ThreadsController < ResourcesController; end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 5bf68decca..ee9374cc91 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -6,19 +6,12 @@ require 'active_support/core_ext/object/with_options'
class MilestonesController < ActionController::Base
def index() head :ok end
alias_method :show, :index
- def rescue_action(e) raise e end
end
ROUTING = ActionDispatch::Routing
-module RoutingTestHelpers
- def url_for(set, options, recall = nil)
- set.send(:url_for, options.merge(:only_path => true, :_path_segments => recall))
- end
-end
-
# See RFC 3986, section 3.3 for allowed path characters.
-class UriReservedCharactersRoutingTest < Test::Unit::TestCase
+class UriReservedCharactersRoutingTest < ActiveSupport::TestCase
include RoutingTestHelpers
def setup
@@ -78,17 +71,172 @@ class MockController
end
end
-class LegacyRouteSetTests < Test::Unit::TestCase
+class LegacyRouteSetTests < ActiveSupport::TestCase
include RoutingTestHelpers
+ include ActionDispatch::RoutingVerbs
attr_reader :rs
+ alias :routes :rs
def setup
- @rs = ::ActionDispatch::Routing::RouteSet.new
+ @rs = ::ActionDispatch::Routing::RouteSet.new
+ @response = nil
end
- def teardown
- @rs.clear!
+ def test_symbols_with_dashes
+ rs.draw do
+ match '/:artist/:song-omg', :to => lambda { |env|
+ resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = JSON.load get(URI('http://example.org/journey/faithfully-omg'))
+ assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash)
+ end
+
+ def test_id_with_dash
+ rs.draw do
+ match '/journey/:id', :to => lambda { |env|
+ resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = JSON.load get(URI('http://example.org/journey/faithfully-omg'))
+ assert_equal({"id"=>"faithfully-omg"}, hash)
+ end
+
+ def test_dash_with_custom_regexp
+ rs.draw do
+ match '/:artist/:song-omg', :constraints => { :song => /\d+/ }, :to => lambda { |env|
+ resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = JSON.load get(URI('http://example.org/journey/123-omg'))
+ assert_equal({"artist"=>"journey", "song"=>"123"}, hash)
+ assert_equal 'Not Found', get(URI('http://example.org/journey/faithfully-omg'))
+ end
+
+ def test_pre_dash
+ rs.draw do
+ match '/:artist/omg-:song', :to => lambda { |env|
+ resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = JSON.load get(URI('http://example.org/journey/omg-faithfully'))
+ assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash)
+ end
+
+ def test_pre_dash_with_custom_regexp
+ rs.draw do
+ match '/:artist/omg-:song', :constraints => { :song => /\d+/ }, :to => lambda { |env|
+ resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = JSON.load get(URI('http://example.org/journey/omg-123'))
+ assert_equal({"artist"=>"journey", "song"=>"123"}, hash)
+ assert_equal 'Not Found', get(URI('http://example.org/journey/omg-faithfully'))
+ end
+
+ def test_star_paths_are_greedy
+ rs.draw do
+ match "/(*filters)", :to => lambda { |env|
+ x = env["action_dispatch.request.path_parameters"][:filters]
+ [200, {}, [x]]
+ }, :format => false
+ end
+
+ u = URI('http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794')
+ assert_equal u.path.sub(/^\//, ''), get(u)
+ end
+
+ def test_star_paths_are_greedy_but_not_too_much
+ rs.draw do
+ match "/(*filters).:format", :to => lambda { |env|
+ x = JSON.dump env["action_dispatch.request.path_parameters"]
+ [200, {}, [x]]
+ }
+ end
+
+ expected = { "filters" => "ne_27.065938,-80.6092/sw_25.489856,-82",
+ "format" => "542794" }
+ u = URI('http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794')
+ assert_equal expected, JSON.parse(get(u))
+ end
+
+ def test_regexp_precidence
+ @rs.draw do
+ match '/whois/:domain', :constraints => {
+ :domain => /\w+\.[\w\.]+/ },
+ :to => lambda { |env| [200, {}, %w{regexp}] }
+
+ match '/whois/:id', :to => lambda { |env| [200, {}, %w{id}] }
+ end
+
+ assert_equal 'regexp', get(URI('http://example.org/whois/example.org'))
+ assert_equal 'id', get(URI('http://example.org/whois/123'))
+ end
+
+ def test_class_and_lambda_constraints
+ subdomain = Class.new {
+ def matches? request
+ request.subdomain.present? and request.subdomain != 'clients'
+ end
+ }
+
+ @rs.draw do
+ match '/', :constraints => subdomain.new,
+ :to => lambda { |env| [200, {}, %w{default}] }
+ match '/', :constraints => { :subdomain => 'clients' },
+ :to => lambda { |env| [200, {}, %w{clients}] }
+ end
+
+ assert_equal 'default', get(URI('http://www.example.org/'))
+ assert_equal 'clients', get(URI('http://clients.example.org/'))
+ end
+
+ def test_lambda_constraints
+ @rs.draw do
+ match '/', :constraints => lambda { |req|
+ req.subdomain.present? and req.subdomain != "clients" },
+ :to => lambda { |env| [200, {}, %w{default}] }
+
+ match '/', :constraints => lambda { |req|
+ req.subdomain.present? && req.subdomain == "clients" },
+ :to => lambda { |env| [200, {}, %w{clients}] }
+ end
+
+ assert_equal 'default', get(URI('http://www.example.org/'))
+ assert_equal 'clients', get(URI('http://clients.example.org/'))
+ end
+
+ def test_empty_string_match
+ rs.draw do
+ get '/:username', :constraints => { :username => /[^\/]+/ },
+ :to => lambda { |e| [200, {}, ['foo']] }
+ end
+ assert_equal 'Not Found', get(URI('http://example.org/'))
+ assert_equal 'foo', get(URI('http://example.org/hello'))
+ end
+
+ def test_non_greedy_glob_regexp
+ params = nil
+ rs.draw do
+ get '/posts/:id(/*filters)', :constraints => { :filters => /.+?/ },
+ :to => lambda { |e|
+ params = e["action_dispatch.request.path_parameters"]
+ [200, {}, ['foo']]
+ }
+ end
+ assert_equal 'foo', get(URI('http://example.org/posts/1/foo.js'))
+ assert_equal({:id=>"1", :filters=>"foo", :format=>"js"}, params)
end
def test_draw_with_block_arity_one_raises
@@ -120,36 +268,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase
test_default_setup
end
- def test_time_recognition
- # We create many routes to make situation more realistic
- @rs = ::ActionDispatch::Routing::RouteSet.new
- @rs.draw {
- root :to => "search#new", :as => "frontpage"
- resources :videos do
- resources :comments
- resource :file, :controller => 'video_file'
- resource :share, :controller => 'video_shares'
- resource :abuse, :controller => 'video_abuses'
- end
- resources :abuses, :controller => 'video_abuses'
- resources :video_uploads
- resources :video_visits
-
- resources :users do
- resource :settings
- resources :videos
- end
- resources :channels do
- resources :videos, :controller => 'channel_videos'
- end
- resource :session
- resource :lost_password
- match 'search' => 'search#index', :as => 'search'
- resources :pages
- match ':controller/:action/:id'
- }
- end
-
def test_route_with_colon_first
rs.draw do
match '/:controller/:action/:id', :action => 'index', :id => nil
@@ -413,7 +531,7 @@ class LegacyRouteSetTests < Test::Unit::TestCase
assert_equal({ :controller => "content", :action => 'show_page', :id => 'foo' }, rs.recognize_path("/page/foo"))
token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in Russian
- token.force_encoding(Encoding::BINARY) if token.respond_to?(:force_encoding)
+ token.force_encoding(Encoding::BINARY)
escaped_token = CGI::escape(token)
assert_equal '/page/' + escaped_token, url_for(rs, { :controller => 'content', :action => 'show_page', :id => token })
@@ -719,12 +837,12 @@ class RouteSetTest < ActiveSupport::TestCase
assert_equal set.routes.first, set.named_routes[:hello]
end
- def test_later_named_routes_take_precedence
+ def test_earlier_named_routes_take_precedence
set.draw do
match '/hello/world' => 'a#b', :as => 'hello'
match '/hello' => 'a#b', :as => 'hello'
end
- assert_equal set.routes.last, set.named_routes[:hello]
+ assert_equal set.routes.first, set.named_routes[:hello]
end
def setup_named_route_test
@@ -1258,7 +1376,20 @@ class RouteSetTest < ActiveSupport::TestCase
end
end
end
-
+
+ def test_route_with_subdomain_and_constraints_must_receive_params
+ name_param = nil
+ set.draw do
+ match 'page/:name' => 'pages#show', :constraints => lambda {|request|
+ name_param = request.params[:name]
+ return true
+ }
+ end
+ assert_equal({:controller => 'pages', :action => 'show', :name => 'mypage'},
+ set.recognize_path('http://subdomain.example.org/page/mypage'))
+ assert_equal(name_param, 'mypage')
+ end
+
def test_route_requirement_recognize_with_ignore_case
set.draw do
match 'page/:name' => 'pages#show',
diff --git a/actionpack/test/controller/runner_test.rb b/actionpack/test/controller/runner_test.rb
index 24c220dcd5..3e9383abb2 100644
--- a/actionpack/test/controller/runner_test.rb
+++ b/actionpack/test/controller/runner_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'action_dispatch/testing/integration'
module ActionDispatch
- class RunnerTest < Test::Unit::TestCase
+ class RunnerTest < ActiveSupport::TestCase
class MyRunner
include Integration::Runner
diff --git a/actionpack/test/controller/selector_test.rb b/actionpack/test/controller/selector_test.rb
index 8ce9e43402..5e302da212 100644
--- a/actionpack/test/controller/selector_test.rb
+++ b/actionpack/test/controller/selector_test.rb
@@ -7,7 +7,7 @@ require 'abstract_unit'
require 'controller/fake_controllers'
require 'action_controller/vendor/html-scanner'
-class SelectorTest < Test::Unit::TestCase
+class SelectorTest < ActiveSupport::TestCase
#
# Basic selector: element, id, class, attributes.
#
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 8f885ff28e..36884846be 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -61,7 +61,7 @@ class SendFileTest < ActionController::TestCase
require 'stringio'
output = StringIO.new
output.binmode
- output.string.force_encoding(file_data.encoding) if output.string.respond_to?(:force_encoding)
+ output.string.force_encoding(file_data.encoding)
assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } }
assert_equal file_data, output.string
end
diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb
new file mode 100644
index 0000000000..13ab19ed8f
--- /dev/null
+++ b/actionpack/test/controller/show_exceptions_test.rb
@@ -0,0 +1,71 @@
+require 'abstract_unit'
+
+module ShowExceptions
+ class ShowExceptionsController < ActionController::Base
+ use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
+ use ActionDispatch::DebugExceptions
+
+ before_filter :only => :another_boom do
+ request.env["action_dispatch.show_detailed_exceptions"] = true
+ end
+
+ def boom
+ raise 'boom!'
+ end
+
+ def another_boom
+ raise 'boom!'
+ end
+
+ def show_detailed_exceptions?
+ request.local?
+ end
+ end
+
+ class ShowExceptionsTest < ActionDispatch::IntegrationTest
+ test 'show error page from a remote ip' do
+ @app = ShowExceptionsController.action(:boom)
+ self.remote_addr = '208.77.188.166'
+ get '/'
+ assert_equal "500 error fixture\n", body
+ end
+
+ test 'show diagnostics from a local ip if show_detailed_exceptions? is set to request.local?' do
+ @app = ShowExceptionsController.action(:boom)
+ ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address|
+ self.remote_addr = ip_address
+ get '/'
+ assert_match(/boom/, body)
+ end
+ end
+
+ test 'show diagnostics from a remote ip when env is already set' do
+ @app = ShowExceptionsController.action(:another_boom)
+ self.remote_addr = '208.77.188.166'
+ get '/'
+ assert_match(/boom/, body)
+ end
+ end
+
+ class ShowExceptionsOverridenController < ShowExceptionsController
+ private
+
+ def show_detailed_exceptions?
+ params['detailed'] == '1'
+ end
+ end
+
+ class ShowExceptionsOverridenTest < ActionDispatch::IntegrationTest
+ test 'show error page' do
+ @app = ShowExceptionsOverridenController.action(:boom)
+ get '/', {'detailed' => '0'}
+ assert_equal "500 error fixture\n", body
+ end
+
+ test 'show diagnostics message' do
+ @app = ShowExceptionsOverridenController.action(:boom)
+ get '/', {'detailed' => '1'}
+ assert_match(/boom/, body)
+ end
+ end
+end
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_case_test.rb
index b64e275363..c957df88b3 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -1,8 +1,7 @@
require 'abstract_unit'
require 'controller/fake_controllers'
-require 'active_support/ordered_hash'
-class TestTest < ActionController::TestCase
+class TestCaseTest < ActionController::TestCase
class TestController < ActionController::Base
def no_op
render :text => 'dummy'
@@ -124,9 +123,6 @@ XML
end
private
- def rescue_action(e)
- raise e
- end
def generate_url(opts)
url_for(opts.merge(:action => "test_uri"))
@@ -158,32 +154,56 @@ XML
end
def test_raw_post_handling
- params = ActiveSupport::OrderedHash[:page, {:name => 'page name'}, 'some key', 123]
+ params = Hash[:page, {:name => 'page name'}, 'some key', 123]
post :render_raw_post, params.dup
assert_equal params.to_query, @response.body
end
def test_body_stream
- params = ActiveSupport::OrderedHash[:page, { :name => 'page name' }, 'some key', 123]
+ params = Hash[:page, { :name => 'page name' }, 'some key', 123]
post :render_body, params.dup
assert_equal params.to_query, @response.body
end
+ def test_document_body_and_params_with_post
+ post :test_params, :id => 1
+ assert_equal("{\"id\"=>\"1\", \"controller\"=>\"test_case_test/test\", \"action\"=>\"test_params\"}", @response.body)
+ end
+
+ def test_document_body_with_post
+ post :render_body, "document body"
+ assert_equal "document body", @response.body
+ end
+
+ def test_document_body_with_put
+ put :render_body, "document body"
+ assert_equal "document body", @response.body
+ end
+
+ def test_head
+ head :test_params
+ assert_equal 200, @response.status
+ end
+
+ def test_head_params_as_sting
+ assert_raise(NoMethodError) { head :test_params, "document body", :id => 10 }
+ end
+
def test_process_without_flash
process :set_flash
assert_equal '><', flash['test']
end
def test_process_with_flash
- process :set_flash, nil, nil, { "test" => "value" }
+ process :set_flash, "GET", nil, nil, { "test" => "value" }
assert_equal '>value<', flash['test']
end
def test_process_with_flash_now
- process :set_flash_now, nil, nil, { "test_now" => "value_now" }
+ process :set_flash_now, "GET", nil, nil, { "test_now" => "value_now" }
assert_equal '>value_now<', flash['test_now']
end
@@ -196,7 +216,7 @@ XML
end
def test_process_with_session_arg
- process :no_op, nil, { 'string' => 'value1', :symbol => 'value2' }
+ process :no_op, "GET", nil, { 'string' => 'value1', :symbol => 'value2' }
assert_equal 'value1', session['string']
assert_equal 'value1', session[:string]
assert_equal 'value2', session['symbol']
@@ -223,22 +243,29 @@ XML
def test_process_with_request_uri_with_no_params
process :test_uri
- assert_equal "/test_test/test/test_uri", @response.body
+ assert_equal "/test_case_test/test/test_uri", @response.body
end
def test_process_with_request_uri_with_params
- process :test_uri, :id => 7
- assert_equal "/test_test/test/test_uri/7", @response.body
+ process :test_uri, "GET", :id => 7
+ assert_equal "/test_case_test/test/test_uri/7", @response.body
+ end
+
+ def test_process_with_old_api
+ assert_deprecated do
+ process :test_uri, :id => 7
+ assert_equal "/test_case_test/test/test_uri/7", @response.body
+ end
end
def test_process_with_request_uri_with_params_with_explicit_uri
@request.env['PATH_INFO'] = "/explicit/uri"
- process :test_uri, :id => 7
+ process :test_uri, "GET", :id => 7
assert_equal "/explicit/uri", @response.body
end
def test_process_with_query_string
- process :test_query_string, :q => 'test'
+ process :test_query_string, "GET", :q => 'test'
assert_equal "q=test", @response.body
end
@@ -250,9 +277,9 @@ XML
end
def test_multiple_calls
- process :test_only_one_param, :left => true
+ process :test_only_one_param, "GET", :left => true
assert_equal "OK", @response.body
- process :test_only_one_param, :right => true
+ process :test_only_one_param, "GET", :right => true
assert_equal "OK", @response.body
end
@@ -511,7 +538,7 @@ XML
get :test_params, :page => {:name => "Page name", :month => '4', :year => '2004', :day => '6'}
parsed_params = eval(@response.body)
assert_equal(
- {'controller' => 'test_test/test', 'action' => 'test_params',
+ {'controller' => 'test_case_test/test', 'action' => 'test_params',
'page' => {'name' => "Page name", 'month' => '4', 'year' => '2004', 'day' => '6'}},
parsed_params
)
@@ -521,7 +548,7 @@ XML
get :test_params, :page => {:name => "Page name", :month => 4, :year => 2004, :day => 6}
parsed_params = eval(@response.body)
assert_equal(
- {'controller' => 'test_test/test', 'action' => 'test_params',
+ {'controller' => 'test_case_test/test', 'action' => 'test_params',
'page' => {'name' => "Page name", 'month' => '4', 'year' => '2004', 'day' => '6'}},
parsed_params
)
@@ -533,7 +560,7 @@ XML
end
parsed_params = eval(@response.body)
assert_equal(
- {'controller' => 'test_test/test', 'action' => 'test_params',
+ {'controller' => 'test_case_test/test', 'action' => 'test_params',
'frozen' => 'icy', 'frozens' => ['icy']},
parsed_params
)
@@ -553,7 +580,7 @@ XML
def test_array_path_parameter_handled_properly
with_routing do |set|
set.draw do
- match 'file/*path', :to => 'test_test/test#test_params'
+ match 'file/*path', :to => 'test_case_test/test#test_params'
match ':controller/:action'
end
@@ -665,20 +692,15 @@ XML
FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart'
- if RUBY_VERSION < '1.9'
- READ_BINARY = 'rb'
- READ_PLAIN = 'r'
- else
- READ_BINARY = 'rb:binary'
- READ_PLAIN = 'r:binary'
- end
+ READ_BINARY = 'rb:binary'
+ READ_PLAIN = 'r:binary'
def test_test_uploaded_file
filename = 'mona_lisa.jpg'
path = "#{FILES_DIR}/#{filename}"
content_type = 'image/png'
expected = File.read(path)
- expected.force_encoding(Encoding::BINARY) if expected.respond_to?(:force_encoding)
+ expected.force_encoding(Encoding::BINARY)
file = Rack::Test::UploadedFile.new(path, content_type)
assert_equal filename, file.original_filename
@@ -693,7 +715,7 @@ XML
end
def test_fixture_path_is_accessed_from_self_instead_of_active_support_test_case
- TestTest.stubs(:fixture_path).returns(FILES_DIR)
+ TestCaseTest.stubs(:fixture_path).returns(FILES_DIR)
uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png')
assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
diff --git a/actionpack/test/controller/url_for_integration_test.rb b/actionpack/test/controller/url_for_integration_test.rb
index 7b734ff0fb..451ea6027d 100644
--- a/actionpack/test/controller/url_for_integration_test.rb
+++ b/actionpack/test/controller/url_for_integration_test.rb
@@ -3,12 +3,6 @@ require 'abstract_unit'
require 'controller/fake_controllers'
require 'active_support/core_ext/object/with_options'
-module RoutingTestHelpers
- def url_for(set, options, recall = nil)
- set.send(:url_for, options.merge(:only_path => true, :_path_segments => recall))
- end
-end
-
module ActionPack
class URLForIntegrationTest < ActiveSupport::TestCase
include RoutingTestHelpers
diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index 11ced2df2a..288efbf7c3 100644
--- a/actionpack/test/controller/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -16,6 +16,10 @@ module AbstractController
W.default_url_options[:host] = 'www.basecamphq.com'
end
+ def add_numeric_host!
+ W.default_url_options[:host] = '127.0.0.1'
+ end
+
def test_exception_is_thrown_without_host
assert_raise ArgumentError do
W.new.url_for :controller => 'c', :action => 'a', :id => 'i'
@@ -67,6 +71,14 @@ module AbstractController
)
end
+ def test_subdomain_may_be_object
+ model = mock(:to_param => 'api')
+ add_host!
+ assert_equal('http://api.basecamphq.com/c/a/i',
+ W.new.url_for(:subdomain => model, :controller => 'c', :action => 'a', :id => 'i')
+ )
+ end
+
def test_subdomain_may_be_removed
add_host!
assert_equal('http://basecamphq.com/c/a/i',
@@ -81,6 +93,13 @@ module AbstractController
)
end
+ def test_subdomain_may_be_accepted_with_numeric_host
+ add_numeric_host!
+ assert_equal('http://127.0.0.1/c/a/i',
+ W.new.url_for(:subdomain => 'api', :controller => 'c', :action => 'a', :id => 'i')
+ )
+ end
+
def test_domain_may_be_changed
add_host!
assert_equal('http://www.37signals.com/c/a/i',
diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb
index f5ac886c20..40f6dc6f0f 100644
--- a/actionpack/test/controller/view_paths_test.rb
+++ b/actionpack/test/controller/view_paths_test.rb
@@ -3,7 +3,6 @@ require 'abstract_unit'
class ViewLoadPathsTest < ActionController::TestCase
class TestController < ActionController::Base
def self.controller_path() "test" end
- def rescue_action(e) raise end
before_filter :add_view_path, :only => :hello_world_at_request_time
@@ -16,22 +15,17 @@ class ViewLoadPathsTest < ActionController::TestCase
end
end
- class Test::SubController < ActionController::Base
- layout 'test/sub'
- def hello_world; render(:template => 'test/hello_world'); end
+ module Test
+ class SubController < ActionController::Base
+ layout 'test/sub'
+ def hello_world; render(:template => 'test/hello_world'); end
+ end
end
def setup
- # TestController.view_paths = nil
-
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
-
@controller = TestController.new
- # Following is needed in order to setup @controller.template object properly
- @controller.send :assign_shortcuts, @request, @response
- @controller.send :initialize_template_class, @response
-
@paths = TestController.view_paths
end
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index ae8588cbb0..351e61eeae 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -18,8 +18,6 @@ class WebServiceTest < ActionDispatch::IntegrationTest
s << "#{k}#{value}"
end
end
-
- def rescue_action(e) raise end
end
def setup
diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb
index eed2eca2ab..f767b07e75 100644
--- a/actionpack/test/dispatch/callbacks_test.rb
+++ b/actionpack/test/dispatch/callbacks_test.rb
@@ -27,6 +27,12 @@ class DispatcherTest < ActiveSupport::TestCase
dispatch
assert_equal 4, Foo.a
assert_equal 4, Foo.b
+
+ dispatch do |env|
+ raise "error"
+ end rescue nil
+ assert_equal 6, Foo.a
+ assert_equal 6, Foo.b
end
def test_to_prepare_and_cleanup_delegation
@@ -44,8 +50,9 @@ class DispatcherTest < ActiveSupport::TestCase
private
def dispatch(&block)
- @dispatcher ||= ActionDispatch::Callbacks.new(block || DummyApp.new)
- @dispatcher.call({'rack.input' => StringIO.new('')})
+ ActionDispatch::Callbacks.new(block || DummyApp.new).call(
+ {'rack.input' => StringIO.new('')}
+ )
end
end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 49da448001..3e48d97e67 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -210,8 +210,8 @@ class CookiesTest < ActionController::TestCase
assert_equal({"user_name" => "david"}, @response.cookies)
end
- def test_setting_cookie_with_secure_in_development
- Rails.env.stubs(:development?).returns(true)
+ def test_setting_cookie_with_secure_when_always_write_cookie_is_true
+ ActionDispatch::Cookies::CookieJar.any_instance.stubs(:always_write_cookie).returns(true)
get :authenticate_with_secure
assert_cookie_header "user_name=david; path=/; secure"
assert_equal({"user_name" => "david"}, @response.cookies)
@@ -245,6 +245,17 @@ class CookiesTest < ActionController::TestCase
assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"
end
+ def test_deleted_cookie_predicate
+ cookies.delete("user_name")
+ assert cookies.deleted?("user_name")
+ assert_equal false, cookies.deleted?("another")
+ end
+
+ def test_deleted_cookie_predicate_with_mismatching_options
+ cookies.delete("user_name", :path => "/path")
+ assert_equal false, cookies.deleted?("user_name", :path => "/different")
+ end
+
def test_cookies_persist_throughout_request
response = get :authenticate
assert response.headers["Set-Cookie"] =~ /user_name=david/
@@ -564,100 +575,4 @@ class CookiesTest < ActionController::TestCase
assert_not_equal expected.split("\n"), header
end
end
-end
-
-class CookiesIntegrationTest < ActionDispatch::IntegrationTest
- SessionKey = '_myapp_session'
- SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
-
- class TestController < ActionController::Base
- def dont_set_cookies
- head :ok
- end
-
- def set_cookies
- cookies["that"] = "hello"
- head :ok
- end
- end
-
- def test_setting_cookies_raises_after_stream_back_to_client
- with_test_route_set do
- get '/set_cookies'
- assert_raise(ActionDispatch::ClosedError) {
- request.cookie_jar['alert'] = 'alert'
- cookies['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_cookies_raises_after_stream_back_to_client_even_without_cookies
- with_test_route_set do
- get '/dont_set_cookies'
- assert_raise(ActionDispatch::ClosedError) {
- request.cookie_jar['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_permanent_cookies_raises_after_stream_back_to_client
- with_test_route_set do
- get '/set_cookies'
- assert_raise(ActionDispatch::ClosedError) {
- request.cookie_jar.permanent['alert'] = 'alert'
- cookies['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_permanent_cookies_raises_after_stream_back_to_client_even_without_cookies
- with_test_route_set do
- get '/dont_set_cookies'
- assert_raise(ActionDispatch::ClosedError) {
- request.cookie_jar.permanent['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_signed_cookies_raises_after_stream_back_to_client
- with_test_route_set do
- get '/set_cookies'
- assert_raise(ActionDispatch::ClosedError) {
- request.cookie_jar.signed['alert'] = 'alert'
- cookies['alert'] = 'alert'
- }
- end
- end
-
- def test_setting_signed_cookies_raises_after_stream_back_to_client_even_without_cookies
- with_test_route_set do
- get '/dont_set_cookies'
- assert_raise(ActionDispatch::ClosedError) {
- request.cookie_jar.signed['alert'] = 'alert'
- }
- end
- end
-
- private
-
- # Overwrite get to send SessionSecret in env hash
- def get(path, parameters = nil, env = {})
- env["action_dispatch.secret_token"] ||= SessionSecret
- super
- end
-
- def with_test_route_set
- with_routing do |set|
- set.draw do
- match ':action', :to => CookiesIntegrationTest::TestController
- end
-
- @app = self.class.build_app(set) do |middleware|
- middleware.use ActionDispatch::Cookies
- middleware.delete "ActionDispatch::ShowExceptions"
- end
-
- yield
- end
- end
-end
+end \ No newline at end of file
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
new file mode 100644
index 0000000000..11c292d61a
--- /dev/null
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -0,0 +1,157 @@
+require 'abstract_unit'
+
+class DebugExceptionsTest < ActionDispatch::IntegrationTest
+
+ class Boomer
+ attr_accessor :closed
+
+ def initialize(detailed = false)
+ @detailed = detailed
+ @closed = false
+ end
+
+ def each
+ end
+
+ def close
+ @closed = true
+ end
+
+ def call(env)
+ env['action_dispatch.show_detailed_exceptions'] = @detailed
+ req = ActionDispatch::Request.new(env)
+ case req.path
+ when "/pass"
+ [404, { "X-Cascade" => "pass" }, self]
+ when "/not_found"
+ raise AbstractController::ActionNotFound
+ when "/runtime_error"
+ raise RuntimeError
+ when "/method_not_allowed"
+ raise ActionController::MethodNotAllowed
+ when "/not_implemented"
+ raise ActionController::NotImplemented
+ when "/unprocessable_entity"
+ raise ActionController::InvalidAuthenticityToken
+ when "/not_found_original_exception"
+ raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new)
+ else
+ raise "puke!"
+ end
+ end
+ end
+
+ ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false))
+ DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true))
+
+ test 'skip diagnosis if not showing detailed exceptions' do
+ @app = ProductionApp
+ assert_raise RuntimeError do
+ get "/", {}, {'action_dispatch.show_exceptions' => true}
+ end
+ end
+
+ test 'skip diagnosis if not showing exceptions' do
+ @app = DevelopmentApp
+ assert_raise RuntimeError do
+ get "/", {}, {'action_dispatch.show_exceptions' => false}
+ end
+ end
+
+ test 'raise an exception on cascade pass' do
+ @app = ProductionApp
+ assert_raise ActionController::RoutingError do
+ get "/pass", {}, {'action_dispatch.show_exceptions' => true}
+ end
+ end
+
+ test 'closes the response body on cascade pass' do
+ boomer = Boomer.new(false)
+ @app = ActionDispatch::DebugExceptions.new(boomer)
+ assert_raise ActionController::RoutingError do
+ get "/pass", {}, {'action_dispatch.show_exceptions' => true}
+ end
+ assert boomer.closed, "Expected to close the response body"
+ end
+
+ test "rescue with diagnostics message" do
+ @app = DevelopmentApp
+
+ get "/", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 500
+ assert_match(/puke/, body)
+
+ get "/not_found", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 404
+ assert_match(/#{AbstractController::ActionNotFound.name}/, body)
+
+ get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 405
+ assert_match(/ActionController::MethodNotAllowed/, body)
+ end
+
+ test "does not show filtered parameters" do
+ @app = DevelopmentApp
+
+ get "/", {"foo"=>"bar"}, {'action_dispatch.show_exceptions' => true,
+ 'action_dispatch.parameter_filter' => [:foo]}
+ assert_response 500
+ assert_match("&quot;foo&quot;=&gt;&quot;[FILTERED]&quot;", body)
+ end
+
+ test "show registered original exception for wrapped exceptions" do
+ @app = DevelopmentApp
+
+ get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 404
+ assert_match(/AbstractController::ActionNotFound/, body)
+ end
+
+ test "show the controller name in the diagnostics template when controller name is present" do
+ @app = DevelopmentApp
+ get("/runtime_error", {}, {
+ 'action_dispatch.show_exceptions' => true,
+ 'action_dispatch.request.parameters' => {
+ 'action' => 'show',
+ 'id' => 'unknown',
+ 'controller' => 'featured_tile'
+ }
+ })
+ assert_response 500
+ assert_match(/RuntimeError\n in FeaturedTileController/, body)
+ end
+
+ test "sets the HTTP charset parameter" do
+ @app = DevelopmentApp
+
+ get "/", {}, {'action_dispatch.show_exceptions' => true}
+ assert_equal "text/html; charset=utf-8", response.headers["Content-Type"]
+ end
+
+ test 'uses logger from env' do
+ @app = DevelopmentApp
+ output = StringIO.new
+ get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => Logger.new(output)}
+ assert_match(/puke/, output.rewind && output.read)
+ end
+
+ test 'uses backtrace cleaner from env' do
+ @app = DevelopmentApp
+ cleaner = stub(:clean => ['passed backtrace cleaner'])
+ get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.backtrace_cleaner' => cleaner}
+ assert_match(/passed backtrace cleaner/, body)
+ end
+
+ test 'logs exception backtrace when all lines silenced' do
+ output = StringIO.new
+ backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
+ backtrace_cleaner.add_silencer { true }
+
+ env = {'action_dispatch.show_exceptions' => true,
+ 'action_dispatch.logger' => Logger.new(output),
+ 'action_dispatch.backtrace_cleaner' => backtrace_cleaner}
+
+ get "/", {}, env
+ assert_operator((output.rewind && output.read).lines.count, :>, 10)
+ end
+end
diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb
index 831f3db3e2..4191ed1ff4 100644
--- a/actionpack/test/dispatch/middleware_stack_test.rb
+++ b/actionpack/test/dispatch/middleware_stack_test.rb
@@ -81,6 +81,12 @@ class MiddlewareStackTest < ActiveSupport::TestCase
assert_equal BazMiddleware, @stack[0].klass
end
+ test "swaps one middleware out for same middleware class" do
+ assert_equal FooMiddleware, @stack[0].klass
+ @stack.swap(FooMiddleware, FooMiddleware, Proc.new { |env| [500, {}, ['error!']] })
+ assert_equal FooMiddleware, @stack[0].klass
+ end
+
test "raise an error on invalid index" do
assert_raise RuntimeError do
@stack.insert("HiyaMiddleware", BazMiddleware)
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 08fe2127b9..0372c42920 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -69,6 +69,12 @@ class MimeTypeTest < ActiveSupport::TestCase
assert_equal expect, Mime::Type.parse(accept)
end
+ test "parse single media range with q" do
+ accept = "text/html;q=0.9"
+ expect = [Mime::HTML]
+ assert_equal expect, Mime::Type.parse(accept)
+ end
+
# Accept header send with user HTTP_USER_AGENT: Sunrise/0.42j (Windows XP)
test "parse broken acceptlines" do
accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/*,,*/*;q=0.5"
@@ -95,6 +101,37 @@ class MimeTypeTest < ActiveSupport::TestCase
end
end
+ test "custom type with type aliases" do
+ begin
+ Mime::Type.register "text/foobar", :foobar, ["text/foo", "text/bar"]
+ %w[text/foobar text/foo text/bar].each do |type|
+ assert_equal Mime::FOOBAR, type
+ end
+ ensure
+ Mime::Type.unregister(:FOOBAR)
+ end
+ end
+
+ test "custom type with extension aliases" do
+ begin
+ Mime::Type.register "text/foobar", :foobar, [], [:foo, "bar"]
+ %w[foobar foo bar].each do |extension|
+ assert_equal Mime::FOOBAR, Mime::EXTENSION_LOOKUP[extension]
+ end
+ ensure
+ Mime::Type.unregister(:FOOBAR)
+ end
+ end
+
+ test "register alias" do
+ begin
+ Mime::Type.register_alias "application/xhtml+xml", :foobar
+ assert_equal Mime::HTML, Mime::EXTENSION_LOOKUP['foobar']
+ ensure
+ Mime::Type.unregister(:FOOBAR)
+ end
+ end
+
test "type should be equal to symbol" do
assert_equal Mime::HTML, 'application/xhtml+xml'
assert_equal Mime::HTML, :html
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
index 1a032539b9..f7a746120e 100644
--- a/actionpack/test/dispatch/mount_test.rb
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -51,4 +51,4 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
get "/fakeengine"
assert_equal "OK", response.body
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index 93eccaecbd..bd5b5edab0 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -30,6 +30,7 @@ module TestGenerationPrefix
match "/url_to_application", :to => "inside_engine_generating#url_to_application"
match "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine"
match "/conflicting_url", :to => "inside_engine_generating#conflicting"
+ match "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test
end
routes
@@ -152,6 +153,8 @@ module TestGenerationPrefix
RailsApplication.routes.default_url_options = {}
end
+ include BlogEngine.routes.mounted_helpers
+
# Inside Engine
test "[ENGINE] generating engine's url use SCRIPT_NAME from request" do
get "/pure-awesomeness/blog/posts/1"
@@ -219,6 +222,10 @@ module TestGenerationPrefix
end
# Inside any Object
+ test "[OBJECT] proxy route should override respond_to?() as expected" do
+ assert_respond_to blog_engine, :named_helper_that_should_be_invoked_only_in_respond_to_test_path
+ end
+
test "[OBJECT] generating engine's route includes prefix" do
assert_equal "/awesome/blog/posts/1", engine_object.post_path(:id => 1)
end
diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb
index eaabc1feb3..ce9ccfcee8 100644
--- a/actionpack/test/dispatch/reloader_test.rb
+++ b/actionpack/test/dispatch/reloader_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class ReloaderTest < Test::Unit::TestCase
+class ReloaderTest < ActiveSupport::TestCase
Reloader = ActionDispatch::Reloader
def test_prepare_callbacks
@@ -43,6 +43,29 @@ class ReloaderTest < Test::Unit::TestCase
assert_respond_to body, :close
end
+ def test_returned_body_object_always_responds_to_close_even_if_called_twice
+ body = call_and_return_body
+ assert_respond_to body, :close
+ body.close
+
+ body = call_and_return_body
+ assert_respond_to body, :close
+ body.close
+ end
+
+ def test_condition_specifies_when_to_reload
+ i, j = 0, 0, 0, 0
+ Reloader.to_prepare { |*args| i += 1 }
+ Reloader.to_cleanup { |*args| j += 1 }
+ app = Reloader.new(lambda { |env| [200, {}, []] }, lambda { i < 3 })
+ 5.times do
+ resp = app.call({})
+ resp[2].close
+ end
+ assert_equal 3, i
+ assert_equal 3, j
+ end
+
def test_returned_body_object_behaves_like_underlying_object
body = call_and_return_body do
b = MyBody.new
@@ -116,6 +139,15 @@ class ReloaderTest < Test::Unit::TestCase
assert cleaned
end
+ def test_prepend_prepare_callback
+ i = 10
+ Reloader.to_prepare { i += 1 }
+ Reloader.to_prepare(:prepend => true) { i = 0 }
+
+ Reloader.prepare!
+ assert_equal 1, i
+ end
+
def test_cleanup_callbacks_are_called_on_exceptions
cleaned = false
Reloader.to_cleanup { cleaned = true }
@@ -132,7 +164,8 @@ class ReloaderTest < Test::Unit::TestCase
private
def call_and_return_body(&block)
- @reloader ||= Reloader.new(block || proc {[200, {}, 'response']})
+ @response ||= 'response'
+ @reloader ||= Reloader.new(block || proc {[200, {}, @response]})
@reloader.call({'rack.input' => StringIO.new('')})[2]
end
end
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index d854d55173..ae425dd406 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -32,13 +32,21 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
test "logs error if parsing unsuccessful" do
with_test_routing do
+ output = StringIO.new
+ json = "[\"person]\": {\"name\": \"David\"}}"
+ post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => ActiveSupport::Logger.new(output)}
+ assert_response :error
+ output.rewind && err = output.read
+ assert err =~ /Error occurred while parsing request parameters/
+ end
+ end
+
+ test "occurring a parse error if parsing unsuccessful" do
+ with_test_routing do
begin
- $stderr = StringIO.new
+ $stderr = StringIO.new # suppress the log
json = "[\"person]\": {\"name\": \"David\"}}"
- post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => true}
- assert_response :error
- $stderr.rewind && err = $stderr.read
- assert err =~ /Error occurred while parsing request parameters/
+ assert_raise(MultiJson::DecodeError) { post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => false} }
ensure
$stderr = STDERR
end
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 560ea00923..d144f013f5 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -89,7 +89,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
# Rack doesn't handle multipart/mixed for us.
files = params['files']
- files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
+ files.force_encoding('ASCII-8BIT')
assert_equal 19756, files.size
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 04a0fb6f34..05569561d2 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -146,8 +146,6 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
end
def assert_utf8(object)
- return unless "ruby".encoding_aware?
-
correct_encoding = Encoding.default_internal
unless object.is_a?(Hash)
diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
index 38453dfe48..afd400c2a9 100644
--- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
@@ -41,7 +41,7 @@ class XmlParamsParsingTest < ActionDispatch::IntegrationTest
test "parses single file" do
with_test_routing do
- xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar></person>"
+ xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{::Base64.encode64('ABC')}</avatar></person>"
post "/parse", xml, default_headers
assert_response :ok
@@ -54,13 +54,21 @@ class XmlParamsParsingTest < ActionDispatch::IntegrationTest
test "logs error if parsing unsuccessful" do
with_test_routing do
+ output = StringIO.new
+ xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{::Base64.encode64('ABC')}</avatar></pineapple>"
+ post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => ActiveSupport::Logger.new(output))
+ assert_response :error
+ output.rewind && err = output.read
+ assert err =~ /Error occurred while parsing request parameters/
+ end
+ end
+
+ test "occurring a parse error if parsing unsuccessful" do
+ with_test_routing do
begin
- $stderr = StringIO.new
- xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar></pineapple>"
- post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => true)
- assert_response :error
- $stderr.rewind && err = $stderr.read
- assert err =~ /Error occurred while parsing request parameters/
+ $stderr = StringIO.new # suppress the log
+ xml = "<person><name>David</name></pineapple>"
+ assert_raise(REXML::ParseException) { post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => false) }
ensure
$stderr = STDERR
end
@@ -72,8 +80,8 @@ class XmlParamsParsingTest < ActionDispatch::IntegrationTest
<person>
<name>David</name>
<avatars>
- <avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar>
- <avatar type='file' name='you.gif' content_type='image/gif'>#{ActiveSupport::Base64.encode64('DEF')}</avatar>
+ <avatar type='file' name='me.jpg' content_type='image/jpg'>#{::Base64.encode64('ABC')}</avatar>
+ <avatar type='file' name='you.gif' content_type='image/gif'>#{::Base64.encode64('DEF')}</avatar>
</avatars>
</person>
end_body
diff --git a/actionpack/test/dispatch/request_id_test.rb b/actionpack/test/dispatch/request_id_test.rb
index b6e8b6c3ad..4b98cd32f2 100644
--- a/actionpack/test/dispatch/request_id_test.rb
+++ b/actionpack/test/dispatch/request_id_test.rb
@@ -14,7 +14,7 @@ class RequestIdTest < ActiveSupport::TestCase
end
test "generating a request id when none is supplied" do
- assert_match(/\w+/, stub_request.uuid)
+ assert_match(/\w+-\w+-\w+-\w+-\w+/, stub_request.uuid)
end
private
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index a611252b31..8f0ac5310e 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -36,13 +36,13 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'REMOTE_ADDR' => '1.2.3.4',
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
- assert_equal '1.2.3.4', request.remote_ip
+ assert_equal '3.4.5.6', request.remote_ip
request = stub_request 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
assert_equal '3.4.5.6', request.remote_ip
- request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6'
+ request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,unknown'
assert_equal '3.4.5.6', request.remote_ip
request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.16.0.1,3.4.5.6'
@@ -63,7 +63,7 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
assert_equal 'unknown', request.remote_ip
- request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
+ request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 9.9.9.9, 10.0.0.1, 172.31.4.4'
assert_equal '3.4.5.6', request.remote_ip
request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
@@ -85,12 +85,17 @@ class RequestTest < ActiveSupport::TestCase
:ip_spoofing_check => false
assert_equal '2.2.2.2', request.remote_ip
- request = stub_request 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
+ request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 8.8.8.8'
assert_equal '9.9.9.9', request.remote_ip
end
- test "remote ip with user specified trusted proxies" do
- @trusted_proxies = /^67\.205\.106\.73$/i
+ test "remote ip when the remote ip middleware returns nil" do
+ request = stub_request 'REMOTE_ADDR' => '127.0.0.1'
+ assert_equal '127.0.0.1', request.remote_ip
+ end
+
+ test "remote ip with user specified trusted proxies String" do
+ @trusted_proxies = "67.205.106.73"
request = stub_request 'REMOTE_ADDR' => '67.205.106.73',
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
@@ -106,15 +111,26 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'REMOTE_ADDR' => '67.205.106.74,172.16.0.1',
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
- assert_equal '67.205.106.74', request.remote_ip
+ assert_equal '3.4.5.6', request.remote_ip
request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,67.205.106.73'
assert_equal 'unknown', request.remote_ip
- request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73'
+ request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 9.9.9.9, 10.0.0.1, 67.205.106.73'
assert_equal '3.4.5.6', request.remote_ip
end
+ test "remote ip with user specified trusted proxies Regexp" do
+ @trusted_proxies = /^67\.205\.106\.73$/i
+
+ request = stub_request 'REMOTE_ADDR' => '67.205.106.73',
+ 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
+ assert_equal '3.4.5.6', request.remote_ip
+
+ request = stub_request 'HTTP_X_FORWARDED_FOR' => '67.205.106.73, 10.0.0.1, 9.9.9.9, 3.4.5.6'
+ assert_equal '10.0.0.1', request.remote_ip
+ end
+
test "domains" do
request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org'
assert_equal "rubyonrails.org", request.domain
@@ -613,6 +629,30 @@ class RequestTest < ActiveSupport::TestCase
assert_equal "/authenticate?secret", path
end
+ test "original_fullpath returns ORIGINAL_FULLPATH" do
+ request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar")
+
+ path = request.original_fullpath
+ assert_equal "/foo?bar", path
+ end
+
+ test "original_url returns url built using ORIGINAL_FULLPATH" do
+ request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar",
+ 'HTTP_HOST' => "example.org",
+ 'rack.url_scheme' => "http")
+
+ url = request.original_url
+ assert_equal "http://example.org/foo?bar", url
+ end
+
+ test "original_fullpath returns fullpath if ORIGINAL_FULLPATH is not present" do
+ request = stub_request('PATH_INFO' => "/foo",
+ 'QUERY_STRING' => "bar")
+
+ path = request.original_fullpath
+ assert_equal "/foo?bar", path
+ end
+
protected
def stub_request(env = {})
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 5abbaf74fe..82d1200f8e 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -5,6 +5,12 @@ class ResponseTest < ActiveSupport::TestCase
@response = ActionDispatch::Response.new
end
+ def test_response_body_encoding
+ body = ["hello".encode('utf-8')]
+ response = ActionDispatch::Response.new 200, {}, body
+ assert_equal Encoding::UTF_8, response.body.encoding
+ end
+
test "simple output" do
@response.body = "Hello, World!"
@@ -130,6 +136,17 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal('application/xml; charset=utf-16', resp.headers['Content-Type'])
end
+
+ test "read content type without charset" do
+ original = ActionDispatch::Response.default_charset
+ begin
+ ActionDispatch::Response.default_charset = 'utf-16'
+ resp = ActionDispatch::Response.new(200, { "Content-Type" => "text/xml" })
+ assert_equal('utf-16', resp.charset)
+ ensure
+ ActionDispatch::Response.default_charset = original
+ end
+ end
end
class ResponseIntegrationTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index cf22731823..563c6efe0d 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1,3 +1,4 @@
+# encoding: UTF-8
require 'erb'
require 'abstract_unit'
require 'controller/fake_controllers'
@@ -62,13 +63,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'secure', :to => redirect("/secure/login")
match 'mobile', :to => redirect(:subdomain => 'mobile')
- match 'documentation', :to => redirect(:domain => 'example-documentation.com', :path => '')
- match 'new_documentation', :to => redirect(:path => '/documentation/new')
match 'super_new_documentation', :to => redirect(:host => 'super-docs.com')
- match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
- match 'stores/:name(*rest)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{rest}')
-
match 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector)
constraints(lambda { |req| true }) do
@@ -79,7 +75,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'sign_in' => "sessions#new"
match 'account/modulo/:name', :to => redirect("/%{name}s")
- match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" }
+ match 'account/proc/:name', :to => redirect {|params, req| "/#{params[:name].pluralize}" }
match 'account/proc_req' => redirect {|params, req| "/#{req.method}" }
match 'account/google' => redirect('http://www.google.com/', :status => 302)
@@ -96,6 +92,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match "/local/:action", :controller => "local"
match "/projects/status(.:format)"
+ match "/404", :to => lambda { |env| [404, {"Content-Type" => "text/plain"}, ["NOT FOUND"]] }
constraints(:ip => /192\.168\.1\.\d\d\d/) do
get 'admin' => "queenbee#index"
@@ -528,6 +525,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
"GET"
end
+ def ip
+ "127.0.0.1"
+ end
+
def x_header
@env["HTTP_X_HEADER"] || ""
end
@@ -578,52 +579,42 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
include Routes.url_helpers
def test_logout
- with_test_routes do
- delete '/logout'
- assert_equal 'sessions#destroy', @response.body
+ delete '/logout'
+ assert_equal 'sessions#destroy', @response.body
- assert_equal '/logout', logout_path
- assert_equal '/logout', url_for(:controller => 'sessions', :action => 'destroy', :only_path => true)
- end
+ assert_equal '/logout', logout_path
+ assert_equal '/logout', url_for(:controller => 'sessions', :action => 'destroy', :only_path => true)
end
def test_login
- with_test_routes do
- get '/login'
- assert_equal 'sessions#new', @response.body
- assert_equal '/login', login_path
+ get '/login'
+ assert_equal 'sessions#new', @response.body
+ assert_equal '/login', login_path
- post '/login'
- assert_equal 'sessions#create', @response.body
+ post '/login'
+ assert_equal 'sessions#create', @response.body
- assert_equal '/login', url_for(:controller => 'sessions', :action => 'create', :only_path => true)
- assert_equal '/login', url_for(:controller => 'sessions', :action => 'new', :only_path => true)
+ assert_equal '/login', url_for(:controller => 'sessions', :action => 'create', :only_path => true)
+ assert_equal '/login', url_for(:controller => 'sessions', :action => 'new', :only_path => true)
- assert_equal 'http://rubyonrails.org/login', Routes.url_for(:controller => 'sessions', :action => 'create')
- assert_equal 'http://rubyonrails.org/login', Routes.url_helpers.login_url
- end
+ assert_equal 'http://rubyonrails.org/login', Routes.url_for(:controller => 'sessions', :action => 'create')
+ assert_equal 'http://rubyonrails.org/login', Routes.url_helpers.login_url
end
def test_login_redirect
- with_test_routes do
- get '/account/login'
- verify_redirect 'http://www.example.com/login'
- end
+ get '/account/login'
+ verify_redirect 'http://www.example.com/login'
end
def test_logout_redirect_without_to
- with_test_routes do
- assert_equal '/account/logout', logout_redirect_path
- get '/account/logout'
- verify_redirect 'http://www.example.com/logout'
- end
+ assert_equal '/account/logout', logout_redirect_path
+ get '/account/logout'
+ verify_redirect 'http://www.example.com/logout'
end
def test_namespace_redirect
- with_test_routes do
- get '/private'
- verify_redirect 'http://www.example.com/private/index'
- end
+ get '/private'
+ verify_redirect 'http://www.example.com/private/index'
end
def test_namespace_with_controller_segment
@@ -639,217 +630,159 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
def test_session_singleton_resource
- with_test_routes do
- get '/session'
- assert_equal 'sessions#create', @response.body
- assert_equal '/session', session_path
+ get '/session'
+ assert_equal 'sessions#create', @response.body
+ assert_equal '/session', session_path
- post '/session'
- assert_equal 'sessions#create', @response.body
+ post '/session'
+ assert_equal 'sessions#create', @response.body
- put '/session'
- assert_equal 'sessions#update', @response.body
+ put '/session'
+ assert_equal 'sessions#update', @response.body
- delete '/session'
- assert_equal 'sessions#destroy', @response.body
+ delete '/session'
+ assert_equal 'sessions#destroy', @response.body
- get '/session/new'
- assert_equal 'sessions#new', @response.body
- assert_equal '/session/new', new_session_path
+ get '/session/new'
+ assert_equal 'sessions#new', @response.body
+ assert_equal '/session/new', new_session_path
- get '/session/edit'
- assert_equal 'sessions#edit', @response.body
- assert_equal '/session/edit', edit_session_path
+ get '/session/edit'
+ assert_equal 'sessions#edit', @response.body
+ assert_equal '/session/edit', edit_session_path
- post '/session/reset'
- assert_equal 'sessions#reset', @response.body
- assert_equal '/session/reset', reset_session_path
- end
+ post '/session/reset'
+ assert_equal 'sessions#reset', @response.body
+ assert_equal '/session/reset', reset_session_path
end
def test_session_info_nested_singleton_resource
- with_test_routes do
- get '/session/info'
- assert_equal 'infos#show', @response.body
- assert_equal '/session/info', session_info_path
- end
+ get '/session/info'
+ assert_equal 'infos#show', @response.body
+ assert_equal '/session/info', session_info_path
end
def test_member_on_resource
- with_test_routes do
- get '/session/crush'
- assert_equal 'sessions#crush', @response.body
- assert_equal '/session/crush', crush_session_path
- end
+ get '/session/crush'
+ assert_equal 'sessions#crush', @response.body
+ assert_equal '/session/crush', crush_session_path
end
def test_redirect_modulo
- with_test_routes do
- get '/account/modulo/name'
- verify_redirect 'http://www.example.com/names'
- end
+ get '/account/modulo/name'
+ verify_redirect 'http://www.example.com/names'
end
def test_redirect_proc
- with_test_routes do
- get '/account/proc/person'
- verify_redirect 'http://www.example.com/people'
- end
+ get '/account/proc/person'
+ verify_redirect 'http://www.example.com/people'
end
def test_redirect_proc_with_request
- with_test_routes do
- get '/account/proc_req'
- verify_redirect 'http://www.example.com/GET'
- end
+ get '/account/proc_req'
+ verify_redirect 'http://www.example.com/GET'
end
def test_redirect_hash_with_subdomain
- with_test_routes do
- get '/mobile'
- verify_redirect 'http://mobile.example.com/mobile'
- end
- end
-
- def test_redirect_hash_with_domain_and_path
- with_test_routes do
- get '/documentation'
- verify_redirect 'http://www.example-documentation.com'
- end
- end
-
- def test_redirect_hash_with_path
- with_test_routes do
- get '/new_documentation'
- verify_redirect 'http://www.example.com/documentation/new'
- end
+ get '/mobile'
+ verify_redirect 'http://mobile.example.com/mobile'
end
def test_redirect_hash_with_host
- with_test_routes do
- get '/super_new_documentation?section=top'
- verify_redirect 'http://super-docs.com/super_new_documentation?section=top'
- end
- end
-
- def test_redirect_hash_path_substitution
- with_test_routes do
- get '/stores/iernest'
- verify_redirect 'http://stores.example.com/iernest'
- end
- end
-
- def test_redirect_hash_path_substitution_with_catch_all
- with_test_routes do
- get '/stores/iernest/products'
- verify_redirect 'http://stores.example.com/iernest/products'
- end
+ get '/super_new_documentation?section=top'
+ verify_redirect 'http://super-docs.com/super_new_documentation?section=top'
end
def test_redirect_class
- with_test_routes do
- get '/youtube_favorites/oHg5SJYRHA0/rick-rolld'
- verify_redirect 'http://www.youtube.com/watch?v=oHg5SJYRHA0'
- end
+ get '/youtube_favorites/oHg5SJYRHA0/rick-rolld'
+ verify_redirect 'http://www.youtube.com/watch?v=oHg5SJYRHA0'
end
def test_openid
- with_test_routes do
- get '/openid/login'
- assert_equal 'openid#login', @response.body
+ get '/openid/login'
+ assert_equal 'openid#login', @response.body
- post '/openid/login'
- assert_equal 'openid#login', @response.body
- end
+ post '/openid/login'
+ assert_equal 'openid#login', @response.body
end
def test_bookmarks
- with_test_routes do
- get '/bookmark/build'
- assert_equal 'bookmarks#new', @response.body
- assert_equal '/bookmark/build', bookmark_new_path
-
- post '/bookmark/create'
- assert_equal 'bookmarks#create', @response.body
- assert_equal '/bookmark/create', bookmark_path
-
- put '/bookmark/update'
- assert_equal 'bookmarks#update', @response.body
- assert_equal '/bookmark/update', bookmark_update_path
-
- get '/bookmark/remove'
- assert_equal 'bookmarks#destroy', @response.body
- assert_equal '/bookmark/remove', bookmark_remove_path
- end
+ get '/bookmark/build'
+ assert_equal 'bookmarks#new', @response.body
+ assert_equal '/bookmark/build', bookmark_new_path
+
+ post '/bookmark/create'
+ assert_equal 'bookmarks#create', @response.body
+ assert_equal '/bookmark/create', bookmark_path
+
+ put '/bookmark/update'
+ assert_equal 'bookmarks#update', @response.body
+ assert_equal '/bookmark/update', bookmark_update_path
+
+ get '/bookmark/remove'
+ assert_equal 'bookmarks#destroy', @response.body
+ assert_equal '/bookmark/remove', bookmark_remove_path
end
def test_pagemarks
- with_test_routes do
- get '/pagemark/build'
- assert_equal 'pagemarks#new', @response.body
- assert_equal '/pagemark/build', pagemark_new_path
-
- post '/pagemark/create'
- assert_equal 'pagemarks#create', @response.body
- assert_equal '/pagemark/create', pagemark_path
-
- put '/pagemark/update'
- assert_equal 'pagemarks#update', @response.body
- assert_equal '/pagemark/update', pagemark_update_path
-
- get '/pagemark/remove'
- assert_equal 'pagemarks#destroy', @response.body
- assert_equal '/pagemark/remove', pagemark_remove_path
- end
+ get '/pagemark/build'
+ assert_equal 'pagemarks#new', @response.body
+ assert_equal '/pagemark/build', pagemark_new_path
+
+ post '/pagemark/create'
+ assert_equal 'pagemarks#create', @response.body
+ assert_equal '/pagemark/create', pagemark_path
+
+ put '/pagemark/update'
+ assert_equal 'pagemarks#update', @response.body
+ assert_equal '/pagemark/update', pagemark_update_path
+
+ get '/pagemark/remove'
+ assert_equal 'pagemarks#destroy', @response.body
+ assert_equal '/pagemark/remove', pagemark_remove_path
end
def test_admin
- with_test_routes do
- get '/admin', {}, {'REMOTE_ADDR' => '192.168.1.100'}
- assert_equal 'queenbee#index', @response.body
+ get '/admin', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'queenbee#index', @response.body
- get '/admin', {}, {'REMOTE_ADDR' => '10.0.0.100'}
- assert_equal 'pass', @response.headers['X-Cascade']
+ get '/admin', {}, {'REMOTE_ADDR' => '10.0.0.100'}
+ assert_equal 'pass', @response.headers['X-Cascade']
- get '/admin/accounts', {}, {'REMOTE_ADDR' => '192.168.1.100'}
- assert_equal 'queenbee#accounts', @response.body
+ get '/admin/accounts', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'queenbee#accounts', @response.body
- get '/admin/accounts', {}, {'REMOTE_ADDR' => '10.0.0.100'}
- assert_equal 'pass', @response.headers['X-Cascade']
+ get '/admin/accounts', {}, {'REMOTE_ADDR' => '10.0.0.100'}
+ assert_equal 'pass', @response.headers['X-Cascade']
- get '/admin/passwords', {}, {'REMOTE_ADDR' => '192.168.1.100'}
- assert_equal 'queenbee#passwords', @response.body
+ get '/admin/passwords', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'queenbee#passwords', @response.body
- get '/admin/passwords', {}, {'REMOTE_ADDR' => '10.0.0.100'}
- assert_equal 'pass', @response.headers['X-Cascade']
- end
+ get '/admin/passwords', {}, {'REMOTE_ADDR' => '10.0.0.100'}
+ assert_equal 'pass', @response.headers['X-Cascade']
end
def test_global
- with_test_routes do
- get '/global/dashboard'
- assert_equal 'global#dashboard', @response.body
+ get '/global/dashboard'
+ assert_equal 'global#dashboard', @response.body
- get '/global/export'
- assert_equal 'global#export', @response.body
+ get '/global/export'
+ assert_equal 'global#export', @response.body
- get '/global/hide_notice'
- assert_equal 'global#hide_notice', @response.body
+ get '/global/hide_notice'
+ assert_equal 'global#hide_notice', @response.body
- get '/export/123/foo.txt'
- assert_equal 'global#export', @response.body
+ get '/export/123/foo.txt'
+ assert_equal 'global#export', @response.body
- assert_equal '/global/export', export_request_path
- assert_equal '/global/hide_notice', global_hide_notice_path
- assert_equal '/export/123/foo.txt', export_download_path(:id => 123, :file => 'foo.txt')
- end
+ assert_equal '/global/export', export_request_path
+ assert_equal '/global/hide_notice', global_hide_notice_path
+ assert_equal '/export/123/foo.txt', export_download_path(:id => 123, :file => 'foo.txt')
end
def test_local
- with_test_routes do
- get '/local/dashboard'
- assert_equal 'local#dashboard', @response.body
- end
+ get '/local/dashboard'
+ assert_equal 'local#dashboard', @response.body
end
# tests the use of dup in url_for
@@ -876,648 +809,553 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
def test_projects_status
- with_test_routes do
- assert_equal '/projects/status', url_for(:controller => 'projects', :action => 'status', :only_path => true)
- assert_equal '/projects/status.json', url_for(:controller => 'projects', :action => 'status', :format => 'json', :only_path => true)
- end
+ assert_equal '/projects/status', url_for(:controller => 'projects', :action => 'status', :only_path => true)
+ assert_equal '/projects/status.json', url_for(:controller => 'projects', :action => 'status', :format => 'json', :only_path => true)
end
def test_projects
- with_test_routes do
- get '/projects'
- assert_equal 'project#index', @response.body
- assert_equal '/projects', projects_path
+ get '/projects'
+ assert_equal 'project#index', @response.body
+ assert_equal '/projects', projects_path
- post '/projects'
- assert_equal 'project#create', @response.body
+ post '/projects'
+ assert_equal 'project#create', @response.body
- get '/projects.xml'
- assert_equal 'project#index', @response.body
- assert_equal '/projects.xml', projects_path(:format => 'xml')
+ get '/projects.xml'
+ assert_equal 'project#index', @response.body
+ assert_equal '/projects.xml', projects_path(:format => 'xml')
- get '/projects/new'
- assert_equal 'project#new', @response.body
- assert_equal '/projects/new', new_project_path
+ get '/projects/new'
+ assert_equal 'project#new', @response.body
+ assert_equal '/projects/new', new_project_path
- get '/projects/new.xml'
- assert_equal 'project#new', @response.body
- assert_equal '/projects/new.xml', new_project_path(:format => 'xml')
+ get '/projects/new.xml'
+ assert_equal 'project#new', @response.body
+ assert_equal '/projects/new.xml', new_project_path(:format => 'xml')
- get '/projects/1'
- assert_equal 'project#show', @response.body
- assert_equal '/projects/1', project_path(:id => '1')
+ get '/projects/1'
+ assert_equal 'project#show', @response.body
+ assert_equal '/projects/1', project_path(:id => '1')
- get '/projects/1.xml'
- assert_equal 'project#show', @response.body
- assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml')
+ get '/projects/1.xml'
+ assert_equal 'project#show', @response.body
+ assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml')
- get '/projects/1/edit'
- assert_equal 'project#edit', @response.body
- assert_equal '/projects/1/edit', edit_project_path(:id => '1')
- end
+ get '/projects/1/edit'
+ assert_equal 'project#edit', @response.body
+ assert_equal '/projects/1/edit', edit_project_path(:id => '1')
end
def test_projects_involvements
- with_test_routes do
- get '/projects/1/involvements'
- assert_equal 'involvements#index', @response.body
- assert_equal '/projects/1/involvements', project_involvements_path(:project_id => '1')
+ get '/projects/1/involvements'
+ assert_equal 'involvements#index', @response.body
+ assert_equal '/projects/1/involvements', project_involvements_path(:project_id => '1')
- get '/projects/1/involvements/new'
- assert_equal 'involvements#new', @response.body
- assert_equal '/projects/1/involvements/new', new_project_involvement_path(:project_id => '1')
+ get '/projects/1/involvements/new'
+ assert_equal 'involvements#new', @response.body
+ assert_equal '/projects/1/involvements/new', new_project_involvement_path(:project_id => '1')
- get '/projects/1/involvements/1'
- assert_equal 'involvements#show', @response.body
- assert_equal '/projects/1/involvements/1', project_involvement_path(:project_id => '1', :id => '1')
+ get '/projects/1/involvements/1'
+ assert_equal 'involvements#show', @response.body
+ assert_equal '/projects/1/involvements/1', project_involvement_path(:project_id => '1', :id => '1')
- put '/projects/1/involvements/1'
- assert_equal 'involvements#update', @response.body
+ put '/projects/1/involvements/1'
+ assert_equal 'involvements#update', @response.body
- delete '/projects/1/involvements/1'
- assert_equal 'involvements#destroy', @response.body
+ delete '/projects/1/involvements/1'
+ assert_equal 'involvements#destroy', @response.body
- get '/projects/1/involvements/1/edit'
- assert_equal 'involvements#edit', @response.body
- assert_equal '/projects/1/involvements/1/edit', edit_project_involvement_path(:project_id => '1', :id => '1')
- end
+ get '/projects/1/involvements/1/edit'
+ assert_equal 'involvements#edit', @response.body
+ assert_equal '/projects/1/involvements/1/edit', edit_project_involvement_path(:project_id => '1', :id => '1')
end
def test_projects_attachments
- with_test_routes do
- get '/projects/1/attachments'
- assert_equal 'attachments#index', @response.body
- assert_equal '/projects/1/attachments', project_attachments_path(:project_id => '1')
- end
+ get '/projects/1/attachments'
+ assert_equal 'attachments#index', @response.body
+ assert_equal '/projects/1/attachments', project_attachments_path(:project_id => '1')
end
def test_projects_participants
- with_test_routes do
- get '/projects/1/participants'
- assert_equal 'participants#index', @response.body
- assert_equal '/projects/1/participants', project_participants_path(:project_id => '1')
-
- put '/projects/1/participants/update_all'
- assert_equal 'participants#update_all', @response.body
- assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1')
- end
+ get '/projects/1/participants'
+ assert_equal 'participants#index', @response.body
+ assert_equal '/projects/1/participants', project_participants_path(:project_id => '1')
+
+ put '/projects/1/participants/update_all'
+ assert_equal 'participants#update_all', @response.body
+ assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1')
end
def test_projects_companies
- with_test_routes do
- get '/projects/1/companies'
- assert_equal 'companies#index', @response.body
- assert_equal '/projects/1/companies', project_companies_path(:project_id => '1')
-
- get '/projects/1/companies/1/people'
- assert_equal 'people#index', @response.body
- assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1')
-
- get '/projects/1/companies/1/avatar'
- assert_equal 'avatar#show', @response.body
- assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1')
- end
+ get '/projects/1/companies'
+ assert_equal 'companies#index', @response.body
+ assert_equal '/projects/1/companies', project_companies_path(:project_id => '1')
+
+ get '/projects/1/companies/1/people'
+ assert_equal 'people#index', @response.body
+ assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1')
+
+ get '/projects/1/companies/1/avatar'
+ assert_equal 'avatar#show', @response.body
+ assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1')
end
def test_project_manager
- with_test_routes do
- get '/projects/1/manager'
- assert_equal 'managers#show', @response.body
- assert_equal '/projects/1/manager', project_super_manager_path(:project_id => '1')
-
- get '/projects/1/manager/new'
- assert_equal 'managers#new', @response.body
- assert_equal '/projects/1/manager/new', new_project_super_manager_path(:project_id => '1')
-
- post '/projects/1/manager/fire'
- assert_equal 'managers#fire', @response.body
- assert_equal '/projects/1/manager/fire', fire_project_super_manager_path(:project_id => '1')
- end
+ get '/projects/1/manager'
+ assert_equal 'managers#show', @response.body
+ assert_equal '/projects/1/manager', project_super_manager_path(:project_id => '1')
+
+ get '/projects/1/manager/new'
+ assert_equal 'managers#new', @response.body
+ assert_equal '/projects/1/manager/new', new_project_super_manager_path(:project_id => '1')
+
+ post '/projects/1/manager/fire'
+ assert_equal 'managers#fire', @response.body
+ assert_equal '/projects/1/manager/fire', fire_project_super_manager_path(:project_id => '1')
end
def test_project_images
- with_test_routes do
- get '/projects/1/images'
- assert_equal 'images#index', @response.body
- assert_equal '/projects/1/images', project_funny_images_path(:project_id => '1')
-
- get '/projects/1/images/new'
- assert_equal 'images#new', @response.body
- assert_equal '/projects/1/images/new', new_project_funny_image_path(:project_id => '1')
-
- post '/projects/1/images/1/revise'
- assert_equal 'images#revise', @response.body
- assert_equal '/projects/1/images/1/revise', revise_project_funny_image_path(:project_id => '1', :id => '1')
- end
+ get '/projects/1/images'
+ assert_equal 'images#index', @response.body
+ assert_equal '/projects/1/images', project_funny_images_path(:project_id => '1')
+
+ get '/projects/1/images/new'
+ assert_equal 'images#new', @response.body
+ assert_equal '/projects/1/images/new', new_project_funny_image_path(:project_id => '1')
+
+ post '/projects/1/images/1/revise'
+ assert_equal 'images#revise', @response.body
+ assert_equal '/projects/1/images/1/revise', revise_project_funny_image_path(:project_id => '1', :id => '1')
end
def test_projects_people
- with_test_routes do
- get '/projects/1/people'
- assert_equal 'people#index', @response.body
- assert_equal '/projects/1/people', project_people_path(:project_id => '1')
-
- get '/projects/1/people/1'
- assert_equal 'people#show', @response.body
- assert_equal '/projects/1/people/1', project_person_path(:project_id => '1', :id => '1')
-
- get '/projects/1/people/1/7a2dec8/avatar'
- assert_equal 'avatars#show', @response.body
- assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8')
-
- put '/projects/1/people/1/accessible_projects'
- assert_equal 'people#accessible_projects', @response.body
- assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1')
-
- post '/projects/1/people/1/resend'
- assert_equal 'people#resend', @response.body
- assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1')
-
- post '/projects/1/people/1/generate_new_password'
- assert_equal 'people#generate_new_password', @response.body
- assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1')
- end
+ get '/projects/1/people'
+ assert_equal 'people#index', @response.body
+ assert_equal '/projects/1/people', project_people_path(:project_id => '1')
+
+ get '/projects/1/people/1'
+ assert_equal 'people#show', @response.body
+ assert_equal '/projects/1/people/1', project_person_path(:project_id => '1', :id => '1')
+
+ get '/projects/1/people/1/7a2dec8/avatar'
+ assert_equal 'avatars#show', @response.body
+ assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8')
+
+ put '/projects/1/people/1/accessible_projects'
+ assert_equal 'people#accessible_projects', @response.body
+ assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1')
+
+ post '/projects/1/people/1/resend'
+ assert_equal 'people#resend', @response.body
+ assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1')
+
+ post '/projects/1/people/1/generate_new_password'
+ assert_equal 'people#generate_new_password', @response.body
+ assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1')
end
def test_projects_with_resources_path_names
- with_test_routes do
- get '/projects/info_about_correlation_indexes'
- assert_equal 'project#correlation_indexes', @response.body
- assert_equal '/projects/info_about_correlation_indexes', correlation_indexes_projects_path
- end
+ get '/projects/info_about_correlation_indexes'
+ assert_equal 'project#correlation_indexes', @response.body
+ assert_equal '/projects/info_about_correlation_indexes', correlation_indexes_projects_path
end
def test_projects_posts
- with_test_routes do
- get '/projects/1/posts'
- assert_equal 'posts#index', @response.body
- assert_equal '/projects/1/posts', project_posts_path(:project_id => '1')
-
- get '/projects/1/posts/archive'
- assert_equal 'posts#archive', @response.body
- assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1')
-
- get '/projects/1/posts/toggle_view'
- assert_equal 'posts#toggle_view', @response.body
- assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1')
-
- post '/projects/1/posts/1/preview'
- assert_equal 'posts#preview', @response.body
- assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1')
-
- get '/projects/1/posts/1/subscription'
- assert_equal 'subscriptions#show', @response.body
- assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1')
-
- get '/projects/1/posts/1/comments'
- assert_equal 'comments#index', @response.body
- assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1')
-
- post '/projects/1/posts/1/comments/preview'
- assert_equal 'comments#preview', @response.body
- assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1')
- end
+ get '/projects/1/posts'
+ assert_equal 'posts#index', @response.body
+ assert_equal '/projects/1/posts', project_posts_path(:project_id => '1')
+
+ get '/projects/1/posts/archive'
+ assert_equal 'posts#archive', @response.body
+ assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1')
+
+ get '/projects/1/posts/toggle_view'
+ assert_equal 'posts#toggle_view', @response.body
+ assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1')
+
+ post '/projects/1/posts/1/preview'
+ assert_equal 'posts#preview', @response.body
+ assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1')
+
+ get '/projects/1/posts/1/subscription'
+ assert_equal 'subscriptions#show', @response.body
+ assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1')
+
+ get '/projects/1/posts/1/comments'
+ assert_equal 'comments#index', @response.body
+ assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1')
+
+ post '/projects/1/posts/1/comments/preview'
+ assert_equal 'comments#preview', @response.body
+ assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1')
end
def test_replies
- with_test_routes do
- put '/replies/1/answer'
- assert_equal 'replies#mark_as_answer', @response.body
+ put '/replies/1/answer'
+ assert_equal 'replies#mark_as_answer', @response.body
- delete '/replies/1/answer'
- assert_equal 'replies#unmark_as_answer', @response.body
- end
+ delete '/replies/1/answer'
+ assert_equal 'replies#unmark_as_answer', @response.body
end
def test_resource_routes_with_only_and_except
- with_test_routes do
- get '/posts'
- assert_equal 'posts#index', @response.body
- assert_equal '/posts', posts_path
-
- get '/posts/1'
- assert_equal 'posts#show', @response.body
- assert_equal '/posts/1', post_path(:id => 1)
-
- get '/posts/1/comments'
- assert_equal 'comments#index', @response.body
- assert_equal '/posts/1/comments', post_comments_path(:post_id => 1)
-
- post '/posts'
- assert_equal 'pass', @response.headers['X-Cascade']
- put '/posts/1'
- assert_equal 'pass', @response.headers['X-Cascade']
- delete '/posts/1'
- assert_equal 'pass', @response.headers['X-Cascade']
- delete '/posts/1/comments'
- assert_equal 'pass', @response.headers['X-Cascade']
- end
+ get '/posts'
+ assert_equal 'posts#index', @response.body
+ assert_equal '/posts', posts_path
+
+ get '/posts/1'
+ assert_equal 'posts#show', @response.body
+ assert_equal '/posts/1', post_path(:id => 1)
+
+ get '/posts/1/comments'
+ assert_equal 'comments#index', @response.body
+ assert_equal '/posts/1/comments', post_comments_path(:post_id => 1)
+
+ post '/posts'
+ assert_equal 'pass', @response.headers['X-Cascade']
+ put '/posts/1'
+ assert_equal 'pass', @response.headers['X-Cascade']
+ delete '/posts/1'
+ assert_equal 'pass', @response.headers['X-Cascade']
+ delete '/posts/1/comments'
+ assert_equal 'pass', @response.headers['X-Cascade']
end
def test_resource_routes_only_create_update_destroy
- with_test_routes do
- delete '/past'
- assert_equal 'pasts#destroy', @response.body
- assert_equal '/past', past_path
-
- put '/present'
- assert_equal 'presents#update', @response.body
- assert_equal '/present', present_path
-
- post '/future'
- assert_equal 'futures#create', @response.body
- assert_equal '/future', future_path
- end
+ delete '/past'
+ assert_equal 'pasts#destroy', @response.body
+ assert_equal '/past', past_path
+
+ put '/present'
+ assert_equal 'presents#update', @response.body
+ assert_equal '/present', present_path
+
+ post '/future'
+ assert_equal 'futures#create', @response.body
+ assert_equal '/future', future_path
end
def test_resources_routes_only_create_update_destroy
- with_test_routes do
- post '/relationships'
- assert_equal 'relationships#create', @response.body
- assert_equal '/relationships', relationships_path
-
- delete '/relationships/1'
- assert_equal 'relationships#destroy', @response.body
- assert_equal '/relationships/1', relationship_path(1)
-
- put '/friendships/1'
- assert_equal 'friendships#update', @response.body
- assert_equal '/friendships/1', friendship_path(1)
- end
+ post '/relationships'
+ assert_equal 'relationships#create', @response.body
+ assert_equal '/relationships', relationships_path
+
+ delete '/relationships/1'
+ assert_equal 'relationships#destroy', @response.body
+ assert_equal '/relationships/1', relationship_path(1)
+
+ put '/friendships/1'
+ assert_equal 'friendships#update', @response.body
+ assert_equal '/friendships/1', friendship_path(1)
end
def test_resource_with_slugs_in_ids
- with_test_routes do
- get '/posts/rails-rocks'
- assert_equal 'posts#show', @response.body
- assert_equal '/posts/rails-rocks', post_path(:id => 'rails-rocks')
- end
+ get '/posts/rails-rocks'
+ assert_equal 'posts#show', @response.body
+ assert_equal '/posts/rails-rocks', post_path(:id => 'rails-rocks')
end
def test_resources_for_uncountable_names
- with_test_routes do
- assert_equal '/sheep', sheep_index_path
- assert_equal '/sheep/1', sheep_path(1)
- assert_equal '/sheep/new', new_sheep_path
- assert_equal '/sheep/1/edit', edit_sheep_path(1)
- assert_equal '/sheep/1/_it', _it_sheep_path(1)
- end
+ assert_equal '/sheep', sheep_index_path
+ assert_equal '/sheep/1', sheep_path(1)
+ assert_equal '/sheep/new', new_sheep_path
+ assert_equal '/sheep/1/edit', edit_sheep_path(1)
+ assert_equal '/sheep/1/_it', _it_sheep_path(1)
end
def test_path_names
- with_test_routes do
- get '/pt/projetos'
- assert_equal 'projects#index', @response.body
- assert_equal '/pt/projetos', pt_projects_path
-
- get '/pt/projetos/1/editar'
- assert_equal 'projects#edit', @response.body
- assert_equal '/pt/projetos/1/editar', edit_pt_project_path(1)
-
- get '/pt/administrador'
- assert_equal 'admins#show', @response.body
- assert_equal '/pt/administrador', pt_admin_path
-
- get '/pt/administrador/novo'
- assert_equal 'admins#new', @response.body
- assert_equal '/pt/administrador/novo', new_pt_admin_path
-
- put '/pt/administrador/ativar'
- assert_equal 'admins#activate', @response.body
- assert_equal '/pt/administrador/ativar', activate_pt_admin_path
- end
+ get '/pt/projetos'
+ assert_equal 'projects#index', @response.body
+ assert_equal '/pt/projetos', pt_projects_path
+
+ get '/pt/projetos/1/editar'
+ assert_equal 'projects#edit', @response.body
+ assert_equal '/pt/projetos/1/editar', edit_pt_project_path(1)
+
+ get '/pt/administrador'
+ assert_equal 'admins#show', @response.body
+ assert_equal '/pt/administrador', pt_admin_path
+
+ get '/pt/administrador/novo'
+ assert_equal 'admins#new', @response.body
+ assert_equal '/pt/administrador/novo', new_pt_admin_path
+
+ put '/pt/administrador/ativar'
+ assert_equal 'admins#activate', @response.body
+ assert_equal '/pt/administrador/ativar', activate_pt_admin_path
end
def test_path_option_override
- with_test_routes do
- get '/pt/projetos/novo/abrir'
- assert_equal 'projects#open', @response.body
- assert_equal '/pt/projetos/novo/abrir', open_new_pt_project_path
-
- put '/pt/projetos/1/fechar'
- assert_equal 'projects#close', @response.body
- assert_equal '/pt/projetos/1/fechar', close_pt_project_path(1)
- end
+ get '/pt/projetos/novo/abrir'
+ assert_equal 'projects#open', @response.body
+ assert_equal '/pt/projetos/novo/abrir', open_new_pt_project_path
+
+ put '/pt/projetos/1/fechar'
+ assert_equal 'projects#close', @response.body
+ assert_equal '/pt/projetos/1/fechar', close_pt_project_path(1)
end
def test_sprockets
- with_test_routes do
- get '/sprockets.js'
- assert_equal 'javascripts', @response.body
- end
+ get '/sprockets.js'
+ assert_equal 'javascripts', @response.body
end
def test_update_person_route
- with_test_routes do
- get '/people/1/update'
- assert_equal 'people#update', @response.body
+ get '/people/1/update'
+ assert_equal 'people#update', @response.body
- assert_equal '/people/1/update', update_person_path(:id => 1)
- end
+ assert_equal '/people/1/update', update_person_path(:id => 1)
end
def test_update_project_person
- with_test_routes do
- get '/projects/1/people/2/update'
- assert_equal 'people#update', @response.body
+ get '/projects/1/people/2/update'
+ assert_equal 'people#update', @response.body
- assert_equal '/projects/1/people/2/update', update_project_person_path(:project_id => 1, :id => 2)
- end
+ assert_equal '/projects/1/people/2/update', update_project_person_path(:project_id => 1, :id => 2)
end
def test_forum_products
- with_test_routes do
- get '/forum'
- assert_equal 'forum/products#index', @response.body
- assert_equal '/forum', forum_products_path
-
- get '/forum/basecamp'
- assert_equal 'forum/products#show', @response.body
- assert_equal '/forum/basecamp', forum_product_path(:id => 'basecamp')
-
- get '/forum/basecamp/questions'
- assert_equal 'forum/questions#index', @response.body
- assert_equal '/forum/basecamp/questions', forum_product_questions_path(:product_id => 'basecamp')
-
- get '/forum/basecamp/questions/1'
- assert_equal 'forum/questions#show', @response.body
- assert_equal '/forum/basecamp/questions/1', forum_product_question_path(:product_id => 'basecamp', :id => 1)
- end
+ get '/forum'
+ assert_equal 'forum/products#index', @response.body
+ assert_equal '/forum', forum_products_path
+
+ get '/forum/basecamp'
+ assert_equal 'forum/products#show', @response.body
+ assert_equal '/forum/basecamp', forum_product_path(:id => 'basecamp')
+
+ get '/forum/basecamp/questions'
+ assert_equal 'forum/questions#index', @response.body
+ assert_equal '/forum/basecamp/questions', forum_product_questions_path(:product_id => 'basecamp')
+
+ get '/forum/basecamp/questions/1'
+ assert_equal 'forum/questions#show', @response.body
+ assert_equal '/forum/basecamp/questions/1', forum_product_question_path(:product_id => 'basecamp', :id => 1)
end
def test_articles_perma
- with_test_routes do
- get '/articles/2009/08/18/rails-3'
- assert_equal 'articles#show', @response.body
+ get '/articles/2009/08/18/rails-3'
+ assert_equal 'articles#show', @response.body
- assert_equal '/articles/2009/8/18/rails-3', article_path(:year => 2009, :month => 8, :day => 18, :title => 'rails-3')
- end
+ assert_equal '/articles/2009/8/18/rails-3', article_path(:year => 2009, :month => 8, :day => 18, :title => 'rails-3')
end
def test_account_namespace
- with_test_routes do
- get '/account/subscription'
- assert_equal 'account/subscriptions#show', @response.body
- assert_equal '/account/subscription', account_subscription_path
-
- get '/account/credit'
- assert_equal 'account/credits#show', @response.body
- assert_equal '/account/credit', account_credit_path
-
- get '/account/credit_card'
- assert_equal 'account/credit_cards#show', @response.body
- assert_equal '/account/credit_card', account_credit_card_path
- end
+ get '/account/subscription'
+ assert_equal 'account/subscriptions#show', @response.body
+ assert_equal '/account/subscription', account_subscription_path
+
+ get '/account/credit'
+ assert_equal 'account/credits#show', @response.body
+ assert_equal '/account/credit', account_credit_path
+
+ get '/account/credit_card'
+ assert_equal 'account/credit_cards#show', @response.body
+ assert_equal '/account/credit_card', account_credit_card_path
end
def test_nested_namespace
- with_test_routes do
- get '/account/admin/subscription'
- assert_equal 'account/admin/subscriptions#show', @response.body
- assert_equal '/account/admin/subscription', account_admin_subscription_path
- end
+ get '/account/admin/subscription'
+ assert_equal 'account/admin/subscriptions#show', @response.body
+ assert_equal '/account/admin/subscription', account_admin_subscription_path
end
def test_namespace_nested_in_resources
- with_test_routes do
- get '/clients/1/google/account'
- assert_equal '/clients/1/google/account', client_google_account_path(1)
- assert_equal 'google/accounts#show', @response.body
-
- get '/clients/1/google/account/secret/info'
- assert_equal '/clients/1/google/account/secret/info', client_google_account_secret_info_path(1)
- assert_equal 'google/secret/infos#show', @response.body
- end
+ get '/clients/1/google/account'
+ assert_equal '/clients/1/google/account', client_google_account_path(1)
+ assert_equal 'google/accounts#show', @response.body
+
+ get '/clients/1/google/account/secret/info'
+ assert_equal '/clients/1/google/account/secret/info', client_google_account_secret_info_path(1)
+ assert_equal 'google/secret/infos#show', @response.body
end
def test_namespace_with_options
- with_test_routes do
- get '/usuarios'
- assert_equal '/usuarios', users_root_path
- assert_equal 'users/home#index', @response.body
- end
+ get '/usuarios'
+ assert_equal '/usuarios', users_root_path
+ assert_equal 'users/home#index', @response.body
end
def test_articles_with_id
- with_test_routes do
- get '/articles/rails/1'
- assert_equal 'articles#with_id', @response.body
+ get '/articles/rails/1'
+ assert_equal 'articles#with_id', @response.body
- get '/articles/123/1'
- assert_equal 'pass', @response.headers['X-Cascade']
+ get '/articles/123/1'
+ assert_equal 'pass', @response.headers['X-Cascade']
- assert_equal '/articles/rails/1', article_with_title_path(:title => 'rails', :id => 1)
- end
+ assert_equal '/articles/rails/1', article_with_title_path(:title => 'rails', :id => 1)
end
def test_access_token_rooms
- with_test_routes do
- get '/12345/rooms'
- assert_equal 'rooms#index', @response.body
+ get '/12345/rooms'
+ assert_equal 'rooms#index', @response.body
- get '/12345/rooms/1'
- assert_equal 'rooms#show', @response.body
+ get '/12345/rooms/1'
+ assert_equal 'rooms#show', @response.body
- get '/12345/rooms/1/edit'
- assert_equal 'rooms#edit', @response.body
- end
+ get '/12345/rooms/1/edit'
+ assert_equal 'rooms#edit', @response.body
end
def test_root
- with_test_routes do
- assert_equal '/', root_path
- get '/'
- assert_equal 'projects#index', @response.body
- end
+ assert_equal '/', root_path
+ get '/'
+ assert_equal 'projects#index', @response.body
end
def test_index
- with_test_routes do
- assert_equal '/info', info_path
- get '/info'
- assert_equal 'projects#info', @response.body
- end
+ assert_equal '/info', info_path
+ get '/info'
+ assert_equal 'projects#info', @response.body
end
def test_match_shorthand_with_no_scope
- with_test_routes do
- assert_equal '/account/overview', account_overview_path
- get '/account/overview'
- assert_equal 'account#overview', @response.body
- end
+ assert_equal '/account/overview', account_overview_path
+ get '/account/overview'
+ assert_equal 'account#overview', @response.body
end
def test_match_shorthand_inside_namespace
- with_test_routes do
- assert_equal '/account/shorthand', account_shorthand_path
- get '/account/shorthand'
- assert_equal 'account#shorthand', @response.body
- end
+ assert_equal '/account/shorthand', account_shorthand_path
+ get '/account/shorthand'
+ assert_equal 'account#shorthand', @response.body
end
def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper
- with_test_routes do
- assert_equal '/replies', replies_path
- end
+ assert_equal '/replies', replies_path
end
def test_scoped_controller_with_namespace_and_action
- with_test_routes do
- assert_equal '/account/twitter/callback', account_callback_path("twitter")
- get '/account/twitter/callback'
- assert_equal 'account/callbacks#twitter', @response.body
+ assert_equal '/account/twitter/callback', account_callback_path("twitter")
+ get '/account/twitter/callback'
+ assert_equal 'account/callbacks#twitter', @response.body
- get '/account/whatever/callback'
- assert_equal 'Not Found', @response.body
- end
+ get '/account/whatever/callback'
+ assert_equal 'Not Found', @response.body
end
def test_convention_match_nested_and_with_leading_slash
- with_test_routes do
- assert_equal '/account/nested/overview', account_nested_overview_path
- get '/account/nested/overview'
- assert_equal 'account/nested#overview', @response.body
- end
+ assert_equal '/account/nested/overview', account_nested_overview_path
+ get '/account/nested/overview'
+ assert_equal 'account/nested#overview', @response.body
end
def test_convention_with_explicit_end
- with_test_routes do
- get '/sign_in'
- assert_equal 'sessions#new', @response.body
- assert_equal '/sign_in', sign_in_path
- end
+ get '/sign_in'
+ assert_equal 'sessions#new', @response.body
+ assert_equal '/sign_in', sign_in_path
end
def test_redirect_with_complete_url_and_status
- with_test_routes do
- get '/account/google'
- verify_redirect 'http://www.google.com/', 302
- end
+ get '/account/google'
+ verify_redirect 'http://www.google.com/', 302
end
def test_redirect_with_port
previous_host, self.host = self.host, 'www.example.com:3000'
- with_test_routes do
- get '/account/login'
- verify_redirect 'http://www.example.com:3000/login'
- end
+
+ get '/account/login'
+ verify_redirect 'http://www.example.com:3000/login'
ensure
self.host = previous_host
end
def test_normalize_namespaced_matches
- with_test_routes do
- assert_equal '/account/description', account_description_path
+ assert_equal '/account/description', account_description_path
- get '/account/description'
- assert_equal 'account#description', @response.body
- end
+ get '/account/description'
+ assert_equal 'account#description', @response.body
end
def test_namespaced_roots
- with_test_routes do
- assert_equal '/account', account_root_path
- get '/account'
- assert_equal 'account/account#index', @response.body
- end
+ assert_equal '/account', account_root_path
+ get '/account'
+ assert_equal 'account/account#index', @response.body
end
def test_optional_scoped_root
- with_test_routes do
- assert_equal '/en', root_path("en")
- get '/en'
- assert_equal 'projects#index', @response.body
- end
+ assert_equal '/en', root_path("en")
+ get '/en'
+ assert_equal 'projects#index', @response.body
end
def test_optional_scoped_path
- with_test_routes do
- assert_equal '/en/descriptions', descriptions_path("en")
- assert_equal '/descriptions', descriptions_path(nil)
- assert_equal '/en/descriptions/1', description_path("en", 1)
- assert_equal '/descriptions/1', description_path(nil, 1)
+ assert_equal '/en/descriptions', descriptions_path("en")
+ assert_equal '/descriptions', descriptions_path(nil)
+ assert_equal '/en/descriptions/1', description_path("en", 1)
+ assert_equal '/descriptions/1', description_path(nil, 1)
- get '/en/descriptions'
- assert_equal 'descriptions#index', @response.body
+ get '/en/descriptions'
+ assert_equal 'descriptions#index', @response.body
- get '/descriptions'
- assert_equal 'descriptions#index', @response.body
+ get '/descriptions'
+ assert_equal 'descriptions#index', @response.body
- get '/en/descriptions/1'
- assert_equal 'descriptions#show', @response.body
+ get '/en/descriptions/1'
+ assert_equal 'descriptions#show', @response.body
- get '/descriptions/1'
- assert_equal 'descriptions#show', @response.body
- end
+ get '/descriptions/1'
+ assert_equal 'descriptions#show', @response.body
end
def test_nested_optional_scoped_path
- with_test_routes do
- assert_equal '/admin/en/descriptions', admin_descriptions_path("en")
- assert_equal '/admin/descriptions', admin_descriptions_path(nil)
- assert_equal '/admin/en/descriptions/1', admin_description_path("en", 1)
- assert_equal '/admin/descriptions/1', admin_description_path(nil, 1)
+ assert_equal '/admin/en/descriptions', admin_descriptions_path("en")
+ assert_equal '/admin/descriptions', admin_descriptions_path(nil)
+ assert_equal '/admin/en/descriptions/1', admin_description_path("en", 1)
+ assert_equal '/admin/descriptions/1', admin_description_path(nil, 1)
- get '/admin/en/descriptions'
- assert_equal 'admin/descriptions#index', @response.body
+ get '/admin/en/descriptions'
+ assert_equal 'admin/descriptions#index', @response.body
- get '/admin/descriptions'
- assert_equal 'admin/descriptions#index', @response.body
+ get '/admin/descriptions'
+ assert_equal 'admin/descriptions#index', @response.body
- get '/admin/en/descriptions/1'
- assert_equal 'admin/descriptions#show', @response.body
+ get '/admin/en/descriptions/1'
+ assert_equal 'admin/descriptions#show', @response.body
- get '/admin/descriptions/1'
- assert_equal 'admin/descriptions#show', @response.body
- end
+ get '/admin/descriptions/1'
+ assert_equal 'admin/descriptions#show', @response.body
end
def test_nested_optional_path_shorthand
- with_test_routes do
- get '/registrations/new'
- assert @request.params[:locale].nil?
+ get '/registrations/new'
+ assert_nil @request.params[:locale]
- get '/en/registrations/new'
- assert 'en', @request.params[:locale]
- end
+ get '/en/registrations/new'
+ assert_equal 'en', @request.params[:locale]
end
def test_default_params
- with_test_routes do
- get '/inline_pages'
- assert_equal 'home', @request.params[:id]
+ get '/inline_pages'
+ assert_equal 'home', @request.params[:id]
- get '/default_pages'
- assert_equal 'home', @request.params[:id]
+ get '/default_pages'
+ assert_equal 'home', @request.params[:id]
- get '/scoped_pages'
- assert_equal 'home', @request.params[:id]
- end
+ get '/scoped_pages'
+ assert_equal 'home', @request.params[:id]
end
def test_resource_constraints
- with_test_routes do
- get '/products/1'
- assert_equal 'pass', @response.headers['X-Cascade']
- get '/products'
- assert_equal 'products#root', @response.body
- get '/products/favorite'
- assert_equal 'products#favorite', @response.body
- get '/products/0001'
- assert_equal 'products#show', @response.body
-
- get '/products/1/images'
- assert_equal 'pass', @response.headers['X-Cascade']
- get '/products/0001/images'
- assert_equal 'images#index', @response.body
- get '/products/0001/images/0001'
- assert_equal 'images#show', @response.body
-
- get '/dashboard', {}, {'REMOTE_ADDR' => '10.0.0.100'}
- assert_equal 'pass', @response.headers['X-Cascade']
- get '/dashboard', {}, {'REMOTE_ADDR' => '192.168.1.100'}
- assert_equal 'dashboards#show', @response.body
- end
+ get '/products/1'
+ assert_equal 'pass', @response.headers['X-Cascade']
+ get '/products'
+ assert_equal 'products#root', @response.body
+ get '/products/favorite'
+ assert_equal 'products#favorite', @response.body
+ get '/products/0001'
+ assert_equal 'products#show', @response.body
+
+ get '/products/1/images'
+ assert_equal 'pass', @response.headers['X-Cascade']
+ get '/products/0001/images'
+ assert_equal 'images#index', @response.body
+ get '/products/0001/images/0001'
+ assert_equal 'images#show', @response.body
+
+ get '/dashboard', {}, {'REMOTE_ADDR' => '10.0.0.100'}
+ assert_equal 'pass', @response.headers['X-Cascade']
+ get '/dashboard', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'dashboards#show', @response.body
end
def test_root_works_in_the_resources_scope
@@ -1527,731 +1365,651 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
def test_module_scope
- with_test_routes do
- get '/token'
- assert_equal 'api/tokens#show', @response.body
- assert_equal '/token', token_path
- end
+ get '/token'
+ assert_equal 'api/tokens#show', @response.body
+ assert_equal '/token', token_path
end
def test_path_scope
- with_test_routes do
- get '/api/me'
- assert_equal 'mes#show', @response.body
- assert_equal '/api/me', me_path
+ get '/api/me'
+ assert_equal 'mes#show', @response.body
+ assert_equal '/api/me', me_path
- get '/api'
- assert_equal 'mes#index', @response.body
- end
+ get '/api'
+ assert_equal 'mes#index', @response.body
end
def test_url_generator_for_generic_route
- with_test_routes do
- get 'whatever/foo/bar'
- assert_equal 'foo#bar', @response.body
+ get 'whatever/foo/bar'
+ assert_equal 'foo#bar', @response.body
- assert_equal 'http://www.example.com/whatever/foo/bar/1',
- url_for(:controller => "foo", :action => "bar", :id => 1)
- end
+ assert_equal 'http://www.example.com/whatever/foo/bar/1',
+ url_for(:controller => "foo", :action => "bar", :id => 1)
end
def test_url_generator_for_namespaced_generic_route
- with_test_routes do
- get 'whatever/foo/bar/show'
- assert_equal 'foo/bar#show', @response.body
+ get 'whatever/foo/bar/show'
+ assert_equal 'foo/bar#show', @response.body
- get 'whatever/foo/bar/show/1'
- assert_equal 'foo/bar#show', @response.body
+ get 'whatever/foo/bar/show/1'
+ assert_equal 'foo/bar#show', @response.body
- assert_equal 'http://www.example.com/whatever/foo/bar/show',
- url_for(:controller => "foo/bar", :action => "show")
+ assert_equal 'http://www.example.com/whatever/foo/bar/show',
+ url_for(:controller => "foo/bar", :action => "show")
- assert_equal 'http://www.example.com/whatever/foo/bar/show/1',
- url_for(:controller => "foo/bar", :action => "show", :id => '1')
- end
+ assert_equal 'http://www.example.com/whatever/foo/bar/show/1',
+ url_for(:controller => "foo/bar", :action => "show", :id => '1')
end
def test_assert_recognizes_account_overview
- with_test_routes do
- assert_recognizes({:controller => "account", :action => "overview"}, "/account/overview")
- end
+ assert_recognizes({:controller => "account", :action => "overview"}, "/account/overview")
end
def test_resource_new_actions
- with_test_routes do
- assert_equal '/replies/new/preview', preview_new_reply_path
- assert_equal '/pt/projetos/novo/preview', preview_new_pt_project_path
- assert_equal '/pt/administrador/novo/preview', preview_new_pt_admin_path
- assert_equal '/pt/products/novo/preview', preview_new_pt_product_path
- assert_equal '/profile/new/preview', preview_new_profile_path
+ assert_equal '/replies/new/preview', preview_new_reply_path
+ assert_equal '/pt/projetos/novo/preview', preview_new_pt_project_path
+ assert_equal '/pt/administrador/novo/preview', preview_new_pt_admin_path
+ assert_equal '/pt/products/novo/preview', preview_new_pt_product_path
+ assert_equal '/profile/new/preview', preview_new_profile_path
- post '/replies/new/preview'
- assert_equal 'replies#preview', @response.body
+ post '/replies/new/preview'
+ assert_equal 'replies#preview', @response.body
- post '/pt/projetos/novo/preview'
- assert_equal 'projects#preview', @response.body
+ post '/pt/projetos/novo/preview'
+ assert_equal 'projects#preview', @response.body
- post '/pt/administrador/novo/preview'
- assert_equal 'admins#preview', @response.body
+ post '/pt/administrador/novo/preview'
+ assert_equal 'admins#preview', @response.body
- post '/pt/products/novo/preview'
- assert_equal 'products#preview', @response.body
+ post '/pt/products/novo/preview'
+ assert_equal 'products#preview', @response.body
- post '/profile/new/preview'
- assert_equal 'profiles#preview', @response.body
- end
+ post '/profile/new/preview'
+ assert_equal 'profiles#preview', @response.body
end
def test_resource_merges_options_from_scope
- with_test_routes do
- assert_raise(NameError) { new_account_path }
+ assert_raise(NameError) { new_account_path }
- get '/account/new'
- assert_equal 404, status
- end
+ get '/account/new'
+ assert_equal 404, status
end
def test_resources_merges_options_from_scope
- with_test_routes do
- assert_raise(NoMethodError) { edit_product_path('1') }
+ assert_raise(NoMethodError) { edit_product_path('1') }
- get '/products/1/edit'
- assert_equal 404, status
+ get '/products/1/edit'
+ assert_equal 404, status
- assert_raise(NoMethodError) { edit_product_image_path('1', '2') }
+ assert_raise(NoMethodError) { edit_product_image_path('1', '2') }
- post '/products/1/images/2/edit'
- assert_equal 404, status
- end
+ post '/products/1/images/2/edit'
+ assert_equal 404, status
end
def test_shallow_nested_resources
- with_test_routes do
+ get '/api/teams'
+ assert_equal 'api/teams#index', @response.body
+ assert_equal '/api/teams', api_teams_path
- get '/api/teams'
- assert_equal 'api/teams#index', @response.body
- assert_equal '/api/teams', api_teams_path
+ get '/api/teams/new'
+ assert_equal 'api/teams#new', @response.body
+ assert_equal '/api/teams/new', new_api_team_path
- get '/api/teams/new'
- assert_equal 'api/teams#new', @response.body
- assert_equal '/api/teams/new', new_api_team_path
+ get '/api/teams/1'
+ assert_equal 'api/teams#show', @response.body
+ assert_equal '/api/teams/1', api_team_path(:id => '1')
- get '/api/teams/1'
- assert_equal 'api/teams#show', @response.body
- assert_equal '/api/teams/1', api_team_path(:id => '1')
+ get '/api/teams/1/edit'
+ assert_equal 'api/teams#edit', @response.body
+ assert_equal '/api/teams/1/edit', edit_api_team_path(:id => '1')
- get '/api/teams/1/edit'
- assert_equal 'api/teams#edit', @response.body
- assert_equal '/api/teams/1/edit', edit_api_team_path(:id => '1')
+ get '/api/teams/1/players'
+ assert_equal 'api/players#index', @response.body
+ assert_equal '/api/teams/1/players', api_team_players_path(:team_id => '1')
- get '/api/teams/1/players'
- assert_equal 'api/players#index', @response.body
- assert_equal '/api/teams/1/players', api_team_players_path(:team_id => '1')
+ get '/api/teams/1/players/new'
+ assert_equal 'api/players#new', @response.body
+ assert_equal '/api/teams/1/players/new', new_api_team_player_path(:team_id => '1')
- get '/api/teams/1/players/new'
- assert_equal 'api/players#new', @response.body
- assert_equal '/api/teams/1/players/new', new_api_team_player_path(:team_id => '1')
+ get '/api/players/2'
+ assert_equal 'api/players#show', @response.body
+ assert_equal '/api/players/2', api_player_path(:id => '2')
- get '/api/players/2'
- assert_equal 'api/players#show', @response.body
- assert_equal '/api/players/2', api_player_path(:id => '2')
+ get '/api/players/2/edit'
+ assert_equal 'api/players#edit', @response.body
+ assert_equal '/api/players/2/edit', edit_api_player_path(:id => '2')
- get '/api/players/2/edit'
- assert_equal 'api/players#edit', @response.body
- assert_equal '/api/players/2/edit', edit_api_player_path(:id => '2')
+ get '/api/teams/1/captain'
+ assert_equal 'api/captains#show', @response.body
+ assert_equal '/api/teams/1/captain', api_team_captain_path(:team_id => '1')
- get '/api/teams/1/captain'
- assert_equal 'api/captains#show', @response.body
- assert_equal '/api/teams/1/captain', api_team_captain_path(:team_id => '1')
+ get '/api/teams/1/captain/new'
+ assert_equal 'api/captains#new', @response.body
+ assert_equal '/api/teams/1/captain/new', new_api_team_captain_path(:team_id => '1')
- get '/api/teams/1/captain/new'
- assert_equal 'api/captains#new', @response.body
- assert_equal '/api/teams/1/captain/new', new_api_team_captain_path(:team_id => '1')
+ get '/api/teams/1/captain/edit'
+ assert_equal 'api/captains#edit', @response.body
+ assert_equal '/api/teams/1/captain/edit', edit_api_team_captain_path(:team_id => '1')
- get '/api/teams/1/captain/edit'
- assert_equal 'api/captains#edit', @response.body
- assert_equal '/api/teams/1/captain/edit', edit_api_team_captain_path(:team_id => '1')
+ get '/threads'
+ assert_equal 'threads#index', @response.body
+ assert_equal '/threads', threads_path
- get '/threads'
- assert_equal 'threads#index', @response.body
- assert_equal '/threads', threads_path
+ get '/threads/new'
+ assert_equal 'threads#new', @response.body
+ assert_equal '/threads/new', new_thread_path
- get '/threads/new'
- assert_equal 'threads#new', @response.body
- assert_equal '/threads/new', new_thread_path
+ get '/threads/1'
+ assert_equal 'threads#show', @response.body
+ assert_equal '/threads/1', thread_path(:id => '1')
- get '/threads/1'
- assert_equal 'threads#show', @response.body
- assert_equal '/threads/1', thread_path(:id => '1')
+ get '/threads/1/edit'
+ assert_equal 'threads#edit', @response.body
+ assert_equal '/threads/1/edit', edit_thread_path(:id => '1')
- get '/threads/1/edit'
- assert_equal 'threads#edit', @response.body
- assert_equal '/threads/1/edit', edit_thread_path(:id => '1')
+ get '/threads/1/owner'
+ assert_equal 'owners#show', @response.body
+ assert_equal '/threads/1/owner', thread_owner_path(:thread_id => '1')
- get '/threads/1/owner'
- assert_equal 'owners#show', @response.body
- assert_equal '/threads/1/owner', thread_owner_path(:thread_id => '1')
+ get '/threads/1/messages'
+ assert_equal 'messages#index', @response.body
+ assert_equal '/threads/1/messages', thread_messages_path(:thread_id => '1')
- get '/threads/1/messages'
- assert_equal 'messages#index', @response.body
- assert_equal '/threads/1/messages', thread_messages_path(:thread_id => '1')
+ get '/threads/1/messages/new'
+ assert_equal 'messages#new', @response.body
+ assert_equal '/threads/1/messages/new', new_thread_message_path(:thread_id => '1')
- get '/threads/1/messages/new'
- assert_equal 'messages#new', @response.body
- assert_equal '/threads/1/messages/new', new_thread_message_path(:thread_id => '1')
+ get '/messages/2'
+ assert_equal 'messages#show', @response.body
+ assert_equal '/messages/2', message_path(:id => '2')
- get '/messages/2'
- assert_equal 'messages#show', @response.body
- assert_equal '/messages/2', message_path(:id => '2')
+ get '/messages/2/edit'
+ assert_equal 'messages#edit', @response.body
+ assert_equal '/messages/2/edit', edit_message_path(:id => '2')
- get '/messages/2/edit'
- assert_equal 'messages#edit', @response.body
- assert_equal '/messages/2/edit', edit_message_path(:id => '2')
+ get '/messages/2/comments'
+ assert_equal 'comments#index', @response.body
+ assert_equal '/messages/2/comments', message_comments_path(:message_id => '2')
- get '/messages/2/comments'
- assert_equal 'comments#index', @response.body
- assert_equal '/messages/2/comments', message_comments_path(:message_id => '2')
+ get '/messages/2/comments/new'
+ assert_equal 'comments#new', @response.body
+ assert_equal '/messages/2/comments/new', new_message_comment_path(:message_id => '2')
- get '/messages/2/comments/new'
- assert_equal 'comments#new', @response.body
- assert_equal '/messages/2/comments/new', new_message_comment_path(:message_id => '2')
+ get '/comments/3'
+ assert_equal 'comments#show', @response.body
+ assert_equal '/comments/3', comment_path(:id => '3')
- get '/comments/3'
- assert_equal 'comments#show', @response.body
- assert_equal '/comments/3', comment_path(:id => '3')
+ get '/comments/3/edit'
+ assert_equal 'comments#edit', @response.body
+ assert_equal '/comments/3/edit', edit_comment_path(:id => '3')
- get '/comments/3/edit'
- assert_equal 'comments#edit', @response.body
- assert_equal '/comments/3/edit', edit_comment_path(:id => '3')
-
- post '/comments/3/preview'
- assert_equal 'comments#preview', @response.body
- assert_equal '/comments/3/preview', preview_comment_path(:id => '3')
- end
+ post '/comments/3/preview'
+ assert_equal 'comments#preview', @response.body
+ assert_equal '/comments/3/preview', preview_comment_path(:id => '3')
end
def test_shallow_nested_resources_within_scope
- with_test_routes do
+ get '/hello/notes/1/trackbacks'
+ assert_equal 'trackbacks#index', @response.body
+ assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1)
- get '/hello/notes/1/trackbacks'
- assert_equal 'trackbacks#index', @response.body
- assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1)
+ get '/hello/notes/1/edit'
+ assert_equal 'notes#edit', @response.body
+ assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1')
- get '/hello/notes/1/edit'
- assert_equal 'notes#edit', @response.body
- assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1')
+ get '/hello/notes/1/trackbacks/new'
+ assert_equal 'trackbacks#new', @response.body
+ assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1)
- get '/hello/notes/1/trackbacks/new'
- assert_equal 'trackbacks#new', @response.body
- assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1)
+ get '/hello/trackbacks/1'
+ assert_equal 'trackbacks#show', @response.body
+ assert_equal '/hello/trackbacks/1', trackback_path(:id => '1')
- get '/hello/trackbacks/1'
- assert_equal 'trackbacks#show', @response.body
- assert_equal '/hello/trackbacks/1', trackback_path(:id => '1')
+ get '/hello/trackbacks/1/edit'
+ assert_equal 'trackbacks#edit', @response.body
+ assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1')
- get '/hello/trackbacks/1/edit'
- assert_equal 'trackbacks#edit', @response.body
- assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1')
+ put '/hello/trackbacks/1'
+ assert_equal 'trackbacks#update', @response.body
- put '/hello/trackbacks/1'
- assert_equal 'trackbacks#update', @response.body
+ post '/hello/notes/1/trackbacks'
+ assert_equal 'trackbacks#create', @response.body
- post '/hello/notes/1/trackbacks'
- assert_equal 'trackbacks#create', @response.body
+ delete '/hello/trackbacks/1'
+ assert_equal 'trackbacks#destroy', @response.body
- delete '/hello/trackbacks/1'
- assert_equal 'trackbacks#destroy', @response.body
+ get '/hello/notes'
+ assert_equal 'notes#index', @response.body
- get '/hello/notes'
- assert_equal 'notes#index', @response.body
+ post '/hello/notes'
+ assert_equal 'notes#create', @response.body
- post '/hello/notes'
- assert_equal 'notes#create', @response.body
+ get '/hello/notes/new'
+ assert_equal 'notes#new', @response.body
+ assert_equal '/hello/notes/new', new_note_path
- get '/hello/notes/new'
- assert_equal 'notes#new', @response.body
- assert_equal '/hello/notes/new', new_note_path
+ get '/hello/notes/1'
+ assert_equal 'notes#show', @response.body
+ assert_equal '/hello/notes/1', note_path(:id => 1)
- get '/hello/notes/1'
- assert_equal 'notes#show', @response.body
- assert_equal '/hello/notes/1', note_path(:id => 1)
+ put '/hello/notes/1'
+ assert_equal 'notes#update', @response.body
- put '/hello/notes/1'
- assert_equal 'notes#update', @response.body
-
- delete '/hello/notes/1'
- assert_equal 'notes#destroy', @response.body
- end
+ delete '/hello/notes/1'
+ assert_equal 'notes#destroy', @response.body
end
def test_custom_resource_routes_are_scoped
- with_test_routes do
- assert_equal '/customers/recent', recent_customers_path
- assert_equal '/customers/1/profile', profile_customer_path(:id => '1')
- assert_equal '/customers/1/secret/profile', secret_profile_customer_path(:id => '1')
- assert_equal '/customers/new/preview', another_preview_new_customer_path
- assert_equal '/customers/1/avatar/thumbnail.jpg', thumbnail_customer_avatar_path(:customer_id => '1', :format => :jpg)
- assert_equal '/customers/1/invoices/outstanding', outstanding_customer_invoices_path(:customer_id => '1')
- assert_equal '/customers/1/invoices/2/print', print_customer_invoice_path(:customer_id => '1', :id => '2')
- assert_equal '/customers/1/invoices/new/preview', preview_new_customer_invoice_path(:customer_id => '1')
- assert_equal '/customers/1/notes/new/preview', preview_new_customer_note_path(:customer_id => '1')
- assert_equal '/notes/1/print', print_note_path(:id => '1')
- assert_equal '/api/customers/recent', recent_api_customers_path
- assert_equal '/api/customers/1/profile', profile_api_customer_path(:id => '1')
- assert_equal '/api/customers/new/preview', preview_new_api_customer_path
-
- get '/customers/1/invoices/overdue'
- assert_equal 'invoices#overdue', @response.body
-
- get '/customers/1/secret/profile'
- assert_equal 'customers#secret', @response.body
- end
+ assert_equal '/customers/recent', recent_customers_path
+ assert_equal '/customers/1/profile', profile_customer_path(:id => '1')
+ assert_equal '/customers/1/secret/profile', secret_profile_customer_path(:id => '1')
+ assert_equal '/customers/new/preview', another_preview_new_customer_path
+ assert_equal '/customers/1/avatar/thumbnail.jpg', thumbnail_customer_avatar_path(:customer_id => '1', :format => :jpg)
+ assert_equal '/customers/1/invoices/outstanding', outstanding_customer_invoices_path(:customer_id => '1')
+ assert_equal '/customers/1/invoices/2/print', print_customer_invoice_path(:customer_id => '1', :id => '2')
+ assert_equal '/customers/1/invoices/new/preview', preview_new_customer_invoice_path(:customer_id => '1')
+ assert_equal '/customers/1/notes/new/preview', preview_new_customer_note_path(:customer_id => '1')
+ assert_equal '/notes/1/print', print_note_path(:id => '1')
+ assert_equal '/api/customers/recent', recent_api_customers_path
+ assert_equal '/api/customers/1/profile', profile_api_customer_path(:id => '1')
+ assert_equal '/api/customers/new/preview', preview_new_api_customer_path
+
+ get '/customers/1/invoices/overdue'
+ assert_equal 'invoices#overdue', @response.body
+
+ get '/customers/1/secret/profile'
+ assert_equal 'customers#secret', @response.body
end
def test_shallow_nested_routes_ignore_module
- with_test_routes do
- get '/errors/1/notices'
- assert_equal 'api/notices#index', @response.body
- assert_equal '/errors/1/notices', error_notices_path(:error_id => '1')
-
- get '/notices/1'
- assert_equal 'api/notices#show', @response.body
- assert_equal '/notices/1', notice_path(:id => '1')
- end
+ get '/errors/1/notices'
+ assert_equal 'api/notices#index', @response.body
+ assert_equal '/errors/1/notices', error_notices_path(:error_id => '1')
+
+ get '/notices/1'
+ assert_equal 'api/notices#show', @response.body
+ assert_equal '/notices/1', notice_path(:id => '1')
end
def test_non_greedy_regexp
- with_test_routes do
- get '/api/1.0/users'
- assert_equal 'api/users#index', @response.body
- assert_equal '/api/1.0/users', api_users_path(:version => '1.0')
-
- get '/api/1.0/users.json'
- assert_equal 'api/users#index', @response.body
- assert_equal true, @request.format.json?
- assert_equal '/api/1.0/users.json', api_users_path(:version => '1.0', :format => :json)
-
- get '/api/1.0/users/first.last'
- assert_equal 'api/users#show', @response.body
- assert_equal 'first.last', @request.params[:id]
- assert_equal '/api/1.0/users/first.last', api_user_path(:version => '1.0', :id => 'first.last')
-
- get '/api/1.0/users/first.last.xml'
- assert_equal 'api/users#show', @response.body
- assert_equal 'first.last', @request.params[:id]
- assert_equal true, @request.format.xml?
- assert_equal '/api/1.0/users/first.last.xml', api_user_path(:version => '1.0', :id => 'first.last', :format => :xml)
- end
+ get '/api/1.0/users'
+ assert_equal 'api/users#index', @response.body
+ assert_equal '/api/1.0/users', api_users_path(:version => '1.0')
+
+ get '/api/1.0/users.json'
+ assert_equal 'api/users#index', @response.body
+ assert_equal true, @request.format.json?
+ assert_equal '/api/1.0/users.json', api_users_path(:version => '1.0', :format => :json)
+
+ get '/api/1.0/users/first.last'
+ assert_equal 'api/users#show', @response.body
+ assert_equal 'first.last', @request.params[:id]
+ assert_equal '/api/1.0/users/first.last', api_user_path(:version => '1.0', :id => 'first.last')
+
+ get '/api/1.0/users/first.last.xml'
+ assert_equal 'api/users#show', @response.body
+ assert_equal 'first.last', @request.params[:id]
+ assert_equal true, @request.format.xml?
+ assert_equal '/api/1.0/users/first.last.xml', api_user_path(:version => '1.0', :id => 'first.last', :format => :xml)
end
def test_glob_parameter_accepts_regexp
- with_test_routes do
- get '/en/path/to/existing/file.html'
- assert_equal 200, @response.status
- end
+ get '/en/path/to/existing/file.html'
+ assert_equal 200, @response.status
end
def test_resources_controller_name_is_not_pluralized
- with_test_routes do
- get '/content'
- assert_equal 'content#index', @response.body
- end
+ get '/content'
+ assert_equal 'content#index', @response.body
end
def test_url_generator_for_optional_prefix_dynamic_segment
- with_test_routes do
- get '/bob/followers'
- assert_equal 'followers#index', @response.body
- assert_equal 'http://www.example.com/bob/followers',
- url_for(:controller => "followers", :action => "index", :username => "bob")
-
- get '/followers'
- assert_equal 'followers#index', @response.body
- assert_equal 'http://www.example.com/followers',
- url_for(:controller => "followers", :action => "index", :username => nil)
- end
+ get '/bob/followers'
+ assert_equal 'followers#index', @response.body
+ assert_equal 'http://www.example.com/bob/followers',
+ url_for(:controller => "followers", :action => "index", :username => "bob")
+
+ get '/followers'
+ assert_equal 'followers#index', @response.body
+ assert_equal 'http://www.example.com/followers',
+ url_for(:controller => "followers", :action => "index", :username => nil)
end
def test_url_generator_for_optional_suffix_static_and_dynamic_segment
- with_test_routes do
- get '/groups/user/bob'
- assert_equal 'groups#index', @response.body
- assert_equal 'http://www.example.com/groups/user/bob',
- url_for(:controller => "groups", :action => "index", :username => "bob")
-
- get '/groups'
- assert_equal 'groups#index', @response.body
- assert_equal 'http://www.example.com/groups',
- url_for(:controller => "groups", :action => "index", :username => nil)
- end
+ get '/groups/user/bob'
+ assert_equal 'groups#index', @response.body
+ assert_equal 'http://www.example.com/groups/user/bob',
+ url_for(:controller => "groups", :action => "index", :username => "bob")
+
+ get '/groups'
+ assert_equal 'groups#index', @response.body
+ assert_equal 'http://www.example.com/groups',
+ url_for(:controller => "groups", :action => "index", :username => nil)
end
def test_url_generator_for_optional_prefix_static_and_dynamic_segment
- with_test_routes do
- get 'user/bob/photos'
- assert_equal 'photos#index', @response.body
- assert_equal 'http://www.example.com/user/bob/photos',
- url_for(:controller => "photos", :action => "index", :username => "bob")
-
- get 'photos'
- assert_equal 'photos#index', @response.body
- assert_equal 'http://www.example.com/photos',
- url_for(:controller => "photos", :action => "index", :username => nil)
- end
+ get 'user/bob/photos'
+ assert_equal 'photos#index', @response.body
+ assert_equal 'http://www.example.com/user/bob/photos',
+ url_for(:controller => "photos", :action => "index", :username => "bob")
+
+ get 'photos'
+ assert_equal 'photos#index', @response.body
+ assert_equal 'http://www.example.com/photos',
+ url_for(:controller => "photos", :action => "index", :username => nil)
end
def test_url_recognition_for_optional_static_segments
- with_test_routes do
- get '/groups/discussions/messages'
- assert_equal 'messages#index', @response.body
+ get '/groups/discussions/messages'
+ assert_equal 'messages#index', @response.body
- get '/groups/discussions/messages/1'
- assert_equal 'messages#show', @response.body
+ get '/groups/discussions/messages/1'
+ assert_equal 'messages#show', @response.body
- get '/groups/messages'
- assert_equal 'messages#index', @response.body
+ get '/groups/messages'
+ assert_equal 'messages#index', @response.body
- get '/groups/messages/1'
- assert_equal 'messages#show', @response.body
+ get '/groups/messages/1'
+ assert_equal 'messages#show', @response.body
- get '/discussions/messages'
- assert_equal 'messages#index', @response.body
+ get '/discussions/messages'
+ assert_equal 'messages#index', @response.body
- get '/discussions/messages/1'
- assert_equal 'messages#show', @response.body
+ get '/discussions/messages/1'
+ assert_equal 'messages#show', @response.body
- get '/messages'
- assert_equal 'messages#index', @response.body
+ get '/messages'
+ assert_equal 'messages#index', @response.body
- get '/messages/1'
- assert_equal 'messages#show', @response.body
- end
+ get '/messages/1'
+ assert_equal 'messages#show', @response.body
end
def test_router_removes_invalid_conditions
- with_test_routes do
- get '/tickets'
- assert_equal 'tickets#index', @response.body
- assert_equal '/tickets', tickets_path
- end
+ get '/tickets'
+ assert_equal 'tickets#index', @response.body
+ assert_equal '/tickets', tickets_path
end
def test_constraints_are_merged_from_scope
- with_test_routes do
- get '/movies/0001'
- assert_equal 'movies#show', @response.body
- assert_equal '/movies/0001', movie_path(:id => '0001')
-
- get '/movies/00001'
- assert_equal 'Not Found', @response.body
- assert_raises(ActionController::RoutingError){ movie_path(:id => '00001') }
-
- get '/movies/0001/reviews'
- assert_equal 'reviews#index', @response.body
- assert_equal '/movies/0001/reviews', movie_reviews_path(:movie_id => '0001')
-
- get '/movies/00001/reviews'
- assert_equal 'Not Found', @response.body
- assert_raises(ActionController::RoutingError){ movie_reviews_path(:movie_id => '00001') }
-
- get '/movies/0001/reviews/0001'
- assert_equal 'reviews#show', @response.body
- assert_equal '/movies/0001/reviews/0001', movie_review_path(:movie_id => '0001', :id => '0001')
-
- get '/movies/00001/reviews/0001'
- assert_equal 'Not Found', @response.body
- assert_raises(ActionController::RoutingError){ movie_path(:movie_id => '00001', :id => '00001') }
-
- get '/movies/0001/trailer'
- assert_equal 'trailers#show', @response.body
- assert_equal '/movies/0001/trailer', movie_trailer_path(:movie_id => '0001')
-
- get '/movies/00001/trailer'
- assert_equal 'Not Found', @response.body
- assert_raises(ActionController::RoutingError){ movie_trailer_path(:movie_id => '00001') }
- end
+ get '/movies/0001'
+ assert_equal 'movies#show', @response.body
+ assert_equal '/movies/0001', movie_path(:id => '0001')
+
+ get '/movies/00001'
+ assert_equal 'Not Found', @response.body
+ assert_raises(ActionController::RoutingError){ movie_path(:id => '00001') }
+
+ get '/movies/0001/reviews'
+ assert_equal 'reviews#index', @response.body
+ assert_equal '/movies/0001/reviews', movie_reviews_path(:movie_id => '0001')
+
+ get '/movies/00001/reviews'
+ assert_equal 'Not Found', @response.body
+ assert_raises(ActionController::RoutingError){ movie_reviews_path(:movie_id => '00001') }
+
+ get '/movies/0001/reviews/0001'
+ assert_equal 'reviews#show', @response.body
+ assert_equal '/movies/0001/reviews/0001', movie_review_path(:movie_id => '0001', :id => '0001')
+
+ get '/movies/00001/reviews/0001'
+ assert_equal 'Not Found', @response.body
+ assert_raises(ActionController::RoutingError){ movie_path(:movie_id => '00001', :id => '00001') }
+
+ get '/movies/0001/trailer'
+ assert_equal 'trailers#show', @response.body
+ assert_equal '/movies/0001/trailer', movie_trailer_path(:movie_id => '0001')
+
+ get '/movies/00001/trailer'
+ assert_equal 'Not Found', @response.body
+ assert_raises(ActionController::RoutingError){ movie_trailer_path(:movie_id => '00001') }
end
def test_only_should_be_read_from_scope
- with_test_routes do
- get '/only/clubs'
- assert_equal 'only/clubs#index', @response.body
- assert_equal '/only/clubs', only_clubs_path
-
- get '/only/clubs/1/edit'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { edit_only_club_path(:id => '1') }
-
- get '/only/clubs/1/players'
- assert_equal 'only/players#index', @response.body
- assert_equal '/only/clubs/1/players', only_club_players_path(:club_id => '1')
-
- get '/only/clubs/1/players/2/edit'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { edit_only_club_player_path(:club_id => '1', :id => '2') }
-
- get '/only/clubs/1/chairman'
- assert_equal 'only/chairmen#show', @response.body
- assert_equal '/only/clubs/1/chairman', only_club_chairman_path(:club_id => '1')
-
- get '/only/clubs/1/chairman/edit'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { edit_only_club_chairman_path(:club_id => '1') }
- end
+ get '/only/clubs'
+ assert_equal 'only/clubs#index', @response.body
+ assert_equal '/only/clubs', only_clubs_path
+
+ get '/only/clubs/1/edit'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { edit_only_club_path(:id => '1') }
+
+ get '/only/clubs/1/players'
+ assert_equal 'only/players#index', @response.body
+ assert_equal '/only/clubs/1/players', only_club_players_path(:club_id => '1')
+
+ get '/only/clubs/1/players/2/edit'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { edit_only_club_player_path(:club_id => '1', :id => '2') }
+
+ get '/only/clubs/1/chairman'
+ assert_equal 'only/chairmen#show', @response.body
+ assert_equal '/only/clubs/1/chairman', only_club_chairman_path(:club_id => '1')
+
+ get '/only/clubs/1/chairman/edit'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { edit_only_club_chairman_path(:club_id => '1') }
end
def test_except_should_be_read_from_scope
- with_test_routes do
- get '/except/clubs'
- assert_equal 'except/clubs#index', @response.body
- assert_equal '/except/clubs', except_clubs_path
-
- get '/except/clubs/1/edit'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { edit_except_club_path(:id => '1') }
-
- get '/except/clubs/1/players'
- assert_equal 'except/players#index', @response.body
- assert_equal '/except/clubs/1/players', except_club_players_path(:club_id => '1')
-
- get '/except/clubs/1/players/2/edit'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { edit_except_club_player_path(:club_id => '1', :id => '2') }
-
- get '/except/clubs/1/chairman'
- assert_equal 'except/chairmen#show', @response.body
- assert_equal '/except/clubs/1/chairman', except_club_chairman_path(:club_id => '1')
-
- get '/except/clubs/1/chairman/edit'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { edit_except_club_chairman_path(:club_id => '1') }
- end
+ get '/except/clubs'
+ assert_equal 'except/clubs#index', @response.body
+ assert_equal '/except/clubs', except_clubs_path
+
+ get '/except/clubs/1/edit'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { edit_except_club_path(:id => '1') }
+
+ get '/except/clubs/1/players'
+ assert_equal 'except/players#index', @response.body
+ assert_equal '/except/clubs/1/players', except_club_players_path(:club_id => '1')
+
+ get '/except/clubs/1/players/2/edit'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { edit_except_club_player_path(:club_id => '1', :id => '2') }
+
+ get '/except/clubs/1/chairman'
+ assert_equal 'except/chairmen#show', @response.body
+ assert_equal '/except/clubs/1/chairman', except_club_chairman_path(:club_id => '1')
+
+ get '/except/clubs/1/chairman/edit'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { edit_except_club_chairman_path(:club_id => '1') }
end
def test_only_option_should_override_scope
- with_test_routes do
- get '/only/sectors'
- assert_equal 'only/sectors#index', @response.body
- assert_equal '/only/sectors', only_sectors_path
-
- get '/only/sectors/1'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { only_sector_path(:id => '1') }
- end
+ get '/only/sectors'
+ assert_equal 'only/sectors#index', @response.body
+ assert_equal '/only/sectors', only_sectors_path
+
+ get '/only/sectors/1'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { only_sector_path(:id => '1') }
end
def test_only_option_should_not_inherit
- with_test_routes do
- get '/only/sectors/1/companies/2'
- assert_equal 'only/companies#show', @response.body
- assert_equal '/only/sectors/1/companies/2', only_sector_company_path(:sector_id => '1', :id => '2')
-
- get '/only/sectors/1/leader'
- assert_equal 'only/leaders#show', @response.body
- assert_equal '/only/sectors/1/leader', only_sector_leader_path(:sector_id => '1')
- end
+ get '/only/sectors/1/companies/2'
+ assert_equal 'only/companies#show', @response.body
+ assert_equal '/only/sectors/1/companies/2', only_sector_company_path(:sector_id => '1', :id => '2')
+
+ get '/only/sectors/1/leader'
+ assert_equal 'only/leaders#show', @response.body
+ assert_equal '/only/sectors/1/leader', only_sector_leader_path(:sector_id => '1')
end
def test_except_option_should_override_scope
- with_test_routes do
- get '/except/sectors'
- assert_equal 'except/sectors#index', @response.body
- assert_equal '/except/sectors', except_sectors_path
-
- get '/except/sectors/1'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { except_sector_path(:id => '1') }
- end
+ get '/except/sectors'
+ assert_equal 'except/sectors#index', @response.body
+ assert_equal '/except/sectors', except_sectors_path
+
+ get '/except/sectors/1'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { except_sector_path(:id => '1') }
end
def test_except_option_should_not_inherit
- with_test_routes do
- get '/except/sectors/1/companies/2'
- assert_equal 'except/companies#show', @response.body
- assert_equal '/except/sectors/1/companies/2', except_sector_company_path(:sector_id => '1', :id => '2')
-
- get '/except/sectors/1/leader'
- assert_equal 'except/leaders#show', @response.body
- assert_equal '/except/sectors/1/leader', except_sector_leader_path(:sector_id => '1')
- end
+ get '/except/sectors/1/companies/2'
+ assert_equal 'except/companies#show', @response.body
+ assert_equal '/except/sectors/1/companies/2', except_sector_company_path(:sector_id => '1', :id => '2')
+
+ get '/except/sectors/1/leader'
+ assert_equal 'except/leaders#show', @response.body
+ assert_equal '/except/sectors/1/leader', except_sector_leader_path(:sector_id => '1')
end
def test_except_option_should_override_scoped_only
- with_test_routes do
- get '/only/sectors/1/managers'
- assert_equal 'only/managers#index', @response.body
- assert_equal '/only/sectors/1/managers', only_sector_managers_path(:sector_id => '1')
-
- get '/only/sectors/1/managers/2'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { only_sector_manager_path(:sector_id => '1', :id => '2') }
- end
+ get '/only/sectors/1/managers'
+ assert_equal 'only/managers#index', @response.body
+ assert_equal '/only/sectors/1/managers', only_sector_managers_path(:sector_id => '1')
+
+ get '/only/sectors/1/managers/2'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { only_sector_manager_path(:sector_id => '1', :id => '2') }
end
def test_only_option_should_override_scoped_except
- with_test_routes do
- get '/except/sectors/1/managers'
- assert_equal 'except/managers#index', @response.body
- assert_equal '/except/sectors/1/managers', except_sector_managers_path(:sector_id => '1')
-
- get '/except/sectors/1/managers/2'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { except_sector_manager_path(:sector_id => '1', :id => '2') }
- end
+ get '/except/sectors/1/managers'
+ assert_equal 'except/managers#index', @response.body
+ assert_equal '/except/sectors/1/managers', except_sector_managers_path(:sector_id => '1')
+
+ get '/except/sectors/1/managers/2'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { except_sector_manager_path(:sector_id => '1', :id => '2') }
end
def test_only_scope_should_override_parent_scope
- with_test_routes do
- get '/only/sectors/1/companies/2/divisions'
- assert_equal 'only/divisions#index', @response.body
- assert_equal '/only/sectors/1/companies/2/divisions', only_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
-
- get '/only/sectors/1/companies/2/divisions/3'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { only_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
- end
+ get '/only/sectors/1/companies/2/divisions'
+ assert_equal 'only/divisions#index', @response.body
+ assert_equal '/only/sectors/1/companies/2/divisions', only_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
+
+ get '/only/sectors/1/companies/2/divisions/3'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { only_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_except_scope_should_override_parent_scope
- with_test_routes do
- get '/except/sectors/1/companies/2/divisions'
- assert_equal 'except/divisions#index', @response.body
- assert_equal '/except/sectors/1/companies/2/divisions', except_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
-
- get '/except/sectors/1/companies/2/divisions/3'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { except_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
- end
+ get '/except/sectors/1/companies/2/divisions'
+ assert_equal 'except/divisions#index', @response.body
+ assert_equal '/except/sectors/1/companies/2/divisions', except_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
+
+ get '/except/sectors/1/companies/2/divisions/3'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { except_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_except_scope_should_override_parent_only_scope
- with_test_routes do
- get '/only/sectors/1/companies/2/departments'
- assert_equal 'only/departments#index', @response.body
- assert_equal '/only/sectors/1/companies/2/departments', only_sector_company_departments_path(:sector_id => '1', :company_id => '2')
-
- get '/only/sectors/1/companies/2/departments/3'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { only_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
- end
+ get '/only/sectors/1/companies/2/departments'
+ assert_equal 'only/departments#index', @response.body
+ assert_equal '/only/sectors/1/companies/2/departments', only_sector_company_departments_path(:sector_id => '1', :company_id => '2')
+
+ get '/only/sectors/1/companies/2/departments/3'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { only_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_only_scope_should_override_parent_except_scope
- with_test_routes do
- get '/except/sectors/1/companies/2/departments'
- assert_equal 'except/departments#index', @response.body
- assert_equal '/except/sectors/1/companies/2/departments', except_sector_company_departments_path(:sector_id => '1', :company_id => '2')
-
- get '/except/sectors/1/companies/2/departments/3'
- assert_equal 'Not Found', @response.body
- assert_raise(NoMethodError) { except_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
- end
+ get '/except/sectors/1/companies/2/departments'
+ assert_equal 'except/departments#index', @response.body
+ assert_equal '/except/sectors/1/companies/2/departments', except_sector_company_departments_path(:sector_id => '1', :company_id => '2')
+
+ get '/except/sectors/1/companies/2/departments/3'
+ assert_equal 'Not Found', @response.body
+ assert_raise(NoMethodError) { except_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_resources_are_not_pluralized
- with_test_routes do
- get '/transport/taxis'
- assert_equal 'transport/taxis#index', @response.body
- assert_equal '/transport/taxis', transport_taxis_path
+ get '/transport/taxis'
+ assert_equal 'transport/taxis#index', @response.body
+ assert_equal '/transport/taxis', transport_taxis_path
- get '/transport/taxis/new'
- assert_equal 'transport/taxis#new', @response.body
- assert_equal '/transport/taxis/new', new_transport_taxi_path
+ get '/transport/taxis/new'
+ assert_equal 'transport/taxis#new', @response.body
+ assert_equal '/transport/taxis/new', new_transport_taxi_path
- post '/transport/taxis'
- assert_equal 'transport/taxis#create', @response.body
+ post '/transport/taxis'
+ assert_equal 'transport/taxis#create', @response.body
- get '/transport/taxis/1'
- assert_equal 'transport/taxis#show', @response.body
- assert_equal '/transport/taxis/1', transport_taxi_path(:id => '1')
+ get '/transport/taxis/1'
+ assert_equal 'transport/taxis#show', @response.body
+ assert_equal '/transport/taxis/1', transport_taxi_path(:id => '1')
- get '/transport/taxis/1/edit'
- assert_equal 'transport/taxis#edit', @response.body
- assert_equal '/transport/taxis/1/edit', edit_transport_taxi_path(:id => '1')
+ get '/transport/taxis/1/edit'
+ assert_equal 'transport/taxis#edit', @response.body
+ assert_equal '/transport/taxis/1/edit', edit_transport_taxi_path(:id => '1')
- put '/transport/taxis/1'
- assert_equal 'transport/taxis#update', @response.body
+ put '/transport/taxis/1'
+ assert_equal 'transport/taxis#update', @response.body
- delete '/transport/taxis/1'
- assert_equal 'transport/taxis#destroy', @response.body
- end
+ delete '/transport/taxis/1'
+ assert_equal 'transport/taxis#destroy', @response.body
end
def test_singleton_resources_are_not_singularized
- with_test_routes do
- get '/medical/taxis/new'
- assert_equal 'medical/taxes#new', @response.body
- assert_equal '/medical/taxis/new', new_medical_taxis_path
+ get '/medical/taxis/new'
+ assert_equal 'medical/taxis#new', @response.body
+ assert_equal '/medical/taxis/new', new_medical_taxis_path
- post '/medical/taxis'
- assert_equal 'medical/taxes#create', @response.body
+ post '/medical/taxis'
+ assert_equal 'medical/taxis#create', @response.body
- get '/medical/taxis'
- assert_equal 'medical/taxes#show', @response.body
- assert_equal '/medical/taxis', medical_taxis_path
+ get '/medical/taxis'
+ assert_equal 'medical/taxis#show', @response.body
+ assert_equal '/medical/taxis', medical_taxis_path
- get '/medical/taxis/edit'
- assert_equal 'medical/taxes#edit', @response.body
- assert_equal '/medical/taxis/edit', edit_medical_taxis_path
+ get '/medical/taxis/edit'
+ assert_equal 'medical/taxis#edit', @response.body
+ assert_equal '/medical/taxis/edit', edit_medical_taxis_path
- put '/medical/taxis'
- assert_equal 'medical/taxes#update', @response.body
+ put '/medical/taxis'
+ assert_equal 'medical/taxis#update', @response.body
- delete '/medical/taxis'
- assert_equal 'medical/taxes#destroy', @response.body
- end
+ delete '/medical/taxis'
+ assert_equal 'medical/taxis#destroy', @response.body
end
def test_greedy_resource_id_regexp_doesnt_match_edit_and_custom_action
- with_test_routes do
- get '/sections/1/edit'
- assert_equal 'sections#edit', @response.body
- assert_equal '/sections/1/edit', edit_section_path(:id => '1')
-
- get '/sections/1/preview'
- assert_equal 'sections#preview', @response.body
- assert_equal '/sections/1/preview', preview_section_path(:id => '1')
- end
+ get '/sections/1/edit'
+ assert_equal 'sections#edit', @response.body
+ assert_equal '/sections/1/edit', edit_section_path(:id => '1')
+
+ get '/sections/1/preview'
+ assert_equal 'sections#preview', @response.body
+ assert_equal '/sections/1/preview', preview_section_path(:id => '1')
end
def test_resource_constraints_are_pushed_to_scope
- with_test_routes do
- get '/wiki/articles/Ruby_on_Rails_3.0'
- assert_equal 'wiki/articles#show', @response.body
- assert_equal '/wiki/articles/Ruby_on_Rails_3.0', wiki_article_path(:id => 'Ruby_on_Rails_3.0')
-
- get '/wiki/articles/Ruby_on_Rails_3.0/comments/new'
- assert_equal 'wiki/comments#new', @response.body
- assert_equal '/wiki/articles/Ruby_on_Rails_3.0/comments/new', new_wiki_article_comment_path(:article_id => 'Ruby_on_Rails_3.0')
-
- post '/wiki/articles/Ruby_on_Rails_3.0/comments'
- assert_equal 'wiki/comments#create', @response.body
- assert_equal '/wiki/articles/Ruby_on_Rails_3.0/comments', wiki_article_comments_path(:article_id => 'Ruby_on_Rails_3.0')
- end
+ get '/wiki/articles/Ruby_on_Rails_3.0'
+ assert_equal 'wiki/articles#show', @response.body
+ assert_equal '/wiki/articles/Ruby_on_Rails_3.0', wiki_article_path(:id => 'Ruby_on_Rails_3.0')
+
+ get '/wiki/articles/Ruby_on_Rails_3.0/comments/new'
+ assert_equal 'wiki/comments#new', @response.body
+ assert_equal '/wiki/articles/Ruby_on_Rails_3.0/comments/new', new_wiki_article_comment_path(:article_id => 'Ruby_on_Rails_3.0')
+
+ post '/wiki/articles/Ruby_on_Rails_3.0/comments'
+ assert_equal 'wiki/comments#create', @response.body
+ assert_equal '/wiki/articles/Ruby_on_Rails_3.0/comments', wiki_article_comments_path(:article_id => 'Ruby_on_Rails_3.0')
end
def test_resources_path_can_be_a_symbol
- with_test_routes do
- get '/pages'
- assert_equal 'wiki_pages#index', @response.body
- assert_equal '/pages', wiki_pages_path
-
- get '/pages/Ruby_on_Rails'
- assert_equal 'wiki_pages#show', @response.body
- assert_equal '/pages/Ruby_on_Rails', wiki_page_path(:id => 'Ruby_on_Rails')
-
- get '/my_account'
- assert_equal 'wiki_accounts#show', @response.body
- assert_equal '/my_account', wiki_account_path
- end
+ get '/pages'
+ assert_equal 'wiki_pages#index', @response.body
+ assert_equal '/pages', wiki_pages_path
+
+ get '/pages/Ruby_on_Rails'
+ assert_equal 'wiki_pages#show', @response.body
+ assert_equal '/pages/Ruby_on_Rails', wiki_page_path(:id => 'Ruby_on_Rails')
+
+ get '/my_account'
+ assert_equal 'wiki_accounts#show', @response.body
+ assert_equal '/my_account', wiki_account_path
end
def test_redirect_https
- with_test_routes do
- with_https do
- get '/secure'
- verify_redirect 'https://www.example.com/secure/login'
- end
+ with_https do
+ get '/secure'
+ verify_redirect 'https://www.example.com/secure/login'
end
end
@@ -2329,7 +2087,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_named_routes_collision_is_avoided_unless_explicitly_given_as
assert_equal "/c/1", routes_collision_path(1)
- assert_equal "/forced_collision", routes_forced_collision_path
+ assert_equal "/fc/1", routes_forced_collision_path(1)
+ end
+
+ def test_redirect_argument_error
+ routes = Class.new { include ActionDispatch::Routing::Redirection }.new
+ assert_raises(ArgumentError) { routes.redirect Object.new }
end
def test_explicitly_avoiding_the_named_route
@@ -2413,10 +2176,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
private
- def with_test_routes
- yield
- end
-
def with_https
old_https = https?
https!
@@ -2441,7 +2200,8 @@ class TestAppendingRoutes < ActionDispatch::IntegrationTest
lambda { |e| [ 200, { 'Content-Type' => 'text/plain' }, [resp] ] }
end
- setup do
+ def setup
+ super
s = self
@app = ActionDispatch::Routing::RouteSet.new
@app.append do
@@ -2470,6 +2230,32 @@ class TestAppendingRoutes < ActionDispatch::IntegrationTest
end
end
+class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest
+ module ::Admin
+ class StorageFilesController < ActionController::Base
+ def index
+ render :text => "admin/storage_files#index"
+ end
+ end
+ end
+
+ DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new
+ DefaultScopeRoutes.draw do
+ namespace :admin do
+ resources :storage_files, :controller => "StorageFiles"
+ end
+ end
+
+ def app
+ DefaultScopeRoutes
+ end
+
+ def test_controller_options
+ get '/admin/storage_files'
+ assert_equal "admin/storage_files#index", @response.body
+ end
+end
+
class TestDefaultScope < ActionDispatch::IntegrationTest
module ::Blog
class PostsController < ActionController::Base
@@ -2565,3 +2351,54 @@ class TestUriPathEscaping < ActionDispatch::IntegrationTest
assert_equal 'a b/c+d', @response.body
end
end
+
+class TestUnicodePaths < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ match "/#{Rack::Utils.escape("ほげ")}" => lambda { |env|
+ [200, { 'Content-Type' => 'text/plain' }, []]
+ }, :as => :unicode_path
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ test 'recognizes unicode path' do
+ get "/#{Rack::Utils.escape("ほげ")}"
+ assert_equal "200", @response.code
+ end
+end
+
+class TestMultipleNestedController < ActionDispatch::IntegrationTest
+ module ::Foo
+ module Bar
+ class BazController < ActionController::Base
+ def index
+ render :inline => "<%= url_for :controller => '/pooh', :action => 'index' %>"
+ end
+ end
+ end
+ end
+
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ namespace :foo do
+ namespace :bar do
+ match "baz" => "baz#index"
+ end
+ end
+ match "pooh" => "pooh#index"
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ test "controller option which starts with '/' from multiple nested controller" do
+ get "/foo/bar/baz"
+ assert_equal "/pooh", @response.body
+ end
+
+end
+
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index 73e056de23..12405bf45d 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -30,8 +30,6 @@ class CacheStoreTest < ActionDispatch::IntegrationTest
session[:bar] = "baz"
head :ok
end
-
- def rescue_action(e) raise end
end
def test_setting_and_getting_session_value
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 92df6967d6..19969394cd 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -54,8 +54,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
request.session_options[:renew] = true
head :ok
end
-
- def rescue_action(e) raise end
end
def test_setting_session_value
diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb
index 8502bc547b..5277c92b55 100644
--- a/actionpack/test/dispatch/session/mem_cache_store_test.rb
+++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb
@@ -31,8 +31,6 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
session[:bar] = "baz"
head :ok
end
-
- def rescue_action(e) raise end
end
begin
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 42f6c7f79f..45f8fc11b3 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -2,32 +2,33 @@ require 'abstract_unit'
class ShowExceptionsTest < ActionDispatch::IntegrationTest
- Boomer = lambda do |env|
- req = ActionDispatch::Request.new(env)
- case req.path
- when "/not_found"
- raise ActionController::UnknownAction
- when "/runtime_error"
- raise RuntimeError
- when "/method_not_allowed"
- raise ActionController::MethodNotAllowed
- when "/not_implemented"
- raise ActionController::NotImplemented
- when "/unprocessable_entity"
- raise ActionController::InvalidAuthenticityToken
- when "/not_found_original_exception"
- raise ActionView::Template::Error.new('template', {}, AbstractController::ActionNotFound.new)
- else
- raise "puke!"
+ class Boomer
+ def call(env)
+ req = ActionDispatch::Request.new(env)
+ case req.path
+ when "/not_found"
+ raise AbstractController::ActionNotFound
+ when "/method_not_allowed"
+ raise ActionController::MethodNotAllowed
+ when "/not_found_original_exception"
+ raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new)
+ else
+ raise "puke!"
+ end
end
end
- ProductionApp = ActionDispatch::ShowExceptions.new(Boomer, false)
- DevelopmentApp = ActionDispatch::ShowExceptions.new(Boomer, true)
+ ProductionApp = ActionDispatch::ShowExceptions.new(Boomer.new, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public"))
- test "rescue in public from a remote ip" do
+ test "skip exceptions app if not showing exceptions" do
+ @app = ProductionApp
+ assert_raise RuntimeError do
+ get "/", {}, {'action_dispatch.show_exceptions' => false}
+ end
+ end
+
+ test "rescue with error page" do
@app = ProductionApp
- self.remote_addr = '208.77.188.166'
get "/", {}, {'action_dispatch.show_exceptions' => true}
assert_response 500
@@ -42,32 +43,11 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
assert_equal "", body
end
- test "rescue locally from a local request" do
- @app = ProductionApp
- ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address|
- self.remote_addr = ip_address
-
- get "/", {}, {'action_dispatch.show_exceptions' => true}
- assert_response 500
- assert_match(/puke/, body)
-
- get "/not_found", {}, {'action_dispatch.show_exceptions' => true}
- assert_response 404
- assert_match(/#{ActionController::UnknownAction.name}/, body)
-
- get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true}
- assert_response 405
- assert_match(/ActionController::MethodNotAllowed/, body)
- end
- end
-
- test "localize public rescue message" do
- # Change locale
+ test "localize rescue error page" do
old_locale, I18n.locale = I18n.locale, :da
begin
@app = ProductionApp
- self.remote_addr = '208.77.188.166'
get "/", {}, {'action_dispatch.show_exceptions' => true}
assert_response 500
@@ -81,67 +61,42 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
end
end
- test "always rescue locally in development mode" do
- @app = DevelopmentApp
- self.remote_addr = '208.77.188.166'
+ test "sets the HTTP charset parameter" do
+ @app = ProductionApp
get "/", {}, {'action_dispatch.show_exceptions' => true}
- assert_response 500
- assert_match(/puke/, body)
-
- get "/not_found", {}, {'action_dispatch.show_exceptions' => true}
- assert_response 404
- assert_match(/#{ActionController::UnknownAction.name}/, body)
-
- get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true}
- assert_response 405
- assert_match(/ActionController::MethodNotAllowed/, body)
- end
-
- test "does not show filtered parameters" do
- @app = DevelopmentApp
-
- get "/", {"foo"=>"bar"}, {'action_dispatch.show_exceptions' => true,
- 'action_dispatch.parameter_filter' => [:foo]}
- assert_response 500
- assert_match("&quot;foo&quot;=&gt;&quot;[FILTERED]&quot;", body)
+ assert_equal "text/html; charset=utf-8", response.headers["Content-Type"]
end
- test "show registered original exception for wrapped exceptions when consider_all_requests_local is false" do
+ test "show registered original exception for wrapped exceptions" do
@app = ProductionApp
- self.remote_addr = '208.77.188.166'
get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true}
assert_response 404
assert_match(/404 error/, body)
end
- test "show registered original exception for wrapped exceptions when consider_all_requests_local is true" do
- @app = DevelopmentApp
+ test "calls custom exceptions app" do
+ exceptions_app = lambda do |env|
+ assert_kind_of AbstractController::ActionNotFound, env["action_dispatch.exception"]
+ assert_equal "/404", env["PATH_INFO"]
+ [404, { "Content-Type" => "text/plain" }, ["YOU FAILED BRO"]]
+ end
+ @app = ActionDispatch::ShowExceptions.new(Boomer.new, exceptions_app)
get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true}
assert_response 404
- assert_match(/AbstractController::ActionNotFound/, body)
- end
-
- test "show the controller name in the diagnostics template when controller name is present" do
- @app = ProductionApp
- get("/runtime_error", {}, {
- 'action_dispatch.show_exceptions' => true,
- 'action_dispatch.request.parameters' => {
- 'action' => 'show',
- 'id' => 'unknown',
- 'controller' => 'featured_tile'
- }
- })
- assert_response 500
- assert_match(/RuntimeError\n in FeaturedTileController/, body)
+ assert_equal "YOU FAILED BRO", body
end
- test "sets the HTTP charset parameter" do
- @app = DevelopmentApp
+ test "returns an empty response if custom exceptions app returns X-Cascade pass" do
+ exceptions_app = lambda do |env|
+ [404, { "X-Cascade" => "pass" }, []]
+ end
- get "/", {}, {'action_dispatch.show_exceptions' => true}
- assert_equal "text/html; charset=utf-8", response.headers["Content-Type"]
+ @app = ActionDispatch::ShowExceptions.new(Boomer.new, exceptions_app)
+ get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 405
+ assert_equal "", body
end
end
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 9f3cbd19ef..e086d99b19 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require 'abstract_unit'
module StaticTests
@@ -30,6 +31,38 @@ module StaticTests
assert_html "/foo/index.html", get("/foo")
end
+ def test_served_static_file_with_non_english_filename
+ assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}")
+ end
+
+ def test_serves_static_file_with_encoded_pchar
+ assert_html "/foo/foo!bar.html", get("/foo/foo%21bar.html")
+ assert_html "/foo/foo$bar.html", get("/foo/foo%24bar.html")
+ assert_html "/foo/foo&bar.html", get("/foo/foo%26bar.html")
+ assert_html "/foo/foo'bar.html", get("/foo/foo%27bar.html")
+ assert_html "/foo/foo(bar).html", get("/foo/foo%28bar%29.html")
+ assert_html "/foo/foo*bar.html", get("/foo/foo%2Abar.html")
+ assert_html "/foo/foo+bar.html", get("/foo/foo%2Bbar.html")
+ assert_html "/foo/foo,bar.html", get("/foo/foo%2Cbar.html")
+ assert_html "/foo/foo;bar.html", get("/foo/foo%3Bbar.html")
+ assert_html "/foo/foo:bar.html", get("/foo/foo%3Abar.html")
+ assert_html "/foo/foo@bar.html", get("/foo/foo%40bar.html")
+ end
+
+ def test_serves_static_file_with_unencoded_pchar
+ assert_html "/foo/foo!bar.html", get("/foo/foo!bar.html")
+ assert_html "/foo/foo$bar.html", get("/foo/foo$bar.html")
+ assert_html "/foo/foo&bar.html", get("/foo/foo&bar.html")
+ assert_html "/foo/foo'bar.html", get("/foo/foo'bar.html")
+ assert_html "/foo/foo(bar).html", get("/foo/foo(bar).html")
+ assert_html "/foo/foo*bar.html", get("/foo/foo*bar.html")
+ assert_html "/foo/foo+bar.html", get("/foo/foo+bar.html")
+ assert_html "/foo/foo,bar.html", get("/foo/foo,bar.html")
+ assert_html "/foo/foo;bar.html", get("/foo/foo;bar.html")
+ assert_html "/foo/foo:bar.html", get("/foo/foo:bar.html")
+ assert_html "/foo/foo@bar.html", get("/foo/foo@bar.html")
+ end
+
private
def assert_html(body, response)
@@ -53,4 +86,4 @@ class StaticTest < ActiveSupport::TestCase
end
include StaticTests
-end \ No newline at end of file
+end
diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb
index 7e4a1519fb..0b95291e18 100644
--- a/actionpack/test/dispatch/uploaded_file_test.rb
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -13,11 +13,9 @@ module ActionDispatch
assert_equal 'foo', uf.original_filename
end
- if "ruby".encoding_aware?
- def test_filename_should_be_in_utf_8
- uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
- assert_equal "UTF-8", uf.original_filename.encoding.to_s
- end
+ def test_filename_should_be_in_utf_8
+ uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
+ assert_equal "UTF-8", uf.original_filename.encoding.to_s
end
def test_content_type
diff --git a/actionpack/test/fixtures/addresses/list.erb b/actionpack/test/fixtures/addresses/list.erb
deleted file mode 100644
index c75e01eece..0000000000
--- a/actionpack/test/fixtures/addresses/list.erb
+++ /dev/null
@@ -1 +0,0 @@
-We only need to get this far!
diff --git a/actionpack/test/fixtures/company.rb b/actionpack/test/fixtures/company.rb
index cd39ea7898..e29978801e 100644
--- a/actionpack/test/fixtures/company.rb
+++ b/actionpack/test/fixtures/company.rb
@@ -1,10 +1,10 @@
class Company < ActiveRecord::Base
has_one :mascot
attr_protected :rating
- set_sequence_name :companies_nonstd_seq
+ self.sequence_name = :companies_nonstd_seq
validates_presence_of :name
def validate
errors.add('rating', 'rating should not be 2') if rating == 2
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/fixtures/developer.rb b/actionpack/test/fixtures/developer.rb
index c70eda34c6..dd14548fac 100644
--- a/actionpack/test/fixtures/developer.rb
+++ b/actionpack/test/fixtures/developer.rb
@@ -5,5 +5,5 @@ class Developer < ActiveRecord::Base
end
class DeVeLoPeR < ActiveRecord::Base
- set_table_name "developers"
+ self.table_name = "developers"
end
diff --git a/actionpack/test/fixtures/developers.yml b/actionpack/test/fixtures/developers.yml
index 308bf75de2..3656564f63 100644
--- a/actionpack/test/fixtures/developers.yml
+++ b/actionpack/test/fixtures/developers.yml
@@ -8,7 +8,7 @@ jamis:
name: Jamis
salary: 150000
-<% for digit in 3..10 %>
+<% (3..10).each do |digit| %>
dev_<%= digit %>:
id: <%= digit %>
name: fixture_<%= digit %>
diff --git a/actionpack/test/fixtures/public/foo/foo!bar.html b/actionpack/test/fixtures/public/foo/foo!bar.html
new file mode 100644
index 0000000000..2928f2717f
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo!bar.html
@@ -0,0 +1 @@
+/foo/foo!bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo$bar.html b/actionpack/test/fixtures/public/foo/foo$bar.html
new file mode 100644
index 0000000000..4f837df01d
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo$bar.html
@@ -0,0 +1 @@
+/foo/foo$bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo&bar.html b/actionpack/test/fixtures/public/foo/foo&bar.html
new file mode 100644
index 0000000000..c194e8de87
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo&bar.html
@@ -0,0 +1 @@
+/foo/foo&bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo'bar.html b/actionpack/test/fixtures/public/foo/foo'bar.html
new file mode 100644
index 0000000000..25c3275736
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo'bar.html
@@ -0,0 +1 @@
+/foo/foo'bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo(bar).html b/actionpack/test/fixtures/public/foo/foo(bar).html
new file mode 100644
index 0000000000..94fa4cb944
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo(bar).html
@@ -0,0 +1 @@
+/foo/foo(bar).html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo*bar.html b/actionpack/test/fixtures/public/foo/foo*bar.html
new file mode 100644
index 0000000000..79d5194c8d
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo*bar.html
@@ -0,0 +1 @@
+/foo/foo*bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo+bar.html b/actionpack/test/fixtures/public/foo/foo+bar.html
new file mode 100644
index 0000000000..0fdc2ecabc
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo+bar.html
@@ -0,0 +1 @@
+/foo/foo+bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo,bar.html b/actionpack/test/fixtures/public/foo/foo,bar.html
new file mode 100644
index 0000000000..f040fce197
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo,bar.html
@@ -0,0 +1 @@
+/foo/foo,bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo:bar.html b/actionpack/test/fixtures/public/foo/foo:bar.html
new file mode 100644
index 0000000000..7900a2642b
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo:bar.html
@@ -0,0 +1 @@
+/foo/foo:bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo;bar.html b/actionpack/test/fixtures/public/foo/foo;bar.html
new file mode 100644
index 0000000000..2248376954
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo;bar.html
@@ -0,0 +1 @@
+/foo/foo;bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo=bar.html b/actionpack/test/fixtures/public/foo/foo=bar.html
new file mode 100644
index 0000000000..206f69e286
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo=bar.html
@@ -0,0 +1 @@
+/foo/foo=bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/foo@bar.html b/actionpack/test/fixtures/public/foo/foo@bar.html
new file mode 100644
index 0000000000..4e8e90f9b8
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/foo@bar.html
@@ -0,0 +1 @@
+/foo/foo@bar.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/foo/こんにちは.html b/actionpack/test/fixtures/public/foo/こんにちは.html
new file mode 100644
index 0000000000..1df9166522
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/こんにちは.html
@@ -0,0 +1 @@
+means hello in Japanese
diff --git a/actionpack/test/fixtures/sprockets/app/fonts/dir/font.ttf b/actionpack/test/fixtures/sprockets/app/fonts/dir/font.ttf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/fonts/dir/font.ttf
diff --git a/actionpack/test/fixtures/sprockets/app/fonts/font.ttf b/actionpack/test/fixtures/sprockets/app/fonts/font.ttf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/fonts/font.ttf
diff --git a/actionpack/test/fixtures/test/_content_tag_nested_in_content_tag.erb b/actionpack/test/fixtures/test/_content_tag_nested_in_content_tag.erb
new file mode 100644
index 0000000000..2f21a75dd9
--- /dev/null
+++ b/actionpack/test/fixtures/test/_content_tag_nested_in_content_tag.erb
@@ -0,0 +1,3 @@
+<%= content_tag 'p' do %>
+ <%= content_tag 'b', 'Hello' %>
+<% end %>
diff --git a/actionpack/test/fixtures/test/_label_with_block.erb b/actionpack/test/fixtures/test/_label_with_block.erb
new file mode 100644
index 0000000000..40117e594e
--- /dev/null
+++ b/actionpack/test/fixtures/test/_label_with_block.erb
@@ -0,0 +1,4 @@
+<%= label 'post', 'message' do %>
+ Message
+ <%= text_field 'post', 'message' %>
+<% end %>
diff --git a/actionpack/test/lib/controller/fake_controllers.rb b/actionpack/test/lib/controller/fake_controllers.rb
index 09692f77b5..1a2863b689 100644
--- a/actionpack/test/lib/controller/fake_controllers.rb
+++ b/actionpack/test/lib/controller/fake_controllers.rb
@@ -1,11 +1,7 @@
-class << Object; alias_method :const_available?, :const_defined?; end
-
class ContentController < ActionController::Base; end
module Admin
- class << self; alias_method :const_available?, :const_defined?; end
class AccountsController < ActionController::Base; end
- class NewsFeedController < ActionController::Base; end
class PostsController < ActionController::Base; end
class StuffController < ActionController::Base; end
class UserController < ActionController::Base; end
@@ -17,46 +13,23 @@ module Api
class ProductsController < ActionController::Base; end
end
-# TODO: Reduce the number of test controllers we use
class AccountController < ActionController::Base; end
-class AddressesController < ActionController::Base; end
class ArchiveController < ActionController::Base; end
class ArticlesController < ActionController::Base; end
class BarController < ActionController::Base; end
class BlogController < ActionController::Base; end
class BooksController < ActionController::Base; end
-class BraveController < ActionController::Base; end
class CarsController < ActionController::Base; end
class CcController < ActionController::Base; end
class CController < ActionController::Base; end
-class ElsewhereController < ActionController::Base; end
class FooController < ActionController::Base; end
class GeocodeController < ActionController::Base; end
-class HiController < ActionController::Base; end
-class ImageController < ActionController::Base; end
class NewsController < ActionController::Base; end
class NotesController < ActionController::Base; end
+class PagesController < ActionController::Base; end
class PeopleController < ActionController::Base; end
class PostsController < ActionController::Base; end
-class SessionsController < ActionController::Base; end
-class StuffController < ActionController::Base; end
class SubpathBooksController < ActionController::Base; end
class SymbolsController < ActionController::Base; end
class UserController < ActionController::Base; end
-class WeblogController < ActionController::Base; end
-
-# For speed test
-class SpeedController < ActionController::Base; end
-class SearchController < SpeedController; end
-class VideosController < SpeedController; end
-class VideoFileController < SpeedController; end
-class VideoSharesController < SpeedController; end
-class VideoAbusesController < SpeedController; end
-class VideoUploadsController < SpeedController; end
-class VideoVisitsController < SpeedController; end
-class UsersController < SpeedController; end
-class SettingsController < SpeedController; end
-class ChannelsController < SpeedController; end
-class ChannelVideosController < SpeedController; end
-class LostPasswordsController < SpeedController; end
-class PagesController < SpeedController; end
+class UsersController < ActionController::Base; end
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index 363403092b..bbb4cc5ef3 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -34,6 +34,16 @@ end
class GoodCustomer < Customer
end
+class ValidatedCustomer < Customer
+ def errors
+ if name =~ /Sikachu/i
+ []
+ else
+ [{:name => "is invalid"}]
+ end
+ end
+end
+
module Quiz
class Question < Struct.new(:name, :id)
extend ActiveModel::Naming
@@ -175,8 +185,8 @@ class HashBackedAuthor < Hash
end
module Blog
- def self._railtie
- self
+ def self.use_relative_model_naming?
+ true
end
class Post < Struct.new(:title, :id)
diff --git a/actionpack/test/lib/testing_sandbox.rb b/actionpack/test/lib/testing_sandbox.rb
deleted file mode 100644
index c36585104f..0000000000
--- a/actionpack/test/lib/testing_sandbox.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module TestingSandbox
- # Temporarily replaces KCODE for the block
- def with_kcode(kcode)
- if RUBY_VERSION < '1.9'
- old_kcode, $KCODE = $KCODE, kcode
- begin
- yield
- ensure
- $KCODE = old_kcode
- end
- else
- yield
- end
- end
-end
diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb
index 8530a72a82..52be0f1762 100644
--- a/actionpack/test/template/active_model_helper_test.rb
+++ b/actionpack/test/template/active_model_helper_test.rb
@@ -4,7 +4,7 @@ class ActiveModelHelperTest < ActionView::TestCase
tests ActionView::Helpers::ActiveModelHelper
silence_warnings do
- class Post < Struct.new(:author_name, :body)
+ class Post < Struct.new(:author_name, :body, :updated_at)
include ActiveModel::Conversion
include ActiveModel::Validations
@@ -20,9 +20,11 @@ class ActiveModelHelperTest < ActionView::TestCase
@post = Post.new
@post.errors[:author_name] << "can't be empty"
@post.errors[:body] << "foo"
+ @post.errors[:updated_at] << "bar"
@post.author_name = ""
@post.body = "Back to the hill and over it again!"
+ @post.updated_at = Date.new(2004, 6, 15)
end
def test_text_area_with_errors
@@ -39,6 +41,27 @@ class ActiveModelHelperTest < ActionView::TestCase
)
end
+ def test_date_select_with_errors
+ assert_dom_equal(
+ %(<div class="field_with_errors"><select id="post_updated_at_1i" name="post[updated_at(1i)]">\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n</select>\n<input id="post_updated_at_2i" name="post[updated_at(2i)]" type="hidden" value="6" />\n<input id="post_updated_at_3i" name="post[updated_at(3i)]" type="hidden" value="15" />\n</div>),
+ date_select("post", "updated_at", :discard_month => true, :discard_day => true, :start_year => 2004, :end_year => 2005)
+ )
+ end
+
+ def test_datetime_select_with_errors
+ assert_dom_equal(
+ %(<div class="field_with_errors"><input id="post_updated_at_1i" name="post[updated_at(1i)]" type="hidden" value="2004" />\n<input id="post_updated_at_2i" name="post[updated_at(2i)]" type="hidden" value="6" />\n<input id="post_updated_at_3i" name="post[updated_at(3i)]" type="hidden" value="15" />\n<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n<option selected="selected" value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n</select>\n : <select id="post_updated_at_5i" name="post[updated_at(5i)]">\n<option selected="selected" value="00">00</option>\n</select>\n</div>),
+ datetime_select("post", "updated_at", :discard_year => true, :discard_month => true, :discard_day => true, :minute_step => 60)
+ )
+ end
+
+ def test_time_select_with_errors
+ assert_dom_equal(
+ %(<div class="field_with_errors"><input id="post_updated_at_1i" name="post[updated_at(1i)]" type="hidden" value="2004" />\n<input id="post_updated_at_2i" name="post[updated_at(2i)]" type="hidden" value="6" />\n<input id="post_updated_at_3i" name="post[updated_at(3i)]" type="hidden" value="15" />\n<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n<option selected="selected" value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n</select>\n : <select id="post_updated_at_5i" name="post[updated_at(5i)]">\n<option selected="selected" value="00">00</option>\n</select>\n</div>),
+ time_select("post", "updated_at", :minute_step => 60)
+ )
+ end
+
def test_hidden_field_does_not_render_errors
assert_dom_equal(
%(<input id="post_author_name" name="post[author_name]" type="hidden" value="" />),
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index aa7304b3ed..3d2eeea503 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -88,6 +88,18 @@ class AssetTagHelperTest < ActionView::TestCase
%(path_to_javascript("/super/xmlhr.js")) => %(/super/xmlhr.js)
}
+ JavascriptUrlToTag = {
+ %(javascript_url("xmlhr")) => %(http://www.example.com/javascripts/xmlhr.js),
+ %(javascript_url("super/xmlhr")) => %(http://www.example.com/javascripts/super/xmlhr.js),
+ %(javascript_url("/super/xmlhr.js")) => %(http://www.example.com/super/xmlhr.js)
+ }
+
+ UrlToJavascriptToTag = {
+ %(url_to_javascript("xmlhr")) => %(http://www.example.com/javascripts/xmlhr.js),
+ %(url_to_javascript("super/xmlhr")) => %(http://www.example.com/javascripts/super/xmlhr.js),
+ %(url_to_javascript("/super/xmlhr.js")) => %(http://www.example.com/super/xmlhr.js)
+ }
+
JavascriptIncludeToTag = {
%(javascript_include_tag("bank")) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>),
%(javascript_include_tag("bank.js")) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>),
@@ -119,6 +131,20 @@ class AssetTagHelperTest < ActionView::TestCase
%(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss)
}
+ StyleUrlToTag = {
+ %(stylesheet_url("bank")) => %(http://www.example.com/stylesheets/bank.css),
+ %(stylesheet_url("bank.css")) => %(http://www.example.com/stylesheets/bank.css),
+ %(stylesheet_url('subdir/subdir')) => %(http://www.example.com/stylesheets/subdir/subdir.css),
+ %(stylesheet_url('/subdir/subdir.css')) => %(http://www.example.com/subdir/subdir.css)
+ }
+
+ UrlToStyleToTag = {
+ %(url_to_stylesheet("style")) => %(http://www.example.com/stylesheets/style.css),
+ %(url_to_stylesheet("style.css")) => %(http://www.example.com/stylesheets/style.css),
+ %(url_to_stylesheet('dir/file')) => %(http://www.example.com/stylesheets/dir/file.css),
+ %(url_to_stylesheet('/dir/file.rcss')) => %(http://www.example.com/dir/file.rcss)
+ }
+
StyleLinkToTag = {
%(stylesheet_link_tag("bank")) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />),
%(stylesheet_link_tag("bank.css")) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />),
@@ -149,6 +175,20 @@ class AssetTagHelperTest < ActionView::TestCase
%(path_to_image("/dir/xml.png")) => %(/dir/xml.png)
}
+ ImageUrlToTag = {
+ %(image_url("xml")) => %(http://www.example.com/images/xml),
+ %(image_url("xml.png")) => %(http://www.example.com/images/xml.png),
+ %(image_url("dir/xml.png")) => %(http://www.example.com/images/dir/xml.png),
+ %(image_url("/dir/xml.png")) => %(http://www.example.com/dir/xml.png)
+ }
+
+ UrlToImageToTag = {
+ %(url_to_image("xml")) => %(http://www.example.com/images/xml),
+ %(url_to_image("xml.png")) => %(http://www.example.com/images/xml.png),
+ %(url_to_image("dir/xml.png")) => %(http://www.example.com/images/dir/xml.png),
+ %(url_to_image("/dir/xml.png")) => %(http://www.example.com/dir/xml.png)
+ }
+
ImageLinkToTag = {
%(image_tag("xml.png")) => %(<img alt="Xml" src="/images/xml.png" />),
%(image_tag("rss.gif", :alt => "rss syndication")) => %(<img alt="rss syndication" src="/images/rss.gif" />),
@@ -168,7 +208,7 @@ class AssetTagHelperTest < ActionView::TestCase
}
FaviconLinkToTag = {
- %(favicon_link_tag) => %(<link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
+ %(favicon_link_tag) => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico') => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico', :rel => 'foo') => %(<link href="/images/favicon.ico" rel="foo" type="image/vnd.microsoft.icon" />),
%(favicon_link_tag 'favicon.ico', :rel => 'foo', :type => 'bar') => %(<link href="/images/favicon.ico" rel="foo" type="bar" />),
@@ -189,23 +229,38 @@ class AssetTagHelperTest < ActionView::TestCase
%(path_to_video("/dir/xml.ogg")) => %(/dir/xml.ogg)
}
+ VideoUrlToTag = {
+ %(video_url("xml")) => %(http://www.example.com/videos/xml),
+ %(video_url("xml.ogg")) => %(http://www.example.com/videos/xml.ogg),
+ %(video_url("dir/xml.ogg")) => %(http://www.example.com/videos/dir/xml.ogg),
+ %(video_url("/dir/xml.ogg")) => %(http://www.example.com/dir/xml.ogg)
+ }
+
+ UrlToVideoToTag = {
+ %(url_to_video("xml")) => %(http://www.example.com/videos/xml),
+ %(url_to_video("xml.ogg")) => %(http://www.example.com/videos/xml.ogg),
+ %(url_to_video("dir/xml.ogg")) => %(http://www.example.com/videos/dir/xml.ogg),
+ %(url_to_video("/dir/xml.ogg")) => %(http://www.example.com/dir/xml.ogg)
+ }
+
VideoLinkToTag = {
- %(video_tag("xml.ogg")) => %(<video src="/videos/xml.ogg" />),
- %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="autoplay" controls="controls" src="/videos/rss.m4v" />),
- %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="autobuffer" src="/videos/rss.m4v" />),
- %(video_tag("gold.m4v", :size => "160x120")) => %(<video height="120" src="/videos/gold.m4v" width="160" />),
- %(video_tag("gold.m4v", "size" => "320x240")) => %(<video height="240" src="/videos/gold.m4v" width="320" />),
- %(video_tag("trailer.ogg", :poster => "screenshot.png")) => %(<video poster="/images/screenshot.png" src="/videos/trailer.ogg" />),
- %(video_tag("error.avi", "size" => "100")) => %(<video src="/videos/error.avi" />),
- %(video_tag("error.avi", "size" => "100 x 100")) => %(<video src="/videos/error.avi" />),
- %(video_tag("error.avi", "size" => "x")) => %(<video src="/videos/error.avi" />),
- %(video_tag("http://media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="http://media.rubyonrails.org/video/rails_blog_2.mov" />),
- %(video_tag("//media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="//media.rubyonrails.org/video/rails_blog_2.mov" />),
- %(video_tag(["multiple.ogg", "multiple.avi"])) => %(<video><source src="multiple.ogg" /><source src="multiple.avi" /></video>),
- %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="controls" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>)
+ %(video_tag("xml.ogg")) => %(<video src="/videos/xml.ogg"></video>),
+ %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="autoplay" controls="controls" src="/videos/rss.m4v"></video>),
+ %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="autobuffer" src="/videos/rss.m4v"></video>),
+ %(video_tag("gold.m4v", :size => "160x120")) => %(<video height="120" src="/videos/gold.m4v" width="160"></video>),
+ %(video_tag("gold.m4v", "size" => "320x240")) => %(<video height="240" src="/videos/gold.m4v" width="320"></video>),
+ %(video_tag("trailer.ogg", :poster => "screenshot.png")) => %(<video poster="/images/screenshot.png" src="/videos/trailer.ogg"></video>),
+ %(video_tag("error.avi", "size" => "100")) => %(<video src="/videos/error.avi"></video>),
+ %(video_tag("error.avi", "size" => "100 x 100")) => %(<video src="/videos/error.avi"></video>),
+ %(video_tag("error.avi", "size" => "x")) => %(<video src="/videos/error.avi"></video>),
+ %(video_tag("http://media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="http://media.rubyonrails.org/video/rails_blog_2.mov"></video>),
+ %(video_tag("//media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="//media.rubyonrails.org/video/rails_blog_2.mov"></video>),
+ %(video_tag("multiple.ogg", "multiple.avi")) => %(<video><source src="/videos/multiple.ogg" /><source src="/videos/multiple.avi" /></video>),
+ %(video_tag(["multiple.ogg", "multiple.avi"])) => %(<video><source src="/videos/multiple.ogg" /><source src="/videos/multiple.avi" /></video>),
+ %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="controls" height="120" width="160"><source src="/videos/multiple.ogg" /><source src="/videos/multiple.avi" /></video>)
}
- AudioPathToTag = {
+ AudioPathToTag = {
%(audio_path("xml")) => %(/audios/xml),
%(audio_path("xml.wav")) => %(/audios/xml.wav),
%(audio_path("dir/xml.wav")) => %(/audios/dir/xml.wav),
@@ -219,11 +274,28 @@ class AssetTagHelperTest < ActionView::TestCase
%(path_to_audio("/dir/xml.wav")) => %(/dir/xml.wav)
}
+ AudioUrlToTag = {
+ %(audio_url("xml")) => %(http://www.example.com/audios/xml),
+ %(audio_url("xml.wav")) => %(http://www.example.com/audios/xml.wav),
+ %(audio_url("dir/xml.wav")) => %(http://www.example.com/audios/dir/xml.wav),
+ %(audio_url("/dir/xml.wav")) => %(http://www.example.com/dir/xml.wav)
+ }
+
+ UrlToAudioToTag = {
+ %(url_to_audio("xml")) => %(http://www.example.com/audios/xml),
+ %(url_to_audio("xml.wav")) => %(http://www.example.com/audios/xml.wav),
+ %(url_to_audio("dir/xml.wav")) => %(http://www.example.com/audios/dir/xml.wav),
+ %(url_to_audio("/dir/xml.wav")) => %(http://www.example.com/dir/xml.wav)
+ }
+
AudioLinkToTag = {
- %(audio_tag("xml.wav")) => %(<audio src="/audios/xml.wav" />),
- %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="autoplay" controls="controls" src="/audios/rss.wav" />),
- %(audio_tag("http://media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="http://media.rubyonrails.org/audio/rails_blog_2.mov" />),
- %(audio_tag("//media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="//media.rubyonrails.org/audio/rails_blog_2.mov" />),
+ %(audio_tag("xml.wav")) => %(<audio src="/audios/xml.wav"></audio>),
+ %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="autoplay" controls="controls" src="/audios/rss.wav"></audio>),
+ %(audio_tag("http://media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="http://media.rubyonrails.org/audio/rails_blog_2.mov"></audio>),
+ %(audio_tag("//media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="//media.rubyonrails.org/audio/rails_blog_2.mov"></audio>),
+ %(audio_tag("audio.mp3", "audio.ogg")) => %(<audio><source src="/audios/audio.mp3" /><source src="/audios/audio.ogg" /></audio>),
+ %(audio_tag(["audio.mp3", "audio.ogg"])) => %(<audio><source src="/audios/audio.mp3" /><source src="/audios/audio.ogg" /></audio>),
+ %(audio_tag(["audio.mp3", "audio.ogg"], :autobuffer => true, :controls => true)) => %(<audio autobuffer="autobuffer" controls="controls"><source src="/audios/audio.mp3" /><source src="/audios/audio.ogg" /></audio>)
}
def test_auto_discovery_link_tag
@@ -238,6 +310,14 @@ class AssetTagHelperTest < ActionView::TestCase
PathToJavascriptToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_javascript_url
+ JavascriptUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_url_to_javascript_alias_for_javascript_url
+ UrlToJavascriptToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_javascript_include_tag_with_blank_asset_id
ENV["RAILS_ASSET_ID"] = ""
JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
@@ -345,6 +425,15 @@ class AssetTagHelperTest < ActionView::TestCase
PathToStyleToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_stylesheet_url
+ ENV["RAILS_ASSET_ID"] = ""
+ StyleUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_url_to_stylesheet_alias_for_stylesheet_url
+ UrlToStyleToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_stylesheet_link_tag
ENV["RAILS_ASSET_ID"] = ""
StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
@@ -429,6 +518,14 @@ class AssetTagHelperTest < ActionView::TestCase
PathToImageToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_image_url
+ ImageUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_url_to_image_alias_for_image_url
+ UrlToImageToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_image_alt
[nil, '/', '/foo/bar/', 'foo/bar/'].each do |prefix|
assert_equal 'Rails', image_alt("#{prefix}rails.png")
@@ -441,6 +538,12 @@ class AssetTagHelperTest < ActionView::TestCase
ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_image_tag_does_not_modify_options
+ options = {:size => '16x10'}
+ image_tag('icon', options)
+ assert_equal({:size => '16x10'}, options)
+ end
+
def test_favicon_link_tag
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -468,6 +571,14 @@ class AssetTagHelperTest < ActionView::TestCase
PathToVideoToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_video_url
+ VideoUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_url_to_video_alias_for_video_url
+ UrlToVideoToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_video_tag
VideoLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -480,10 +591,26 @@ class AssetTagHelperTest < ActionView::TestCase
PathToAudioToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_audio_url
+ AudioUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_url_to_audio_alias_for_audio_url
+ UrlToAudioToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_audio_tag
AudioLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_video_audio_tag_does_not_modify_options
+ options = {:autoplay => true}
+ video_tag('video', options)
+ assert_equal({:autoplay => true}, options)
+ audio_tag('audio', options)
+ assert_equal({:autoplay => true}, options)
+ end
+
def test_timebased_asset_id
expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
@@ -555,7 +682,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
end
-
@controller.request.stubs(:ssl?).returns(false)
assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png")
@@ -1123,6 +1249,22 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
end
+ def test_should_compute_proper_url_with_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
+ assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_url("xmlhr"))
+ assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_url("style"))
+ assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_url("xml.png"))
+ end
+
+ def test_should_compute_proper_url_with_asset_host_and_default_protocol
+ @controller.config.asset_host = "assets.example.com"
+ @controller.config.default_asset_host_protocol = :request
+ assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_url("xmlhr"))
+ assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_url("style"))
+ assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_url("xml.png"))
+ end
+
def test_should_ignore_asset_host_on_complete_url
@controller.config.asset_host = "http://assets.example.com"
assert_dom_equal(%(<link href="http://bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />), stylesheet_link_tag("http://bar.example.com/stylesheets/style.css"))
@@ -1136,16 +1278,19 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
def test_should_wildcard_asset_host_between_zero_and_four
@controller.config.asset_host = 'http://a%d.example.com'
assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png'))
+ assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_url('xml.png'))
end
def test_asset_host_without_protocol_should_be_protocol_relative
@controller.config.asset_host = 'a.example.com'
assert_equal 'gopher://a.example.com/collaboration/hieraki/images/xml.png', image_path('xml.png')
+ assert_equal 'gopher://a.example.com/collaboration/hieraki/images/xml.png', image_url('xml.png')
end
def test_asset_host_without_protocol_should_be_protocol_relative_even_if_path_present
@controller.config.asset_host = 'a.example.com/files/go/here'
assert_equal 'gopher://a.example.com/files/go/here/collaboration/hieraki/images/xml.png', image_path('xml.png')
+ assert_equal 'gopher://a.example.com/files/go/here/collaboration/hieraki/images/xml.png', image_url('xml.png')
end
def test_assert_css_and_js_of_the_same_name_return_correct_extension
diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb
index 81d7444cf8..d26aa9aa85 100644
--- a/actionpack/test/template/atom_feed_helper_test.rb
+++ b/actionpack/test/template/atom_feed_helper_test.rb
@@ -16,7 +16,7 @@ class ScrollsController < ActionController::Base
feed.title("My great blog!")
feed.updated((@scrolls.first.created_at))
- @scrolls.each do |scroll|
+ @scrolls.each do |scroll|
feed.entry(scroll) do |entry|
entry.title(scroll.title)
entry.content(scroll.body, :type => 'html')
@@ -185,11 +185,6 @@ class ScrollsController < ActionController::Base
render :inline => FEEDS[params[:id]], :type => :builder
end
-
- protected
- def rescue_action(e)
- raise(e)
- end
end
class AtomFeedTest < ActionController::TestCase
diff --git a/actionpack/test/template/benchmark_helper_test.rb b/actionpack/test/template/benchmark_helper_test.rb
new file mode 100644
index 0000000000..1bdda22959
--- /dev/null
+++ b/actionpack/test/template/benchmark_helper_test.rb
@@ -0,0 +1,24 @@
+require 'abstract_unit'
+require 'stringio'
+
+class BenchmarkHelperTest < ActionView::TestCase
+ include RenderERBUtils
+ tests ActionView::Helpers::BenchmarkHelper
+
+ def test_output_in_erb
+ output = render_erb("Hello <%= benchmark do %>world<% end %>")
+ expected = 'Hello world'
+ assert_equal expected, output
+ end
+
+ def test_returns_value_from_block
+ assert_equal 'test', benchmark { 'test' }
+ end
+
+ def test_default_message
+ log = StringIO.new
+ self.stubs(:logger).returns(Logger.new(log))
+ benchmark {}
+ assert_match(log.rewind && log.read, /Benchmarking \(\d+.\d+ms\)/)
+ end
+end
diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb
index 13e2d5b595..17469f8c3e 100644
--- a/actionpack/test/template/capture_helper_test.rb
+++ b/actionpack/test/template/capture_helper_test.rb
@@ -53,6 +53,13 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal 'foobar', content_for(:title)
end
+ def test_content_for_with_multiple_calls_and_flush
+ assert ! content_for?(:title)
+ content_for :title, 'foo'
+ content_for :title, 'bar', true
+ assert_equal 'bar', content_for(:title)
+ end
+
def test_content_for_with_block
assert ! content_for?(:title)
content_for :title do
@@ -63,6 +70,39 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal 'foobar', content_for(:title)
end
+ def test_content_for_with_block_and_multiple_calls_with_flush
+ assert ! content_for?(:title)
+ content_for :title do
+ 'foo'
+ end
+ content_for :title, true do
+ 'bar'
+ end
+ assert_equal 'bar', content_for(:title)
+ end
+
+ def test_content_for_with_block_and_multiple_calls_with_flush_nil_content
+ assert ! content_for?(:title)
+ content_for :title do
+ 'foo'
+ end
+ content_for :title, nil, true do
+ 'bar'
+ end
+ assert_equal 'bar', content_for(:title)
+ end
+
+ def test_content_for_with_block_and_multiple_calls_without_flush
+ assert ! content_for?(:title)
+ content_for :title do
+ 'foo'
+ end
+ content_for :title, false do
+ 'bar'
+ end
+ assert_equal 'foobar', content_for(:title)
+ end
+
def test_content_for_with_whitespace_block
assert ! content_for?(:title)
content_for :title, 'foo'
@@ -74,12 +114,27 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal 'foobar', content_for(:title)
end
+ def test_content_for_with_whitespace_block_and_flush
+ assert ! content_for?(:title)
+ content_for :title, 'foo'
+ content_for :title, true do
+ output_buffer << " \n "
+ nil
+ end
+ content_for :title, 'bar', true
+ assert_equal 'bar', content_for(:title)
+ end
+
def test_content_for_returns_nil_when_writing
assert ! content_for?(:title)
assert_equal nil, content_for(:title, 'foo')
assert_equal nil, content_for(:title) { output_buffer << 'bar'; nil }
assert_equal nil, content_for(:title) { output_buffer << " \n "; nil }
assert_equal 'foobar', content_for(:title)
+ assert_equal nil, content_for(:title, 'foo', true)
+ assert_equal nil, content_for(:title, true) { output_buffer << 'bar'; nil }
+ assert_equal nil, content_for(:title, true) { output_buffer << " \n "; nil }
+ assert_equal 'bar', content_for(:title)
end
def test_content_for_question_mark
@@ -131,17 +186,15 @@ class CaptureHelperTest < ActionView::TestCase
assert buffer.equal?(@av.output_buffer)
end
- unless RUBY_VERSION < '1.9'
- def test_with_output_buffer_sets_proper_encoding
- @av.output_buffer = ActionView::OutputBuffer.new
+ def test_with_output_buffer_sets_proper_encoding
+ @av.output_buffer = ActionView::OutputBuffer.new
- # Ensure we set the output buffer to an encoding different than the default one.
- alt_encoding = alt_encoding(@av.output_buffer)
- @av.output_buffer.force_encoding(alt_encoding)
+ # Ensure we set the output buffer to an encoding different than the default one.
+ alt_encoding = alt_encoding(@av.output_buffer)
+ @av.output_buffer.force_encoding(alt_encoding)
- @av.with_output_buffer do
- assert_equal alt_encoding, @av.output_buffer.encoding
- end
+ @av.with_output_buffer do
+ assert_equal alt_encoding, @av.output_buffer.encoding
end
end
@@ -165,14 +218,12 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal '', view.output_buffer
end
- unless RUBY_VERSION < '1.9'
- def test_flush_output_buffer_preserves_the_encoding_of_the_output_buffer
- view = view_with_controller
- alt_encoding = alt_encoding(view.output_buffer)
- view.output_buffer.force_encoding(alt_encoding)
- flush_output_buffer
- assert_equal alt_encoding, view.output_buffer.encoding
- end
+ def test_flush_output_buffer_preserves_the_encoding_of_the_output_buffer
+ view = view_with_controller
+ alt_encoding = alt_encoding(view.output_buffer)
+ view.output_buffer.force_encoding(alt_encoding)
+ flush_output_buffer
+ assert_equal alt_encoding, view.output_buffer.encoding
end
def alt_encoding(output_buffer)
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
index 8fc78283d8..f5dc2fbb33 100644
--- a/actionpack/test/template/compiled_templates_test.rb
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -1,8 +1,12 @@
require 'abstract_unit'
require 'controller/fake_models'
-class CompiledTemplatesTest < Test::Unit::TestCase
+class CompiledTemplatesTest < ActiveSupport::TestCase
def setup
+ # Clean up any details key cached to expose failures
+ # that otherwise would appear just on isolated tests
+ ActionView::LookupContext::DetailsKey.clear
+
@compiled_templates = ActionView::CompiledTemplates
@compiled_templates.instance_methods.each do |m|
@compiled_templates.send(:remove_method, m) if m =~ /^_render_template_/
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
index d45215acfd..82b62e64f3 100644
--- a/actionpack/test/template/date_helper_i18n_test.rb
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
+class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase
include ActionView::Helpers::DateHelper
attr_reader :request
@@ -71,7 +71,7 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
end
end
-class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
+class DateHelperSelectTagsI18nTests < ActiveSupport::TestCase
include ActionView::Helpers::DateHelper
attr_reader :request
@@ -103,7 +103,7 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
I18n.expects(:translate).with(('datetime.prompts.' + key.to_s).to_sym, :locale => 'en').returns prompt
end
- I18n.expects(:translate).with(:'date.order', :locale => 'en').returns [:year, :month, :day]
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:year, :month, :day]
datetime_select('post', 'updated_at', :locale => 'en', :include_seconds => true, :prompt => true)
end
@@ -115,7 +115,15 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
end
def test_date_or_time_select_given_no_order_options_translates_order
- I18n.expects(:translate).with(:'date.order', :locale => 'en').returns [:year, :month, :day]
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:year, :month, :day]
datetime_select('post', 'updated_at', :locale => 'en')
end
+
+ def test_date_or_time_select_given_invalid_order
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:invalid, :month, :day]
+
+ assert_raise StandardError do
+ datetime_select('post', 'updated_at', :locale => 'en')
+ end
+ end
end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index af30ec9892..9e2f4ec347 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -164,6 +164,15 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_day(nil, :include_blank => true)
end
+ def test_select_day_with_two_digit_numbers
+ expected = %(<select id="date_day" name="date[day]">\n)
+ expected << %(<option value="1">01</option>\n<option selected="selected" value="2">02</option>\n<option value="3">03</option>\n<option value="4">04</option>\n<option value="5">05</option>\n<option value="6">06</option>\n<option value="7">07</option>\n<option value="8">08</option>\n<option value="9">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_day(Time.mktime(2011, 8, 2), :use_two_digit_numbers => true)
+ assert_dom_equal expected, select_day(2, :use_two_digit_numbers => true)
+ end
+
def test_select_day_with_html_options
expected = %(<select id="date_day" name="date[day]" class="selector">\n)
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
@@ -198,6 +207,15 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_month(8)
end
+ def test_select_month_with_two_digit_numbers
+ expected = %(<select id="date_month" name="date[month]">\n)
+ expected << %(<option value="1">01</option>\n<option value="2">02</option>\n<option value="3">03</option>\n<option value="4">04</option>\n<option value="5">05</option>\n<option value="6">06</option>\n<option value="7">07</option>\n<option value="8" selected="selected">08</option>\n<option value="9">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_month(Time.mktime(2011, 8, 16), :use_two_digit_numbers => true)
+ assert_dom_equal expected, select_month(8, :use_two_digit_numbers => true)
+ end
+
def test_select_month_with_disabled
expected = %(<select id="date_month" name="date[month]" disabled="disabled">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
@@ -986,7 +1004,6 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :ampm => true)
end
-
def test_select_datetime_with_separators
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -1379,6 +1396,25 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on", :order => [ :month, :year ])
end
+ def test_date_select_without_day_with_separator
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = "<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << "/"
+
+ expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", :date_separator => '/', :order => [ :month, :year ])
+ end
+
def test_date_select_without_day_and_with_disabled_html_option
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
@@ -1551,7 +1587,7 @@ class DateHelperTest < ActionView::TestCase
start_year = Time.now.year-5
end_year = Time.now.year+5
- expected = '<input name="post[written_on(3i)]" type="hidden" id="post_written_on_3i"/>' + "\n"
+ expected = '<input name="post[written_on(3i)]" type="hidden" id="post_written_on_3i" value="1"/>' + "\n"
expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << "<option value=\"\"></option>\n"
start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
@@ -1565,6 +1601,40 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on", :order=>[:year, :month], :include_blank=>true)
end
+ def test_date_select_with_nil_and_blank_and_discard_month
+ @post = Post.new
+
+ start_year = Time.now.year-5
+ end_year = Time.now.year+5
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << "<option value=\"\"></option>\n"
+ start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+ expected << "</select>\n"
+ expected << '<input name="post[written_on(2i)]" type="hidden" id="post_written_on_2i" value="1"/>' + "\n"
+ expected << '<input name="post[written_on(3i)]" type="hidden" id="post_written_on_3i" value="1"/>' + "\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", :discard_month => true, :include_blank=>true)
+ end
+
+ def test_date_select_with_nil_and_blank_and_discard_year
+ @post = Post.new
+
+ expected = '<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="1" />' + "\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+ expected << "<option value=\"\"></option>\n"
+ 1.upto(12) { |i| expected << %(<option value="#{i}">#{Date::MONTHNAMES[i]}</option>\n) }
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+ expected << "<option value=\"\"></option>\n"
+ 1.upto(31) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", :discard_year => true, :include_blank=>true)
+ end
+
def test_date_select_cant_override_discard_hour
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
diff --git a/actionpack/test/template/erb_util_test.rb b/actionpack/test/template/erb_util_test.rb
index 790ab1c74c..ca2710e9b3 100644
--- a/actionpack/test/template/erb_util_test.rb
+++ b/actionpack/test/template/erb_util_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/object/inclusion'
-class ErbUtilTest < Test::Unit::TestCase
+class ErbUtilTest < ActiveSupport::TestCase
include ERB::Util
ERB::Util::HTML_ESCAPE.each do |given, expected|
@@ -44,4 +44,18 @@ class ErbUtilTest < Test::Unit::TestCase
assert_equal chr, html_escape(chr)
end
end
+
+ def test_html_escape_once
+ assert_equal '1 &lt; 2 &amp; 3', html_escape_once('1 < 2 &amp; 3')
+ end
+
+ def test_html_escape_once_returns_unsafe_strings_when_passed_unsafe_strings
+ value = html_escape_once('1 < 2 &amp; 3')
+ assert !value.html_safe?
+ end
+
+ def test_html_escape_once_returns_safe_strings_when_passed_safe_strings
+ value = html_escape_once('1 < 2 &amp; 3'.html_safe)
+ assert value.html_safe?
+ end
end
diff --git a/actionpack/test/template/form_collections_helper_test.rb b/actionpack/test/template/form_collections_helper_test.rb
new file mode 100644
index 0000000000..4d878635ef
--- /dev/null
+++ b/actionpack/test/template/form_collections_helper_test.rb
@@ -0,0 +1,327 @@
+require 'abstract_unit'
+
+class Category < Struct.new(:id, :name)
+end
+
+class FormCollectionsHelperTest < ActionView::TestCase
+ def assert_no_select(selector, value = nil)
+ assert_select(selector, :text => value, :count => 0)
+ end
+
+ def with_collection_radio_buttons(*args, &block)
+ @output_buffer = collection_radio_buttons(*args, &block)
+ end
+
+ def with_collection_check_boxes(*args, &block)
+ @output_buffer = collection_check_boxes(*args, &block)
+ end
+
+ # COLLECTION RADIO BUTTONS
+ test 'collection radio accepts a collection and generate inputs from value method' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s
+
+ assert_select 'input[type=radio][value=true]#user_active_true'
+ assert_select 'input[type=radio][value=false]#user_active_false'
+ end
+
+ test 'collection radio accepts a collection and generate inputs from label method' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s
+
+ assert_select 'label[for=user_active_true]', 'true'
+ assert_select 'label[for=user_active_false]', 'false'
+ end
+
+ test 'collection radio handles camelized collection values for labels correctly' do
+ with_collection_radio_buttons :user, :active, ['Yes', 'No'], :to_s, :to_s
+
+ assert_select 'label[for=user_active_yes]', 'Yes'
+ assert_select 'label[for=user_active_no]', 'No'
+ end
+
+ test 'colection radio should sanitize collection values for labels correctly' do
+ with_collection_radio_buttons :user, :name, ['$0.99', '$1.99'], :to_s, :to_s
+ assert_select 'label[for=user_name_099]', '$0.99'
+ assert_select 'label[for=user_name_199]', '$1.99'
+ end
+
+ test 'collection radio accepts checked item' do
+ with_collection_radio_buttons :user, :active, [[1, true], [0, false]], :last, :first, :checked => true
+
+ assert_select 'input[type=radio][value=true][checked=checked]'
+ assert_no_select 'input[type=radio][value=false][checked=checked]'
+ end
+
+ test 'collection radio accepts multiple disabled items' do
+ collection = [[1, true], [0, false], [2, 'other']]
+ with_collection_radio_buttons :user, :active, collection, :last, :first, :disabled => [true, false]
+
+ assert_select 'input[type=radio][value=true][disabled=disabled]'
+ assert_select 'input[type=radio][value=false][disabled=disabled]'
+ assert_no_select 'input[type=radio][value=other][disabled=disabled]'
+ end
+
+ test 'collection radio accepts single disable item' do
+ collection = [[1, true], [0, false]]
+ with_collection_radio_buttons :user, :active, collection, :last, :first, :disabled => true
+
+ assert_select 'input[type=radio][value=true][disabled=disabled]'
+ assert_no_select 'input[type=radio][value=false][disabled=disabled]'
+ end
+
+ test 'collection radio accepts html options as input' do
+ collection = [[1, true], [0, false]]
+ with_collection_radio_buttons :user, :active, collection, :last, :first, {}, :class => 'special-radio'
+
+ assert_select 'input[type=radio][value=true].special-radio#user_active_true'
+ assert_select 'input[type=radio][value=false].special-radio#user_active_false'
+ end
+
+ test 'collection radio does not wrap input inside the label' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s
+
+ assert_select 'input[type=radio] + label'
+ assert_no_select 'label input'
+ end
+
+ test 'collection radio accepts a block to render the label as radio button wrapper' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label { b.radio_button }
+ end
+
+ assert_select 'label[for=user_active_true] > input#user_active_true[type=radio]'
+ assert_select 'label[for=user_active_false] > input#user_active_false[type=radio]'
+ end
+
+ test 'collection radio accepts a block to change the order of label and radio button' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label + b.radio_button
+ end
+
+ assert_select 'label[for=user_active_true] + input#user_active_true[type=radio]'
+ assert_select 'label[for=user_active_false] + input#user_active_false[type=radio]'
+ end
+
+ test 'collection radio with block helpers accept extra html options' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label(:class => "radio_button") + b.radio_button(:class => "radio_button")
+ end
+
+ assert_select 'label.radio_button[for=user_active_true] + input#user_active_true.radio_button[type=radio]'
+ assert_select 'label.radio_button[for=user_active_false] + input#user_active_false.radio_button[type=radio]'
+ end
+
+ test 'collection radio with block helpers allows access to current text and value' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label(:"data-value" => b.value) { b.radio_button + b.text }
+ end
+
+ assert_select 'label[for=user_active_true][data-value=true]', 'true' do
+ assert_select 'input#user_active_true[type=radio]'
+ end
+ assert_select 'label[for=user_active_false][data-value=false]', 'false' do
+ assert_select 'input#user_active_false[type=radio]'
+ end
+ end
+
+ test 'collection radio with block helpers allows access to the current object item in the collection to access extra properties' do
+ with_collection_radio_buttons :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label(:class => b.object) { b.radio_button + b.text }
+ end
+
+ assert_select 'label.true[for=user_active_true]', 'true' do
+ assert_select 'input#user_active_true[type=radio]'
+ end
+ assert_select 'label.false[for=user_active_false]', 'false' do
+ assert_select 'input#user_active_false[type=radio]'
+ end
+ end
+
+ test 'collection radio buttons with fields for' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ @output_buffer = fields_for(:post) do |p|
+ p.collection_radio_buttons :category_id, collection, :id, :name
+ end
+
+ assert_select 'input#post_category_id_1[type=radio][value=1]'
+ assert_select 'input#post_category_id_2[type=radio][value=2]'
+
+ assert_select 'label[for=post_category_id_1]', 'Category 1'
+ assert_select 'label[for=post_category_id_2]', 'Category 2'
+ end
+
+ # COLLECTION CHECK BOXES
+ test 'collection check boxes accepts a collection and generate a serie of checkboxes for value method' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_check_boxes :user, :category_ids, collection, :id, :name
+
+ assert_select 'input#user_category_ids_1[type=checkbox][value=1]'
+ assert_select 'input#user_category_ids_2[type=checkbox][value=2]'
+ end
+
+ test 'collection check boxes generates only one hidden field for the entire collection, to ensure something will be sent back to the server when posting an empty collection' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_check_boxes :user, :category_ids, collection, :id, :name
+
+ assert_select "input[type=hidden][name='user[category_ids][]'][value=]", :count => 1
+ end
+
+ test 'collection check boxes accepts a collection and generate a serie of checkboxes with labels for label method' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_check_boxes :user, :category_ids, collection, :id, :name
+
+ assert_select 'label[for=user_category_ids_1]', 'Category 1'
+ assert_select 'label[for=user_category_ids_2]', 'Category 2'
+ end
+
+ test 'collection check boxes handles camelized collection values for labels correctly' do
+ with_collection_check_boxes :user, :active, ['Yes', 'No'], :to_s, :to_s
+
+ assert_select 'label[for=user_active_yes]', 'Yes'
+ assert_select 'label[for=user_active_no]', 'No'
+ end
+
+ test 'colection check box should sanitize collection values for labels correctly' do
+ with_collection_check_boxes :user, :name, ['$0.99', '$1.99'], :to_s, :to_s
+ assert_select 'label[for=user_name_099]', '$0.99'
+ assert_select 'label[for=user_name_199]', '$1.99'
+ end
+
+ test 'collection check boxes accepts selected values as :checked option' do
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => [1, 3]
+
+ assert_select 'input[type=checkbox][value=1][checked=checked]'
+ assert_select 'input[type=checkbox][value=3][checked=checked]'
+ assert_no_select 'input[type=checkbox][value=2][checked=checked]'
+ end
+
+ test 'collection check boxes accepts a single checked value' do
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, :checked => 3
+
+ assert_select 'input[type=checkbox][value=3][checked=checked]'
+ assert_no_select 'input[type=checkbox][value=1][checked=checked]'
+ assert_no_select 'input[type=checkbox][value=2][checked=checked]'
+ end
+
+ test 'collection check boxes accepts selected values as :checked option and override the model values' do
+ user = Struct.new(:category_ids).new(2)
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+
+ @output_buffer = fields_for(:user, user) do |p|
+ p.collection_check_boxes :category_ids, collection, :first, :last, :checked => [1, 3]
+ end
+
+ assert_select 'input[type=checkbox][value=1][checked=checked]'
+ assert_select 'input[type=checkbox][value=3][checked=checked]'
+ assert_no_select 'input[type=checkbox][value=2][checked=checked]'
+ end
+
+ test 'collection check boxes accepts multiple disabled items' do
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, :disabled => [1, 3]
+
+ assert_select 'input[type=checkbox][value=1][disabled=disabled]'
+ assert_select 'input[type=checkbox][value=3][disabled=disabled]'
+ assert_no_select 'input[type=checkbox][value=2][disabled=disabled]'
+ end
+
+ test 'collection check boxes accepts single disable item' do
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, :disabled => 1
+
+ assert_select 'input[type=checkbox][value=1][disabled=disabled]'
+ assert_no_select 'input[type=checkbox][value=3][disabled=disabled]'
+ assert_no_select 'input[type=checkbox][value=2][disabled=disabled]'
+ end
+
+ test 'collection check boxes accepts a proc to disabled items' do
+ collection = (1..3).map{|i| [i, "Category #{i}"] }
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, :disabled => proc { |i| i.first == 1 }
+
+ assert_select 'input[type=checkbox][value=1][disabled=disabled]'
+ assert_no_select 'input[type=checkbox][value=3][disabled=disabled]'
+ assert_no_select 'input[type=checkbox][value=2][disabled=disabled]'
+ end
+
+ test 'collection check boxes accepts html options' do
+ collection = [[1, 'Category 1'], [2, 'Category 2']]
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, {}, :class => 'check'
+
+ assert_select 'input.check[type=checkbox][value=1]'
+ assert_select 'input.check[type=checkbox][value=2]'
+ end
+
+ test 'collection check boxes with fields for' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ @output_buffer = fields_for(:post) do |p|
+ p.collection_check_boxes :category_ids, collection, :id, :name
+ end
+
+ assert_select 'input#post_category_ids_1[type=checkbox][value=1]'
+ assert_select 'input#post_category_ids_2[type=checkbox][value=2]'
+
+ assert_select 'label[for=post_category_ids_1]', 'Category 1'
+ assert_select 'label[for=post_category_ids_2]', 'Category 2'
+ end
+
+ test 'collection check boxes does not wrap input inside the label' do
+ with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s
+
+ assert_select 'input[type=checkbox] + label'
+ assert_no_select 'label input'
+ end
+
+ test 'collection check boxes accepts a block to render the label as check box wrapper' do
+ with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label { b.check_box }
+ end
+
+ assert_select 'label[for=user_active_true] > input#user_active_true[type=checkbox]'
+ assert_select 'label[for=user_active_false] > input#user_active_false[type=checkbox]'
+ end
+
+ test 'collection check boxes accepts a block to change the order of label and check box' do
+ with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label + b.check_box
+ end
+
+ assert_select 'label[for=user_active_true] + input#user_active_true[type=checkbox]'
+ assert_select 'label[for=user_active_false] + input#user_active_false[type=checkbox]'
+ end
+
+ test 'collection check boxes with block helpers accept extra html options' do
+ with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label(:class => "check_box") + b.check_box(:class => "check_box")
+ end
+
+ assert_select 'label.check_box[for=user_active_true] + input#user_active_true.check_box[type=checkbox]'
+ assert_select 'label.check_box[for=user_active_false] + input#user_active_false.check_box[type=checkbox]'
+ end
+
+ test 'collection check boxes with block helpers allows access to current text and value' do
+ with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label(:"data-value" => b.value) { b.check_box + b.text }
+ end
+
+ assert_select 'label[for=user_active_true][data-value=true]', 'true' do
+ assert_select 'input#user_active_true[type=checkbox]'
+ end
+ assert_select 'label[for=user_active_false][data-value=false]', 'false' do
+ assert_select 'input#user_active_false[type=checkbox]'
+ end
+ end
+
+ test 'collection check boxes with block helpers allows access to the current object item in the collection to access extra properties' do
+ with_collection_check_boxes :user, :active, [true, false], :to_s, :to_s do |b|
+ b.label(:class => b.object) { b.check_box + b.text }
+ end
+
+ assert_select 'label.true[for=user_active_true]', 'true' do
+ assert_select 'input#user_active_true[type=checkbox]'
+ end
+ assert_select 'label.false[for=user_active_false]', 'false' do
+ assert_select 'input#user_active_false[type=checkbox]'
+ end
+ end
+end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index e36d032f6c..d072d3bce0 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -3,6 +3,8 @@ require 'controller/fake_models'
require 'active_support/core_ext/object/inclusion'
class FormHelperTest < ActionView::TestCase
+ include RenderERBUtils
+
tests ActionView::Helpers::FormHelper
def form_for(*)
@@ -27,7 +29,13 @@ class FormHelperTest < ActionView::TestCase
:body => "Write entire text here",
:color => {
:red => "Rojo"
+ },
+ :comments => {
+ :body => "Write body here"
}
+ },
+ :tag => {
+ :value => "Tag"
}
}
}
@@ -57,7 +65,7 @@ class FormHelperTest < ActionView::TestCase
def full_messages() [ "Author name can't be empty" ] end
}.new
end
- def @post.id; 123; end
+ def @post.to_key; [123]; end
def @post.id_before_type_cast; 123; end
def @post.to_param; '123'; end
@@ -68,6 +76,12 @@ class FormHelperTest < ActionView::TestCase
@post.secret = 1
@post.written_on = Date.new(2004, 6, 15)
+ @post.comments = []
+ @post.comments << @comment
+
+ @post.tags = []
+ @post.tags << Tag.new
+
@blog_post = Blog::Post.new("And his name will be forty and four.", 44)
end
@@ -101,6 +115,14 @@ class FormHelperTest < ActionView::TestCase
super
end
+ class FooTag < ActionView::Helpers::Tags::Base
+ def initialize; end
+ end
+
+ def test_tags_base_child_without_render_method
+ assert_raise(NotImplementedError) { FooTag.new.render }
+ end
+
def test_label
assert_dom_equal('<label for="post_title">Title</label>', label("post", "title"))
assert_dom_equal('<label for="post_title">The title goes here</label>', label("post", "title", "The title goes here"))
@@ -151,6 +173,40 @@ class FormHelperTest < ActionView::TestCase
I18n.locale = old_locale
end
+ def test_label_with_locales_and_nested_attributes
+ old_locale, I18n.locale = I18n.locale, :label
+ form_for(@post, :html => { :id => 'create-post' }) do |f|
+ f.fields_for(:comments) do |cf|
+ concat cf.label(:body)
+ end
+ end
+
+ expected = whole_form("/posts/123", "create-post" , "edit_post", :method => "put") do
+ "<label for=\"post_comments_attributes_0_body\">Write body here</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ ensure
+ I18n.locale = old_locale
+ end
+
+ def test_label_with_locales_fallback_and_nested_attributes
+ old_locale, I18n.locale = I18n.locale, :label
+ form_for(@post, :html => { :id => 'create-post' }) do |f|
+ f.fields_for(:tags) do |cf|
+ concat cf.label(:value)
+ end
+ end
+
+ expected = whole_form("/posts/123", "create-post" , "edit_post", :method => "put") do
+ "<label for=\"post_tags_attributes_0_value\">Tag</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ ensure
+ I18n.locale = old_locale
+ end
+
def test_label_with_for_attribute_as_symbol
assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for"))
end
@@ -159,6 +215,10 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, "for" => "my_for"))
end
+ def test_label_does_not_generate_for_attribute_when_given_nil
+ assert_dom_equal('<label>Title</label>', label(:post, :title, :for => nil))
+ end
+
def test_label_with_id_attribute_as_symbol
assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, :id => "my_id"))
end
@@ -184,6 +244,14 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal('<label for="post_title">The title, please:</label>', label(:post, :title) { "The title, please:" })
end
+ def test_label_with_block_and_options
+ assert_dom_equal('<label for="my_for">The title, please:</label>', label(:post, :title, "for" => "my_for") { "The title, please:" })
+ end
+
+ def test_label_with_block_in_erb
+ assert_equal "<label for=\"post_message\">\n Message\n <input id=\"post_message\" name=\"post[message]\" size=\"30\" type=\"text\" />\n</label>", view.render("test/label_with_block")
+ end
+
def test_text_field
assert_dom_equal(
'<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title")
@@ -278,6 +346,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_check_box
+ assert check_box("post", "secret").html_safe?
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret")
@@ -321,6 +390,18 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_check_box_with_nil_unchecked_value
+ @post.secret = "on"
+ assert_dom_equal(
+ '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="on" />',
+ check_box("post", "secret", {}, "on", nil)
+ )
+ end
+
+ def test_check_box_with_nil_unchecked_value_is_html_safe
+ assert check_box("post", "secret", {}, "on", nil).html_safe?
+ end
+
def test_check_box_with_multiple_behavior
@post.comment_ids = [2,3]
assert_dom_equal(
@@ -333,11 +414,17 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_checkbox_disabled_disables_hidden_field
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="0" disabled="disabled"/><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
+ check_box("post", "secret", { :disabled => true })
+ )
+ end
- def test_checkbox_disabled_still_submits_checked_value
+ def test_checkbox_form_html5_attribute
assert_dom_equal(
- '<input name="post[secret]" type="hidden" value="1" /><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
- check_box("post", "secret", { :disabled => :true })
+ '<input form="new_form" name="post[secret]" type="hidden" value="0" /><input checked="checked" form="new_form" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
+ check_box("post", "secret", :form => "new_form")
)
end
@@ -427,6 +514,32 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal(expected, telephone_field("user", "cell"))
end
+ def test_date_field
+ expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2004-06-15" />}
+ assert_dom_equal(expected, date_field("post", "written_on"))
+ end
+
+ def test_date_field_with_datetime_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2004-06-15" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ assert_dom_equal(expected, date_field("post", "written_on"))
+ end
+
+ def test_date_field_with_timewithzone_value
+ previous_time_zone, Time.zone = Time.zone, 'UTC'
+ expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2004-06-15" />}
+ @post.written_on = Time.zone.parse('2004-06-15 15:30:45')
+ assert_dom_equal(expected, date_field("post", "written_on"))
+ ensure
+ Time.zone = previous_time_zone
+ end
+
+ def test_date_field_with_nil_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="date" />}
+ @post.written_on = nil
+ assert_dom_equal(expected, date_field("post", "written_on"))
+ end
+
def test_url_field
expected = %{<input id="user_homepage" size="30" name="user[homepage]" type="url" />}
assert_dom_equal(expected, url_field("user", "homepage"))
@@ -438,13 +551,17 @@ class FormHelperTest < ActionView::TestCase
end
def test_number_field
- expected = %{<input name="order[quantity]" size="30" max="9" id="order_quantity" type="number" min="1" />}
+ expected = %{<input name="order[quantity]" max="9" id="order_quantity" type="number" min="1" />}
assert_dom_equal(expected, number_field("order", "quantity", :in => 1...10))
+ expected = %{<input name="order[quantity]" size="30" max="9" id="order_quantity" type="number" min="1" />}
+ assert_dom_equal(expected, number_field("order", "quantity", :size => 30, :in => 1...10))
end
def test_range_input
- expected = %{<input name="hifi[volume]" step="0.1" size="30" max="11" id="hifi_volume" type="range" min="0" />}
+ expected = %{<input name="hifi[volume]" step="0.1" max="11" id="hifi_volume" type="range" min="0" />}
assert_dom_equal(expected, range_field("hifi", "volume", :in => 0..11, :step => 0.1))
+ expected = %{<input name="hifi[volume]" step="0.1" size="30" max="11" id="hifi_volume" type="range" min="0" />}
+ assert_dom_equal(expected, range_field("hifi", "volume", :size => 30, :in => 0..11, :step => 0.1))
end
def test_explicit_name
@@ -572,7 +689,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_auto_index
- pid = @post.id
+ pid = 123
assert_dom_equal(
"<label for=\"post_#{pid}_title\">Title</label>",
label("post[]", "title")
@@ -598,7 +715,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_auto_index_with_nil_id
- pid = @post.id
+ pid = 123
assert_dom_equal(
"<input name=\"post[#{pid}][title]\" size=\"30\" type=\"text\" value=\"Hello World\" />",
text_field("post[]","title", :id => nil)
@@ -633,6 +750,7 @@ class FormHelperTest < ActionView::TestCase
concat f.text_area(:body)
concat f.check_box(:secret)
concat f.submit('Create post')
+ concat f.button('Create post')
end
expected = whole_form("/posts/123", "create-post" , "edit_post", :method => "put") do
@@ -641,7 +759,8 @@ class FormHelperTest < ActionView::TestCase
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
- "<input name='commit' type='submit' value='Create post' />"
+ "<input name='commit' type='submit' value='Create post' />" +
+ "<button name='button' type='submit'>Create post</button>"
end
assert_dom_equal expected, output_buffer
@@ -690,7 +809,7 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
- def test_form_for_with_isolated_namespaced_model
+ def test_form_for_with_model_using_relative_model_naming
form_for(@blog_post) do |f|
concat f.text_field :title
concat f.submit('Edit post')
@@ -713,7 +832,7 @@ class FormHelperTest < ActionView::TestCase
concat f.submit('Create post')
end
- expected = whole_form("/posts/123", "create-post", "other_name_edit", :method => "put") do
+ expected = whole_form("/posts/123", "create-post", "edit_other_name", :method => "put") do
"<label for='other_name_title' class='post_title'>Title</label>" +
"<input name='other_name[title]' size='30' id='other_name_title' value='Hello World' type='text' />" +
"<textarea name='other_name[body]' id='other_name_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
@@ -809,6 +928,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_remote_without_html
@post.persisted = false
+ @post.stubs(:to_key).returns(nil)
form_for(@post, :remote => true) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
@@ -850,7 +970,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'post[]_edit', 'post[]_edit', 'put') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'put') do
"<label for='post_123_title'>Title</label>" +
"<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
"<textarea name='post[123][body]' id='post_123_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
@@ -868,7 +988,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'post[]_edit', 'post[]_edit', 'put') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'put') do
"<input name='post[][title]' size='30' type='text' id='post__title' value='Hello World' />" +
"<textarea name='post[][body]' id='post__body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='post[][secret]' type='hidden' value='0' />" +
@@ -878,10 +998,87 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_form_for_with_namespace
+ form_for(@post, :namespace => 'namespace') do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'put') do
+ "<input name='post[title]' size='30' type='text' id='namespace_post_title' value='Hello World' />" +
+ "<textarea name='post[body]' id='namespace_post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' id='namespace_post_secret' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_for_with_namespace_with_label
+ form_for(@post, :namespace => 'namespace') do |f|
+ concat f.label(:title)
+ concat f.text_field(:title)
+ end
+
+ expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'put') do
+ "<label for='namespace_post_title'>Title</label>" +
+ "<input name='post[title]' size='30' type='text' id='namespace_post_title' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_two_form_for_with_namespace
+ form_for(@post, :namespace => 'namespace_1') do |f|
+ concat f.label(:title)
+ concat f.text_field(:title)
+ end
+
+ expected_1 = whole_form('/posts/123', 'namespace_1_edit_post_123', 'edit_post', 'put') do
+ "<label for='namespace_1_post_title'>Title</label>" +
+ "<input name='post[title]' size='30' type='text' id='namespace_1_post_title' value='Hello World' />"
+ end
+
+ assert_dom_equal expected_1, output_buffer
+
+ form_for(@post, :namespace => 'namespace_2') do |f|
+ concat f.label(:title)
+ concat f.text_field(:title)
+ end
+
+ expected_2 = whole_form('/posts/123', 'namespace_2_edit_post_123', 'edit_post', 'put') do
+ "<label for='namespace_2_post_title'>Title</label>" +
+ "<input name='post[title]' size='30' type='text' id='namespace_2_post_title' value='Hello World' />"
+ end
+
+ assert_dom_equal expected_2, output_buffer
+ end
+
+ def test_fields_for_with_namespace
+ @comment.body = 'Hello World'
+ form_for(@post, :namespace => 'namespace') do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.fields_for(@comment) { |c|
+ concat c.text_field(:body)
+ }
+ end
+
+ expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'put') do
+ "<input name='post[title]' size='30' type='text' id='namespace_post_title' value='Hello World' />" +
+ "<textarea name='post[body]' id='namespace_post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
+ "<input name='post[comment][body]' size='30' type='text' id='namespace_post_comment_body' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_submit_with_object_as_new_record_and_locale_strings
old_locale, I18n.locale = I18n.locale, :submit
@post.persisted = false
+ @post.stubs(:to_key).returns(nil)
form_for(@post) do |f|
concat f.submit
end
@@ -934,7 +1131,7 @@ class FormHelperTest < ActionView::TestCase
concat f.submit
end
- expected = whole_form('/posts/123', 'another_post_edit', 'another_post_edit', :method => 'put') do
+ expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', :method => 'put') do
"<input name='commit' type='submit' value='Update your Post' />"
end
@@ -966,7 +1163,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'post[]_edit', 'post[]_edit', 'put') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'put') do
"<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
"<input name='post[123][comment][][name]' size='30' type='text' id='post_123_comment__name' value='new comment' />"
end
@@ -1025,7 +1222,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'post[]_edit', 'post[]_edit', 'put') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'put') do
"<input name='post[123][comment][title]' size='30' type='text' id='post_123_comment_title' value='Hello World' />"
end
@@ -1053,7 +1250,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'post[]_edit', 'post[]_edit', 'put') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'put') do
"<input name='post[123][comment][123][title]' size='30' type='text' id='post_123_comment_123_title' value='Hello World' />"
end
@@ -1073,9 +1270,9 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'post[]_edit', 'post[]_edit', 'put') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'put') do
"<input name='post[123][comment][5][title]' size='30' type='text' id='post_123_comment_5_title' value='Hello World' />"
- end + whole_form('/posts/123', 'post_edit', 'post_edit', 'put') do
+ end + whole_form('/posts/123', 'edit_post', 'edit_post', 'put') do
"<input name='post[1][comment][123][title]' size='30' type='text' id='post_1_comment_123_title' value='Hello World' />"
end
@@ -1728,7 +1925,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'create-post', 'post_edit', :method => 'put') do
+ expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'put') do
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='parent_post[secret]' type='hidden' value='0' />" +
@@ -1748,7 +1945,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'create-post', 'post_edit', :method => 'put') do
+ expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'put') do
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' size='30' />"
@@ -1953,7 +2150,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_new_object
post = Post.new
post.persisted = false
- def post.id() nil end
+ def post.to_key; nil; end
form_for(post) do |f| end
@@ -2004,8 +2201,8 @@ class FormHelperTest < ActionView::TestCase
end
protected
- def protect_against_forgery?
- false
- end
+ def protect_against_forgery?
+ false
+ end
end
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 469718e1bd..a32525c485 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -87,6 +87,20 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_collection_options_with_proc_for_value_method
+ assert_dom_equal(
+ "<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\">Babe went home</option>\n<option value=\"Cabe\">Cabe went home</option>",
+ options_from_collection_for_select(dummy_posts, lambda { |p| p.author_name }, "title")
+ )
+ end
+
+ def test_collection_options_with_proc_for_text_method
+ assert_dom_equal(
+ "<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\">Babe went home</option>\n<option value=\"Cabe\">Cabe went home</option>",
+ options_from_collection_for_select(dummy_posts, "author_name", lambda { |p| p.title })
+ )
+ 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(
@@ -144,6 +158,13 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_range_options_for_select
+ assert_dom_equal(
+ "<option value=\"1\">1</option>\n<option value=\"2\">2</option>\n<option value=\"3\">3</option>",
+ options_for_select(1..3)
+ )
+ end
+
def test_array_options_for_string_include_in_other_string_bug_fix
assert_dom_equal(
"<option value=\"ruby\">ruby</option>\n<option value=\"rubyonrails\" selected=\"selected\">rubyonrails</option>",
@@ -596,6 +617,24 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_with_nil
+ @post = Post.new
+ @post.category = "othervalue"
+ assert_dom_equal(
+ "<select id=\"post_category\" name=\"post[category]\"><option value=\"\"></option>\n<option value=\"othervalue\" selected=\"selected\">othervalue</option></select>",
+ select("post", "category", [nil, "othervalue"])
+ )
+ end
+
+ def test_select_with_fixnum
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "<select id=\"post_category\" name=\"post[category]\"><option value=\"\">Please select</option>\n<option value=\"\"></option>\n<option value=\"1\">1</option></select>",
+ select("post", "category", [1], :prompt => true, :include_blank => true)
+ )
+ end
+
def test_list_of_lists
@post = Post.new
@post.category = ""
@@ -653,6 +692,15 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_with_range
+ @post = Post.new
+ @post.category = 0
+ assert_dom_equal(
+ "<select id=\"post_category\" name=\"post[category]\"><option value=\"1\">1</option>\n<option value=\"2\">2</option>\n<option value=\"3\">3</option></select>",
+ select("post", "category", 1..3)
+ )
+ end
+
def test_collection_select
@post = Post.new
@post.author_name = "Babe"
@@ -756,7 +804,25 @@ class FormOptionsHelperTest < ActionView::TestCase
assert_dom_equal(
"<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\" disabled=\"disabled\">Cabe</option></select>",
collection_select("post", "author_name", dummy_posts, "author_name", "author_name", :disabled => 'Cabe')
- )
+ )
+ end
+
+ def test_collection_select_with_proc_for_value_method
+ @post = Post.new
+
+ assert_dom_equal(
+ "<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\">Babe went home</option>\n<option value=\"Cabe\">Cabe went home</option></select>",
+ collection_select("post", "author_name", dummy_posts, lambda { |p| p.author_name }, "title")
+ )
+ end
+
+ def test_collection_select_with_proc_for_text_method
+ @post = Post.new
+
+ assert_dom_equal(
+ "<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\">Babe went home</option>\n<option value=\"Cabe\">Cabe went home</option></select>",
+ collection_select("post", "author_name", dummy_posts, "author_name", lambda { |p| p.title })
+ )
end
def test_time_zone_select
@@ -1050,14 +1116,14 @@ class FormOptionsHelperTest < ActionView::TestCase
private
- def dummy_posts
- [ Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
- Post.new("Babe went home", "Babe", "To a little house", "shh!"),
- Post.new("Cabe went home", "Cabe", "To a little house", "shh!") ]
- end
+ def dummy_posts
+ [ Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!") ]
+ end
- def dummy_continents
- [ Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
- Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) ]
- end
+ def dummy_continents
+ [ Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")]),
+ Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")]) ]
+ end
end
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 6eae9bf846..809102e5c2 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -2,6 +2,8 @@ require 'abstract_unit'
require 'active_support/core_ext/object/inclusion'
class FormTagHelperTest < ActionView::TestCase
+ include RenderERBUtils
+
tests ActionView::Helpers::FormTagHelper
def setup
@@ -104,14 +106,14 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_form_tag_with_block_in_erb
- output_buffer = form_tag("http://www.example.com") { concat "Hello world!" }
+ output_buffer = render_erb("<%= form_tag('http://www.example.com') do %>Hello world!<% end %>")
expected = whole_form { "Hello world!" }
assert_dom_equal expected, output_buffer
end
def test_form_tag_with_block_and_method_in_erb
- output_buffer = form_tag("http://www.example.com", :method => :put) { concat "Hello world!" }
+ output_buffer = render_erb("<%= form_tag('http://www.example.com', :method => :put) do %>Hello world!<% end %>")
expected = whole_form("http://www.example.com", :method => "put") do
"Hello world!"
@@ -455,11 +457,16 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal(expected, search_field_tag("query"))
end
- def telephone_field_tag
+ def test_telephone_field_tag
expected = %{<input id="cell" name="cell" type="tel" />}
assert_dom_equal(expected, telephone_field_tag("cell"))
end
+ def test_date_field_tag
+ expected = %{<input id="cell" name="cell" type="date" />}
+ assert_dom_equal(expected, date_field_tag("cell"))
+ end
+
def test_url_field_tag
expected = %{<input id="homepage" name="homepage" type="url" />}
assert_dom_equal(expected, url_field_tag("homepage"))
@@ -480,32 +487,28 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal(expected, range_field_tag("volume", nil, :in => 0..11, :step => 0.1))
end
- def test_pass
- assert_equal 1, 1
- end
-
def test_field_set_tag_in_erb
- output_buffer = field_set_tag("Your details") { concat "Hello world!" }
+ output_buffer = render_erb("<%= field_set_tag('Your details') do %>Hello world!<% end %>")
expected = %(<fieldset><legend>Your details</legend>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
- output_buffer = field_set_tag { concat "Hello world!" }
+ output_buffer = render_erb("<%= field_set_tag do %>Hello world!<% end %>")
expected = %(<fieldset>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
- output_buffer = field_set_tag('') { concat "Hello world!" }
+ output_buffer = render_erb("<%= field_set_tag('') do %>Hello world!<% end %>")
expected = %(<fieldset>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
- output_buffer = field_set_tag('', :class => 'format') { concat "Hello world!" }
+ output_buffer = render_erb("<%= field_set_tag('', :class => 'format') do %>Hello world!<% end %>")
expected = %(<fieldset class="format">Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
end
-
+
def test_text_area_tag_options_symbolize_keys_side_effects
options = { :option => "random_option" }
text_area_tag "body", "hello world", options
diff --git a/actionpack/test/template/html-scanner/cdata_node_test.rb b/actionpack/test/template/html-scanner/cdata_node_test.rb
index 1822cc565a..9b58174641 100644
--- a/actionpack/test/template/html-scanner/cdata_node_test.rb
+++ b/actionpack/test/template/html-scanner/cdata_node_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class CDATANodeTest < Test::Unit::TestCase
+class CDATANodeTest < ActiveSupport::TestCase
def setup
@node = HTML::CDATA.new(nil, 0, 0, "<p>howdy</p>")
end
diff --git a/actionpack/test/template/html-scanner/document_test.rb b/actionpack/test/template/html-scanner/document_test.rb
index 3db2fba783..17f045d549 100644
--- a/actionpack/test/template/html-scanner/document_test.rb
+++ b/actionpack/test/template/html-scanner/document_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class DocumentTest < Test::Unit::TestCase
+class DocumentTest < ActiveSupport::TestCase
def test_handle_doctype
doc = nil
assert_nothing_raised do
diff --git a/actionpack/test/template/html-scanner/node_test.rb b/actionpack/test/template/html-scanner/node_test.rb
index f4b9b198e8..5b5d092036 100644
--- a/actionpack/test/template/html-scanner/node_test.rb
+++ b/actionpack/test/template/html-scanner/node_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class NodeTest < Test::Unit::TestCase
+class NodeTest < ActiveSupport::TestCase
class MockNode
def initialize(matched, value)
diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb
index 62ad6be680..32c655c5fd 100644
--- a/actionpack/test/template/html-scanner/sanitizer_test.rb
+++ b/actionpack/test/template/html-scanner/sanitizer_test.rb
@@ -56,7 +56,6 @@ class SanitizerTest < ActionController::TestCase
assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cd e f"
end
- # TODO: Clean up
def test_sanitize_js_handlers
raw = %{onthis="do that" <a href="#" onclick="hello" name="foo" onbogus="remove me">hello</a>}
assert_sanitized raw, %{onthis="do that" <a name="foo" href="#">hello</a>}
@@ -138,7 +137,7 @@ class SanitizerTest < ActionController::TestCase
assert sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://bad")
end
end
-
+
def test_should_accept_good_protocols_ignoring_case
sanitizer = HTML::WhiteListSanitizer.new
HTML::WhiteListSanitizer.allowed_protocols.each do |proto|
@@ -146,6 +145,13 @@ class SanitizerTest < ActionController::TestCase
end
end
+ def test_should_accept_good_protocols_ignoring_space
+ sanitizer = HTML::WhiteListSanitizer.new
+ HTML::WhiteListSanitizer.allowed_protocols.each do |proto|
+ assert !sanitizer.send(:contains_bad_protocols?, 'src', " #{proto}://good")
+ end
+ end
+
def test_should_accept_good_protocols
sanitizer = HTML::WhiteListSanitizer.new
HTML::WhiteListSanitizer.allowed_protocols.each do |proto|
@@ -208,7 +214,6 @@ class SanitizerTest < ActionController::TestCase
assert_sanitized img_hack, "<img>"
end
- # TODO: Clean up
def test_should_sanitize_attributes
assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'&gt;&lt;script&gt;alert()&lt;/script&gt;">blah</span>)
end
diff --git a/actionpack/test/template/html-scanner/tag_node_test.rb b/actionpack/test/template/html-scanner/tag_node_test.rb
index 3b72243e7d..a29d2d43d7 100644
--- a/actionpack/test/template/html-scanner/tag_node_test.rb
+++ b/actionpack/test/template/html-scanner/tag_node_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class TagNodeTest < Test::Unit::TestCase
+class TagNodeTest < ActiveSupport::TestCase
def test_open_without_attributes
node = tag("<tag>")
assert_equal "tag", node.name
diff --git a/actionpack/test/template/html-scanner/text_node_test.rb b/actionpack/test/template/html-scanner/text_node_test.rb
index 6f61253ffa..cbcb9e78f0 100644
--- a/actionpack/test/template/html-scanner/text_node_test.rb
+++ b/actionpack/test/template/html-scanner/text_node_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class TextNodeTest < Test::Unit::TestCase
+class TextNodeTest < ActiveSupport::TestCase
def setup
@node = HTML::Text.new(nil, 0, 0, "hello, howdy, aloha, annyeong")
end
diff --git a/actionpack/test/template/html-scanner/tokenizer_test.rb b/actionpack/test/template/html-scanner/tokenizer_test.rb
index bf45a7c2e3..1d59de23b6 100644
--- a/actionpack/test/template/html-scanner/tokenizer_test.rb
+++ b/actionpack/test/template/html-scanner/tokenizer_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class TokenizerTest < Test::Unit::TestCase
+class TokenizerTest < ActiveSupport::TestCase
def test_blank
tokenize ""
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 4b9c3c97b1..9441bd6b38 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'active_support/core_ext/string/encoding'
class JavaScriptHelperTest < ActionView::TestCase
tests ActionView::Helpers::JavaScriptHelper
@@ -28,11 +27,7 @@ class JavaScriptHelperTest < ActionView::TestCase
assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos'))
assert_equal %(backslash\\\\test), escape_javascript( %(backslash\\test) )
assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags))
- if "ruby".encoding_aware?
- assert_equal %(unicode &#x2028; newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding('UTF-8').encode!)
- else
- assert_equal %(unicode &#x2028; newline), escape_javascript(%(unicode \342\200\250 newline))
- end
+ assert_equal %(unicode &#x2028; newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding('UTF-8').encode!)
assert_equal %(dont <\\/close> tags), j(%(dont </close> tags))
end
diff --git a/actionpack/test/template/log_subscriber_test.rb b/actionpack/test/template/log_subscriber_test.rb
index 752b0f23a8..1713ce935c 100644
--- a/actionpack/test/template/log_subscriber_test.rb
+++ b/actionpack/test/template/log_subscriber_test.rb
@@ -8,7 +8,6 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
def setup
super
- @old_logger = ActionController::Base.logger
@controller = Object.new
@controller.stubs(:_prefixes).returns(%w(test))
@view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller)
@@ -19,11 +18,10 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
def teardown
super
ActiveSupport::LogSubscriber.log_subscribers.clear
- ActionController::Base.logger = @old_logger
end
def set_logger(logger)
- ActionController::Base.logger = logger
+ ActionView::Base.logger = logger
end
def test_render_file_template
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
index bac2530e3d..c65f707da0 100644
--- a/actionpack/test/template/lookup_context_test.rb
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -1,20 +1,14 @@
require "abstract_unit"
require "abstract_controller/rendering"
-ActionView::LookupContext::DetailsKey.class_eval do
- def self.details_keys
- @details_keys
- end
-end
-
class LookupContextTest < ActiveSupport::TestCase
def setup
@lookup_context = ActionView::LookupContext.new(FIXTURE_LOAD_PATH, {})
+ ActionView::LookupContext::DetailsKey.clear
end
def teardown
I18n.locale = :en
- ActionView::LookupContext::DetailsKey.details_keys.clear
end
test "process view paths on initialization" do
diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb
index 5df09b4d3b..d6e9de9555 100644
--- a/actionpack/test/template/number_helper_i18n_test.rb
+++ b/actionpack/test/template/number_helper_i18n_test.rb
@@ -53,6 +53,13 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("-$10.00", number_to_currency(-10))
end
end
+
+ def test_number_to_currency_without_currency_negative_format
+ clean_i18n do
+ I18n.backend.store_translations 'ts', :number => { :currency => { :format => { :unit => '@', :format => '%n %u' } } }
+ assert_equal("-10.00 @", number_to_currency(-10, :locale => 'ts'))
+ end
+ end
def test_number_with_i18n_precision
#Delimiter was set to ""
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 8d679aac1d..482d953907 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -57,6 +57,7 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("1000.000%", number_to_percentage("1000"))
assert_equal("123.4%", number_to_percentage(123.400, :precision => 3, :strip_insignificant_zeros => true))
assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ','))
+ assert_equal("1000.000 %", number_to_percentage(1000, :format => "%n %"))
end
def test_number_with_delimiter
@@ -267,6 +268,31 @@ class NumberHelperTest < ActionView::TestCase
assert_nil number_to_human(nil)
end
+ def test_number_helpers_do_not_mutate_options_hash
+ options = { 'raise' => true }
+
+ number_to_phone(1, options)
+ assert_equal({ 'raise' => true }, options)
+
+ number_to_currency(1, options)
+ assert_equal({ 'raise' => true }, options)
+
+ number_to_percentage(1, options)
+ assert_equal({ 'raise' => true }, options)
+
+ number_with_delimiter(1, options)
+ assert_equal({ 'raise' => true }, options)
+
+ number_with_precision(1, options)
+ assert_equal({ 'raise' => true }, options)
+
+ number_to_human_size(1, options)
+ assert_equal({ 'raise' => true }, options)
+
+ number_to_human(1, options)
+ assert_equal({ 'raise' => true }, options)
+ end
+
def test_number_helpers_should_return_non_numeric_param_unchanged
assert_equal("+1-x x 123", number_to_phone("x", :country_code => 1, :extension => 123))
assert_equal("x", number_to_phone("x"))
diff --git a/actionpack/test/template/output_buffer_test.rb b/actionpack/test/template/output_buffer_test.rb
index bd49a11af1..eb0df3d1ab 100644
--- a/actionpack/test/template/output_buffer_test.rb
+++ b/actionpack/test/template/output_buffer_test.rb
@@ -39,15 +39,13 @@ class OutputBufferTest < ActionController::TestCase
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)
- @vc.output_buffer = original_buffer
- @vc.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
+ test 'flushing preserves output buffer encoding' do
+ original_buffer = ' '.force_encoding(Encoding::EUC_JP)
+ @vc.output_buffer = original_buffer
+ @vc.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
protected
diff --git a/actionpack/test/template/output_safety_helper_test.rb b/actionpack/test/template/output_safety_helper_test.rb
index fc127c24e9..76c71c9e6d 100644
--- a/actionpack/test/template/output_safety_helper_test.rb
+++ b/actionpack/test/template/output_safety_helper_test.rb
@@ -1,9 +1,7 @@
require 'abstract_unit'
-require 'testing_sandbox'
class OutputSafetyHelperTest < ActionView::TestCase
tests ActionView::Helpers::OutputSafetyHelper
- include TestingSandbox
def setup
@string = "hello"
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb
index 7f23629e05..a84034c02e 100644
--- a/actionpack/test/template/record_tag_helper_test.rb
+++ b/actionpack/test/template/record_tag_helper_test.rb
@@ -1,98 +1,103 @@
require 'abstract_unit'
-require 'controller/fake_models'
-class Post
+class RecordTagPost
extend ActiveModel::Naming
include ActiveModel::Conversion
- attr_writer :id, :body
+ attr_accessor :id, :body
def initialize
- @id = nil
- @body = nil
- super
- end
+ @id = 45
+ @body = "What a wonderful world!"
- def id
- @id || 45
- end
-
- def body
- super || @body || "What a wonderful world!"
+ yield self if block_given?
end
end
class RecordTagHelperTest < ActionView::TestCase
+ include RenderERBUtils
+
tests ActionView::Helpers::RecordTagHelper
def setup
super
- @post = Post.new
- @post.persisted = true
+ @post = RecordTagPost.new
end
def test_content_tag_for
- expected = %(<li class="post bar" id="post_45"></li>)
- actual = content_tag_for(:li, @post, :class => 'bar') { }
+ expected = %(<li class="record_tag_post" id="record_tag_post_45"></li>)
+ actual = content_tag_for(:li, @post) { }
assert_dom_equal expected, actual
end
def test_content_tag_for_prefix
- expected = %(<ul class="archived_post" id="archived_post_45"></ul>)
+ expected = %(<ul class="archived_record_tag_post" id="archived_record_tag_post_45"></ul>)
actual = content_tag_for(:ul, @post, :archived) { }
assert_dom_equal expected, actual
end
- def test_content_tag_for_with_extra_html_tags
- expected = %(<tr class="post bar" id="post_45" style='background-color: #f0f0f0'></tr>)
- actual = content_tag_for(:tr, @post, {:class => "bar", :style => "background-color: #f0f0f0"}) { }
+ def test_content_tag_for_with_extra_html_options
+ expected = %(<tr class="record_tag_post special" id="record_tag_post_45" style='background-color: #f0f0f0'></tr>)
+ actual = content_tag_for(:tr, @post, :class => "special", :style => "background-color: #f0f0f0") { }
+ assert_dom_equal expected, actual
+ end
+
+ def test_content_tag_for_with_prefix_and_extra_html_options
+ expected = %(<tr class="archived_record_tag_post special" id="archived_record_tag_post_45" style='background-color: #f0f0f0'></tr>)
+ actual = content_tag_for(:tr, @post, :archived, :class => "special", :style => "background-color: #f0f0f0") { }
assert_dom_equal expected, actual
end
def test_block_not_in_erb_multiple_calls
- expected = %(<div class="post bar" id="post_45">#{@post.body}</div>)
- actual = div_for(@post, :class => "bar") { @post.body }
+ expected = %(<div class="record_tag_post special" id="record_tag_post_45">What a wonderful world!</div>)
+ actual = div_for(@post, :class => "special") { @post.body }
assert_dom_equal expected, actual
- actual = div_for(@post, :class => "bar") { @post.body }
+ actual = div_for(@post, :class => "special") { @post.body }
assert_dom_equal expected, actual
end
def test_block_works_with_content_tag_for_in_erb
- expected = %(<tr class="post" id="post_45">#{@post.body}</tr>)
- actual = content_tag_for(:tr, @post) { concat @post.body }
+ expected = %(<tr class="record_tag_post" id="record_tag_post_45">What a wonderful world!</tr>)
+ actual = render_erb("<%= content_tag_for(:tr, @post) do %><%= @post.body %><% end %>")
assert_dom_equal expected, actual
end
def test_div_for_in_erb
- expected = %(<div class="post bar" id="post_45">#{@post.body}</div>)
- actual = div_for(@post, :class => "bar") { concat @post.body }
+ expected = %(<div class="record_tag_post special" id="record_tag_post_45">What a wonderful world!</div>)
+ actual = render_erb("<%= div_for(@post, :class => 'special') do %><%= @post.body %><% end %>")
assert_dom_equal expected, actual
end
def test_content_tag_for_collection
- post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
- post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
- expected = %(<li class="post" id="post_101">Hello!</li>\n<li class="post" id="post_102">World!</li>)
- actual = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body }
+ post_1 = RecordTagPost.new { |post| post.id = 101; post.body = "Hello!" }
+ post_2 = RecordTagPost.new { |post| post.id = 102; post.body = "World!" }
+ expected = %(<li class="record_tag_post" id="record_tag_post_101">Hello!</li>\n<li class="record_tag_post" id="record_tag_post_102">World!</li>)
+ actual = content_tag_for(:li, [post_1, post_2]) { |post| post.body }
assert_dom_equal expected, actual
end
def test_div_for_collection
- post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
- post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
- expected = %(<div class="post" id="post_101">Hello!</div>\n<div class="post" id="post_102">World!</div>)
- actual = div_for([post_1, post_2]) { |post| concat post.body }
+ post_1 = RecordTagPost.new { |post| post.id = 101; post.body = "Hello!" }
+ post_2 = RecordTagPost.new { |post| post.id = 102; post.body = "World!" }
+ expected = %(<div class="record_tag_post" id="record_tag_post_101">Hello!</div>\n<div class="record_tag_post" id="record_tag_post_102">World!</div>)
+ actual = div_for([post_1, post_2]) { |post| post.body }
assert_dom_equal expected, actual
end
def test_content_tag_for_single_record_is_html_safe
- result = div_for(@post, :class => "bar") { concat @post.body }
+ result = div_for(@post, :class => "special") { @post.body }
assert result.html_safe?
end
def test_content_tag_for_collection_is_html_safe
- post_1 = Post.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
- post_2 = Post.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
- result = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body }
+ post_1 = RecordTagPost.new { |post| post.id = 101; post.body = "Hello!" }
+ post_2 = RecordTagPost.new { |post| post.id = 102; post.body = "World!" }
+ result = content_tag_for(:li, [post_1, post_2]) { |post| post.body }
assert result.html_safe?
end
+
+ def test_content_tag_for_does_not_change_options_hash
+ options = { :class => "important" }
+ content_tag_for(:li, @post, options) { }
+ assert_equal({ :class => "important" }, options)
+ end
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 77659918f7..5d3dc73ed2 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -20,6 +20,13 @@ module RenderTestCases
assert_equal ORIGINAL_LOCALES, I18n.available_locales.map {|l| l.to_s }.sort
end
+ def test_render_without_options
+ @view.render()
+ flunk "Render did not raise ArgumentError"
+ rescue ArgumentError => e
+ assert_match "You invoked render but did not give any of :partial, :template, :inline, :file or :text option.", e.message
+ end
+
def test_render_file
assert_equal "Hello world!", @view.render(:file => "test/hello_world")
end
@@ -43,21 +50,21 @@ module RenderTestCases
assert_match "<h1>No Comment</h1>", @view.render(:template => "comments/empty", :formats => [:html])
assert_match "<error>No Comment</error>", @view.render(:template => "comments/empty", :formats => [:xml])
end
-
+
def test_render_file_with_locale
assert_equal "<h1>Kein Kommentar</h1>", @view.render(:file => "comments/empty", :locale => [:de])
assert_equal "<h1>Kein Kommentar</h1>", @view.render(:file => "comments/empty", :locale => :de)
end
-
+
def test_render_template_with_locale
assert_equal "<h1>Kein Kommentar</h1>", @view.render(:template => "comments/empty", :locale => [:de])
end
-
+
def test_render_file_with_handlers
assert_equal "<h1>No Comment</h1>\n", @view.render(:file => "comments/empty", :handlers => [:builder])
assert_equal "<h1>No Comment</h1>\n", @view.render(:file => "comments/empty", :handlers => :builder)
end
-
+
def test_render_template_with_handlers
assert_equal "<h1>No Comment</h1>\n", @view.render(:template => "comments/empty", :handlers => [:builder])
end
@@ -142,6 +149,13 @@ module RenderTestCases
"and is followed by any combinations of letters, numbers, or underscores.", e.message
end
+ def test_render_partial_with_incompatible_object
+ @view.render(:partial => nil)
+ flunk "Render did not raise ArgumentError"
+ rescue ArgumentError => e
+ assert_equal "'#{nil.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.", e.message
+ end
+
def test_render_partial_with_errors
@view.render(:partial => "test/raise")
flunk "Render did not raise Template::Error"
@@ -236,36 +250,6 @@ module RenderTestCases
@controller_view.render(customers, :greeting => "Hello")
end
- class CustomerWithDeprecatedPartialPath
- attr_reader :name
-
- def self.model_name
- Struct.new(:partial_path).new("customers/customer")
- end
-
- def initialize(name)
- @name = name
- end
- end
-
- def test_render_partial_using_object_with_deprecated_partial_path
- assert_deprecated(/#model_name.*#partial_path.*#to_partial_path/) do
- assert_equal "Hello: nertzy",
- @controller_view.render(CustomerWithDeprecatedPartialPath.new("nertzy"), :greeting => "Hello")
- end
- end
-
- def test_render_partial_using_collection_with_deprecated_partial_path
- assert_deprecated(/#model_name.*#partial_path.*#to_partial_path/) do
- customers = [
- CustomerWithDeprecatedPartialPath.new("nertzy"),
- CustomerWithDeprecatedPartialPath.new("peeja")
- ]
- assert_equal "Hello: nertzyHello: peeja",
- @controller_view.render(customers, :greeting => "Hello")
- end
- end
-
# TODO: The reason for this test is unclear, improve documentation
def test_render_partial_and_fallback_to_layout
assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
@@ -426,51 +410,49 @@ class LazyViewRenderTest < ActiveSupport::TestCase
GC.start
end
- if '1.9'.respond_to?(:force_encoding)
- def test_render_utf8_template_with_magic_comment
- with_external_encoding Encoding::ASCII_8BIT do
- result = @view.render(:file => "test/utf8_magic", :formats => [:html], :layouts => "layouts/yield")
- assert_equal Encoding::UTF_8, result.encoding
- assert_equal "\nРусский \nтекст\n\nUTF-8\nUTF-8\nUTF-8\n", result
- end
+ def test_render_utf8_template_with_magic_comment
+ with_external_encoding Encoding::ASCII_8BIT do
+ result = @view.render(:file => "test/utf8_magic", :formats => [:html], :layouts => "layouts/yield")
+ assert_equal Encoding::UTF_8, result.encoding
+ assert_equal "\nРусский \nтекст\n\nUTF-8\nUTF-8\nUTF-8\n", result
end
+ end
- def test_render_utf8_template_with_default_external_encoding
- with_external_encoding Encoding::UTF_8 do
- result = @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield")
- assert_equal Encoding::UTF_8, result.encoding
- assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result
- end
+ def test_render_utf8_template_with_default_external_encoding
+ with_external_encoding Encoding::UTF_8 do
+ result = @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield")
+ assert_equal Encoding::UTF_8, result.encoding
+ assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result
end
+ end
- def test_render_utf8_template_with_incompatible_external_encoding
- with_external_encoding Encoding::SHIFT_JIS do
- begin
- @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield")
- flunk 'Should have raised incompatible encoding error'
- rescue ActionView::Template::Error => error
- assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message
- end
+ def test_render_utf8_template_with_incompatible_external_encoding
+ with_external_encoding Encoding::SHIFT_JIS do
+ begin
+ @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield")
+ flunk 'Should have raised incompatible encoding error'
+ rescue ActionView::Template::Error => error
+ assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message
end
end
+ end
- def test_render_utf8_template_with_partial_with_incompatible_encoding
- with_external_encoding Encoding::SHIFT_JIS do
- begin
- @view.render(:file => "test/utf8_magic_with_bare_partial", :formats => [:html], :layouts => "layouts/yield")
- flunk 'Should have raised incompatible encoding error'
- rescue ActionView::Template::Error => error
- assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message
- end
+ def test_render_utf8_template_with_partial_with_incompatible_encoding
+ with_external_encoding Encoding::SHIFT_JIS do
+ begin
+ @view.render(:file => "test/utf8_magic_with_bare_partial", :formats => [:html], :layouts => "layouts/yield")
+ flunk 'Should have raised incompatible encoding error'
+ rescue ActionView::Template::Error => error
+ assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message
end
end
+ end
- def with_external_encoding(encoding)
- old = Encoding.default_external
- silence_warnings { Encoding.default_external = encoding }
- yield
- ensure
- silence_warnings { Encoding.default_external = old }
- end
+ def with_external_encoding(encoding)
+ old = Encoding.default_external
+ silence_warnings { Encoding.default_external = encoding }
+ yield
+ ensure
+ silence_warnings { Encoding.default_external = old }
end
end
diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb
index 222d4dbf4c..4182af590e 100644
--- a/actionpack/test/template/sanitize_helper_test.rb
+++ b/actionpack/test/template/sanitize_helper_test.rb
@@ -1,11 +1,9 @@
require 'abstract_unit'
-require 'testing_sandbox'
# The exhaustive tests are in test/controller/html/sanitizer_test.rb.
# This tests the that the helpers hook up correctly to the sanitizer classes.
class SanitizeHelperTest < ActionView::TestCase
tests ActionView::Helpers::SanitizeHelper
- include TestingSandbox
def test_strip_links
assert_equal "Dont touch me", strip_links("Dont touch me")
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
index db69f95130..1c591bdcc2 100644
--- a/actionpack/test/template/sprockets_helper_test.rb
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -24,6 +24,7 @@ class SprocketsHelperTest < ActionView::TestCase
@assets.append_path(FIXTURES.join("sprockets/app/javascripts"))
@assets.append_path(FIXTURES.join("sprockets/app/stylesheets"))
@assets.append_path(FIXTURES.join("sprockets/app/images"))
+ @assets.append_path(FIXTURES.join("sprockets/app/fonts"))
application = Struct.new(:config, :assets).new(config, @assets)
Rails.stubs(:application).returns(application)
@@ -41,6 +42,10 @@ class SprocketsHelperTest < ActionView::TestCase
@controller ? @controller.config : @config
end
+ def compute_host(source, request, options = {})
+ raise "Should never get here"
+ end
+
test "asset_path" do
assert_match %r{/assets/logo-[0-9a-f]+.png},
asset_path("logo.png")
@@ -125,6 +130,10 @@ class SprocketsHelperTest < ActionView::TestCase
assert_raises ActionController::RoutingError do
asset_path("logo.png")
end
+ @config.asset_host = method :compute_host
+ assert_raises ActionController::RoutingError do
+ asset_path("logo.png")
+ end
end
test "image_tag" do
@@ -139,8 +148,19 @@ class SprocketsHelperTest < ActionView::TestCase
path_to_image("logo.png")
end
+ test "font_path" do
+ assert_match %r{/assets/font-[0-9a-f]+.ttf},
+ font_path("font.ttf")
+
+ assert_match %r{/assets/font-[0-9a-f]+.ttf},
+ path_to_font("font.ttf")
+ end
+
test "javascript_path" do
assert_match %r{/assets/application-[0-9a-f]+.js},
+ javascript_path("application")
+
+ assert_match %r{/assets/application-[0-9a-f]+.js},
javascript_path("application.js")
assert_match %r{/assets/application-[0-9a-f]+.js},
@@ -149,6 +169,9 @@ class SprocketsHelperTest < ActionView::TestCase
test "stylesheet_path" do
assert_match %r{/assets/application-[0-9a-f]+.css},
+ stylesheet_path("application")
+
+ assert_match %r{/assets/application-[0-9a-f]+.css},
stylesheet_path("application.css")
assert_match %r{/assets/application-[0-9a-f]+.css},
@@ -178,6 +201,17 @@ class SprocketsHelperTest < ActionView::TestCase
asset_path("/images/logo.gif")
end
+ test "font path through asset_path" do
+ assert_match %r{/assets/font-[0-9a-f]+.ttf},
+ asset_path('font.ttf')
+
+ assert_match %r{/assets/dir/font-[0-9a-f]+.ttf},
+ asset_path("dir/font.ttf")
+
+ assert_equal "http://www.example.com/fonts/font.ttf",
+ asset_path("http://www.example.com/fonts/font.ttf")
+ end
+
test "javascript path through asset_path" do
assert_match %r{/assets/application-[0-9a-f]+.js},
asset_path(:application, :ext => "js")
@@ -217,6 +251,9 @@ class SprocketsHelperTest < ActionView::TestCase
assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>},
javascript_include_tag(:application, :debug => true)
+ assert_match %r{<script src="/assets/jquery.plugin.js" type="text/javascript"></script>},
+ javascript_include_tag('jquery.plugin', :digest => false)
+
@config.assets.compile = true
@config.assets.debug = true
assert_match %r{<script src="/javascripts/application.js" type="text/javascript"></script>},
diff --git a/actionpack/test/template/sprockets_helper_with_routes_test.rb b/actionpack/test/template/sprockets_helper_with_routes_test.rb
new file mode 100644
index 0000000000..bcbd81a7dd
--- /dev/null
+++ b/actionpack/test/template/sprockets_helper_with_routes_test.rb
@@ -0,0 +1,57 @@
+require 'abstract_unit'
+require 'sprockets'
+require 'sprockets/helpers/rails_helper'
+require 'mocha'
+
+class SprocketsHelperWithRoutesTest < ActionView::TestCase
+ include Sprockets::Helpers::RailsHelper
+
+ # Let's bring in some named routes to test namespace conflicts with potential *_paths.
+ # We have to do this after we bring in the Sprockets RailsHelper so if there are conflicts,
+ # they'll fail in the way we expect in a real live Rails app.
+ routes = ActionDispatch::Routing::RouteSet.new
+ routes.draw do
+ resources :assets
+ end
+ include routes.url_helpers
+
+ def setup
+ super
+ @controller = BasicController.new
+
+ @assets = Sprockets::Environment.new
+ @assets.append_path(FIXTURES.join("sprockets/app/javascripts"))
+ @assets.append_path(FIXTURES.join("sprockets/app/stylesheets"))
+ @assets.append_path(FIXTURES.join("sprockets/app/images"))
+
+ application = Struct.new(:config, :assets).new(config, @assets)
+ Rails.stubs(:application).returns(application)
+ @config = config
+ @config.perform_caching = true
+ @config.assets.digest = true
+ @config.assets.compile = true
+ end
+
+ test "namespace conflicts on a named route called asset_path" do
+ # Testing this for sanity - asset_path is now a named route!
+ assert_match asset_path('test_asset'), '/assets/test_asset'
+
+ assert_match %r{/assets/logo-[0-9a-f]+.png},
+ path_to_asset("logo.png")
+ assert_match %r{/assets/logo-[0-9a-f]+.png},
+ path_to_asset("logo.png", :digest => true)
+ assert_match %r{/assets/logo.png},
+ path_to_asset("logo.png", :digest => false)
+ end
+
+ test "javascript_include_tag with a named_route named asset_path" do
+ assert_match %r{<script src="/assets/application-[0-9a-f]+.js" type="text/javascript"></script>},
+ javascript_include_tag(:application)
+ end
+
+ test "stylesheet_link_tag with a named_route named asset_path" do
+ assert_match %r{<link href="/assets/application-[0-9a-f]+.css" media="screen" rel="stylesheet" type="text/css" />},
+ stylesheet_link_tag(:application)
+ end
+
+end \ No newline at end of file
diff --git a/actionpack/test/template/streaming_render_test.rb b/actionpack/test/template/streaming_render_test.rb
index 4d01352b43..520bf3a824 100644
--- a/actionpack/test/template/streaming_render_test.rb
+++ b/actionpack/test/template/streaming_render_test.rb
@@ -106,4 +106,4 @@ class FiberedTest < ActiveSupport::TestCase
buffered_render(:template => "test/nested_streaming", :layout => "layouts/streaming")
end
-end if defined?(Fiber)
+end
diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb
index 60b466a9ff..6c325d5abb 100644
--- a/actionpack/test/template/tag_helper_test.rb
+++ b/actionpack/test/template/tag_helper_test.rb
@@ -1,6 +1,8 @@
require 'abstract_unit'
class TagHelperTest < ActionView::TestCase
+ include RenderERBUtils
+
tests ActionView::Helpers::TagHelper
def test_tag
@@ -44,12 +46,12 @@ class TagHelperTest < ActionView::TestCase
end
def test_content_tag_with_block_in_erb
- buffer = content_tag(:div) { concat "Hello world!" }
+ buffer = render_erb("<%= content_tag(:div) do %>Hello world!<% end %>")
assert_dom_equal "<div>Hello world!</div>", buffer
end
def test_content_tag_with_block_and_options_in_erb
- buffer = content_tag(:div, :class => "green") { concat "Hello world!" }
+ buffer = render_erb("<%= content_tag(:div, :class => 'green') do %>Hello world!<% end %>")
assert_dom_equal %(<div class="green">Hello world!</div>), buffer
end
@@ -68,10 +70,8 @@ class TagHelperTest < ActionView::TestCase
output_buffer
end
- # TAG TODO: Move this into a real template
def test_content_tag_nested_in_content_tag_in_erb
- buffer = content_tag("p") { concat content_tag("b", "Hello") }
- assert_equal '<p><b>Hello</b></p>', buffer
+ assert_equal "<p>\n <b>Hello</b>\n</p>", view.render("test/content_tag_nested_in_content_tag")
end
def test_content_tag_with_escaped_array_class
diff --git a/actionpack/test/template/template_error_test.rb b/actionpack/test/template/template_error_test.rb
index 3a874082d9..91424daeed 100644
--- a/actionpack/test/template/template_error_test.rb
+++ b/actionpack/test/template/template_error_test.rb
@@ -2,12 +2,12 @@ require "abstract_unit"
class TemplateErrorTest < ActiveSupport::TestCase
def test_provides_original_message
- error = ActionView::Template::Error.new("test", {}, Exception.new("original"))
+ error = ActionView::Template::Error.new("test", Exception.new("original"))
assert_equal "original", error.message
end
def test_provides_useful_inspect
- error = ActionView::Template::Error.new("test", {}, Exception.new("original"))
+ error = ActionView::Template::Error.new("test", Exception.new("original"))
assert_equal "#<ActionView::Template::Error: original>", error.inspect
end
end
diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb
index b0ca7de0b6..3084527f70 100644
--- a/actionpack/test/template/template_test.rb
+++ b/actionpack/test/template/template_test.rb
@@ -8,6 +8,9 @@ class TestERBTemplate < ActiveSupport::TestCase
def disable_cache
yield
end
+
+ def find_template(*args)
+ end
end
class Context
@@ -34,7 +37,7 @@ class TestERBTemplate < ActiveSupport::TestCase
end
def logger
- Logger.new(STDERR)
+ ActiveSupport::Logger.new(STDERR)
end
def my_buffer
@@ -111,66 +114,65 @@ class TestERBTemplate < ActiveSupport::TestCase
end
end
- if "ruby".encoding_aware?
- def test_resulting_string_is_utf8
- @template = new_template
- assert_equal Encoding::UTF_8, render.encoding
- end
+ def test_resulting_string_is_utf8
+ @template = new_template
+ assert_equal Encoding::UTF_8, render.encoding
+ end
- def test_no_magic_comment_word_with_utf_8
- @template = new_template("hello \u{fc}mlat")
+ def test_no_magic_comment_word_with_utf_8
+ @template = new_template("hello \u{fc}mlat")
+ assert_equal Encoding::UTF_8, render.encoding
+ assert_equal "hello \u{fc}mlat", render
+ end
+
+ # This test ensures that if the default_external
+ # is set to something other than UTF-8, we don't
+ # get any errors and get back a UTF-8 String.
+ def test_default_external_works
+ with_external_encoding "ISO-8859-1" do
+ @template = new_template("hello \xFCmlat")
assert_equal Encoding::UTF_8, render.encoding
assert_equal "hello \u{fc}mlat", render
end
+ end
- # This test ensures that if the default_external
- # is set to something other than UTF-8, we don't
- # get any errors and get back a UTF-8 String.
- def test_default_external_works
- with_external_encoding "ISO-8859-1" do
- @template = new_template("hello \xFCmlat")
- assert_equal Encoding::UTF_8, render.encoding
- assert_equal "hello \u{fc}mlat", render
- end
- end
-
- def test_encoding_can_be_specified_with_magic_comment
- @template = new_template("# encoding: ISO-8859-1\nhello \xFCmlat")
- assert_equal Encoding::UTF_8, render.encoding
- assert_equal "\nhello \u{fc}mlat", render
- end
+ def test_encoding_can_be_specified_with_magic_comment
+ @template = new_template("# encoding: ISO-8859-1\nhello \xFCmlat")
+ assert_equal Encoding::UTF_8, render.encoding
+ assert_equal "\nhello \u{fc}mlat", render
+ end
- # TODO: This is currently handled inside ERB. The case of explicitly
- # lying about encodings via the normal Rails API should be handled
- # inside Rails.
- def test_lying_with_magic_comment
- assert_raises(ActionView::Template::Error) do
- @template = new_template("# encoding: UTF-8\nhello \xFCmlat", :virtual_path => nil)
- render
- end
+ # TODO: This is currently handled inside ERB. The case of explicitly
+ # lying about encodings via the normal Rails API should be handled
+ # inside Rails.
+ def test_lying_with_magic_comment
+ assert_raises(ActionView::Template::Error) do
+ @template = new_template("# encoding: UTF-8\nhello \xFCmlat", :virtual_path => nil)
+ render
end
+ end
- def test_encoding_can_be_specified_with_magic_comment_in_erb
- with_external_encoding Encoding::UTF_8 do
- @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat", :virtual_path => nil)
- assert_equal Encoding::UTF_8, render.encoding
- assert_equal "hello \u{fc}mlat", render
- end
+ def test_encoding_can_be_specified_with_magic_comment_in_erb
+ with_external_encoding Encoding::UTF_8 do
+ @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat", :virtual_path => nil)
+ assert_equal Encoding::UTF_8, render.encoding
+ assert_equal "hello \u{fc}mlat", render
end
+ end
- def test_error_when_template_isnt_valid_utf8
- assert_raises(ActionView::Template::Error, /\xFC/) do
- @template = new_template("hello \xFCmlat", :virtual_path => nil)
- render
- end
+ def test_error_when_template_isnt_valid_utf8
+ assert_raises(ActionView::Template::Error, /\xFC/) do
+ @template = new_template("hello \xFCmlat", :virtual_path => nil)
+ render
end
+ end
- def with_external_encoding(encoding)
- old = Encoding.default_external
- silence_warnings { Encoding.default_external = encoding }
- yield
- ensure
- silence_warnings { Encoding.default_external = old }
- end
+ def with_external_encoding(encoding)
+ old = Encoding.default_external
+ Encoding::Converter.new old, encoding if old != encoding
+ silence_warnings { Encoding.default_external = encoding }
+ yield
+ ensure
+ silence_warnings { Encoding.default_external = old }
end
end
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index a75f1bc981..37858c1ba2 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -155,7 +155,7 @@ module ActionView
test "view_assigns excludes internal ivars" do
INTERNAL_IVARS.each do |ivar|
assert defined?(ivar), "expected #{ivar} to be defined"
- assert !view_assigns.keys.include?(ivar.sub('@','').to_sym), "expected #{ivar} to be excluded from view_assigns"
+ assert !view_assigns.keys.include?(ivar.to_s.sub('@', '').to_sym), "expected #{ivar} to be excluded from view_assigns"
end
end
end
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 02f9609483..5865b7f23c 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -1,10 +1,8 @@
# encoding: utf-8
require 'abstract_unit'
-require 'testing_sandbox'
class TextHelperTest < ActionView::TestCase
tests ActionView::Helpers::TextHelper
- include TestingSandbox
def setup
super
@@ -82,25 +80,9 @@ class TextHelperTest < ActionView::TestCase
assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 15, :separator => ' ')
end
- if RUBY_VERSION < '1.9.0'
- def test_truncate_multibyte
- with_kcode 'none' do
- assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10)
- end
- with_kcode 'u' do
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...",
- truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", :length => 10)
- end
- end
- else
- def test_truncate_multibyte
- # .mb_chars always returns a UTF-8 String.
- # assert_equal "\354\225\210\353\205\225\355...",
- # truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10)
-
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
- truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
- end
+ def test_truncate_multibyte
+ assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
+ truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
end
def test_highlight_should_be_html_safe
@@ -109,12 +91,12 @@ class TextHelperTest < ActionView::TestCase
def test_highlight
assert_equal(
- "This is a <strong class=\"highlight\">beautiful</strong> morning",
+ "This is a <mark>beautiful</mark> morning",
highlight("This is a beautiful morning", "beautiful")
)
assert_equal(
- "This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day",
+ "This is a <mark>beautiful</mark> morning, but also a <mark>beautiful</mark> day",
highlight("This is a beautiful morning, but also a beautiful day", "beautiful")
)
@@ -133,31 +115,31 @@ class TextHelperTest < ActionView::TestCase
def test_highlight_should_sanitize_input
assert_equal(
- "This is a <strong class=\"highlight\">beautiful</strong> morning",
+ "This is a <mark>beautiful</mark> morning",
highlight("This is a beautiful morning<script>code!</script>", "beautiful")
)
end
def test_highlight_should_not_sanitize_if_sanitize_option_if_false
assert_equal(
- "This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>",
+ "This is a <mark>beautiful</mark> morning<script>code!</script>",
highlight("This is a beautiful morning<script>code!</script>", "beautiful", :sanitize => false)
)
end
def test_highlight_with_regexp
assert_equal(
- "This is a <strong class=\"highlight\">beautiful!</strong> morning",
+ "This is a <mark>beautiful!</mark> morning",
highlight("This is a beautiful! morning", "beautiful!")
)
assert_equal(
- "This is a <strong class=\"highlight\">beautiful! morning</strong>",
+ "This is a <mark>beautiful! morning</mark>",
highlight("This is a beautiful! morning", "beautiful! morning")
)
assert_equal(
- "This is a <strong class=\"highlight\">beautiful? morning</strong>",
+ "This is a <mark>beautiful? morning</mark>",
highlight("This is a beautiful? morning", "beautiful? morning")
)
end
@@ -175,25 +157,29 @@ class TextHelperTest < ActionView::TestCase
def test_highlight_with_html
assert_equal(
- "<p>This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
+ "<p>This is a <mark>beautiful</mark> morning, but also a <mark>beautiful</mark> day</p>",
highlight("<p>This is a beautiful morning, but also a beautiful day</p>", "beautiful")
)
assert_equal(
- "<p>This is a <em><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
+ "<p>This is a <em><mark>beautiful</mark></em> morning, but also a <mark>beautiful</mark> day</p>",
highlight("<p>This is a <em>beautiful</em> morning, but also a beautiful day</p>", "beautiful")
)
assert_equal(
- "<p>This is a <em class=\"error\"><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> <span class=\"last\">day</span></p>",
+ "<p>This is a <em class=\"error\"><mark>beautiful</mark></em> morning, but also a <mark>beautiful</mark> <span class=\"last\">day</span></p>",
highlight("<p>This is a <em class=\"error\">beautiful</em> morning, but also a beautiful <span class=\"last\">day</span></p>", "beautiful")
)
assert_equal(
- "<p class=\"beautiful\">This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
+ "<p class=\"beautiful\">This is a <mark>beautiful</mark> morning, but also a <mark>beautiful</mark> day</p>",
highlight("<p class=\"beautiful\">This is a beautiful morning, but also a beautiful day</p>", "beautiful")
)
assert_equal(
- "<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful#top?what=beautiful%20morning&amp;when=now+then\">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
+ "<p>This is a <mark>beautiful</mark> <a href=\"http://example.com/beautiful#top?what=beautiful%20morning&amp;when=now+then\">morning</a>, but also a <mark>beautiful</mark> day</p>",
highlight("<p>This is a beautiful <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a beautiful day</p>", "beautiful")
)
+ assert_equal(
+ "<div>abc <b>div</b></div>",
+ highlight("<div>abc div</div>", "div", :highlighter => '<b>\1</b>')
+ )
end
def test_excerpt
@@ -239,21 +225,8 @@ class TextHelperTest < ActionView::TestCase
)
end
- if RUBY_VERSION < '1.9'
- def test_excerpt_with_utf8
- with_kcode('u') do
- assert_equal("...\357\254\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
- end
- with_kcode('none') do
- assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
- end
- end
- else
- def test_excerpt_with_utf8
- assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', 8))
- # .mb_chars always returns UTF-8, even in 1.9. This is not great, but it's how it works. Let's work this out.
- # assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped".force_encoding("BINARY"), 'could', 8))
- end
+ def test_excerpt_with_utf8
+ assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', 8))
end
def test_word_wrap
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index cd9f54e04c..397de9c2ce 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -17,8 +17,13 @@ class TranslationHelperTest < ActiveSupport::TestCase
:hello => '<a>Hello World</a>',
:html => '<a>Hello World</a>',
:hello_html => '<a>Hello World</a>',
+ :interpolated_html => '<a>Hello %{word}</a>',
:array_html => %w(foo bar),
- :array => %w(foo bar)
+ :array => %w(foo bar),
+ :count_html => {
+ :one => '<a>One %{count}</a>',
+ :other => '<a>Other %{count}</a>'
+ }
}
)
@view = ::ActionView::Base.new(ActionController::Base.view_paths, {})
@@ -83,6 +88,17 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert translate(:'translations.hello_html').html_safe?
end
+ def test_translate_escapes_interpolations_in_translations_with_a_html_suffix
+ assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => '<World>')
+ assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => stub(:to_s => "<World>"))
+ end
+
+ def test_translate_with_html_count
+ assert_equal '<a>One 1</a>', translate(:'translations.count_html', :count => 1)
+ assert_equal '<a>Other 2</a>', translate(:'translations.count_html', :count => 2)
+ assert_equal '<a>Other &lt;One&gt;</a>', translate(:'translations.count_html', :count => '<One>')
+ end
+
def test_translation_returning_an_array_ignores_html_suffix
assert_equal ["foo", "bar"], translate(:'translations.array_html')
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index bc45fabf34..b482bd3251 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -1,6 +1,5 @@
# encoding: utf-8
require 'abstract_unit'
-require 'active_support/ordered_hash'
require 'controller/fake_controllers'
class UrlHelperTest < ActiveSupport::TestCase
@@ -11,6 +10,9 @@ class UrlHelperTest < ActiveSupport::TestCase
# In those cases, we'll set up a simple mock
attr_accessor :controller, :request
+ cattr_accessor :request_forgery
+ self.request_forgery = false
+
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
match "/" => "foo#bar"
@@ -28,13 +30,13 @@ class UrlHelperTest < ActiveSupport::TestCase
setup :_prepare_context
- def hash_for(opts = [])
- ActiveSupport::OrderedHash[*([:controller, "foo", :action, "bar"].concat(opts))]
+ def hash_for(options = {})
+ { :controller => "foo", :action => "bar" }.merge!(options)
end
alias url_hash hash_for
def test_url_for_does_not_escape_urls
- assert_equal "/?a=b&c=d", url_for(hash_for([:a, :b, :c, :d]))
+ assert_equal "/?a=b&c=d", url_for(hash_for(:a => :b, :c => :d))
end
def test_url_for_with_back
@@ -49,11 +51,22 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_equal 'javascript:history.back()', url_for(:back)
end
- # todo: missing test cases
+ # TODO: missing test cases
def test_button_to_with_straight_url
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
end
+ def test_button_to_with_straight_url_and_request_forgery
+ self.request_forgery = true
+
+ assert_dom_equal(
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /><input name="form_token" type="hidden" value="secret" /></div></form>},
+ button_to("Hello", "http://www.example.com")
+ )
+ ensure
+ self.request_forgery = false
+ end
+
def test_button_to_with_form_class
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :form_class => 'custom-class')
end
@@ -91,7 +104,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_button_to_with_remote_and_form_options
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\" data-remote=\"true\" data-type=\"json\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :remote => true, :form => { :class => "custom-class", "data-type" => "json" } )
end
-
+
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
@@ -154,7 +167,7 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_link_tag_with_host_option
- hash = hash_for([:host, "www.example.com"])
+ hash = hash_for(:host => "www.example.com")
expected = %q{<a href="http://www.example.com/">Test Link</a>}
assert_dom_equal(expected, link_to('Test Link', hash))
end
@@ -329,7 +342,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_current_page_with_params_that_match
@request = request_for_url("/?order=desc&page=1")
- assert current_page?(hash_for([:order, "desc", :page, "1"]))
+ assert current_page?(hash_for(:order => "desc", :page => "1"))
assert current_page?("http://www.example.com/?order=desc&page=1")
end
@@ -357,20 +370,20 @@ class UrlHelperTest < ActiveSupport::TestCase
@request = request_for_url("/?order=desc&page=1")
assert_equal "Showing",
- link_to_unless_current("Showing", hash_for([:order, 'desc', :page, '1']))
+ link_to_unless_current("Showing", hash_for(:order => 'desc', :page => '1'))
assert_equal "Showing",
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=1")
@request = request_for_url("/?order=desc")
assert_equal %{<a href="/?order=asc">Showing</a>},
- link_to_unless_current("Showing", hash_for([:order, :asc]))
+ link_to_unless_current("Showing", hash_for(:order => :asc))
assert_equal %{<a href="http://www.example.com/?order=asc">Showing</a>},
link_to_unless_current("Showing", "http://www.example.com/?order=asc")
@request = request_for_url("/?order=desc")
assert_equal %{<a href="/?order=desc&amp;page=2\">Showing</a>},
- link_to_unless_current("Showing", hash_for([:order, "desc", :page, 2]))
+ link_to_unless_current("Showing", hash_for(:order => "desc", :page => 2))
assert_equal %{<a href="http://www.example.com/?order=desc&amp;page=2">Showing</a>},
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=2")
@@ -435,9 +448,16 @@ class UrlHelperTest < ActiveSupport::TestCase
assert mail_to("me@domain.com", "My email", :encode => "hex").html_safe?
end
- # TODO: button_to looks at this ... why?
def protect_against_forgery?
- false
+ self.request_forgery
+ end
+
+ def form_authenticity_token
+ "secret"
+ end
+
+ def request_forgery_protection_token
+ "form_token"
end
private
@@ -506,8 +526,6 @@ class UrlHelperControllerTest < ActionController::TestCase
render :inline => '<%= url_for(:action => :show_url_for) %>'
end
- def rescue_action(e) raise e end
-
def override_url_helper
render :inline => '<%= override_url_helper_path %>'
end
@@ -595,8 +613,6 @@ class TasksController < ActionController::Base
render_default
end
- def rescue_action(e) raise e end
-
protected
def render_default
render :inline =>
@@ -655,8 +671,6 @@ class WorkshopsController < ActionController::Base
@workshop = Workshop.new(params[:id])
render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
-
- def rescue_action(e) raise e end
end
class SessionsController < ActionController::Base
@@ -677,8 +691,6 @@ class SessionsController < ActionController::Base
@session = Session.new(params[:id])
render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
-
- def rescue_action(e) raise e end
end
class PolymorphicControllerTest < ActionController::TestCase
diff --git a/actionpack/test/ts_isolated.rb b/actionpack/test/ts_isolated.rb
index 5670d93613..7430de2299 100644
--- a/actionpack/test/ts_isolated.rb
+++ b/actionpack/test/ts_isolated.rb
@@ -1,10 +1,12 @@
+$:.unshift(File.dirname(__FILE__))
$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
-require 'test/unit'
+require 'minitest/autorun'
require 'rbconfig'
require 'active_support/core_ext/kernel/reporting'
+require 'abstract_unit'
-class TestIsolated < Test::Unit::TestCase
+class TestIsolated < ActiveSupport::TestCase
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
Dir["#{File.dirname(__FILE__)}/{abstract,controller,dispatch,template}/**/*_test.rb"].each do |file|
diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG
deleted file mode 100644
index bf077bef35..0000000000
--- a/activemodel/CHANGELOG
+++ /dev/null
@@ -1,109 +0,0 @@
-* Added ActiveModel::Errors#added? to check if a specific error has been added [Martin Svalin]
-
-* Add ability to define strict validation(with :strict => true option) that always raises exception when fails [Bogdan Gusiev]
-
-* Deprecate "Model.model_name.partial_path" in favor of "model.to_partial_path" [Grant Hutchins, Peter Jaros]
-
-* Provide mass_assignment_sanitizer as an easy API to replace the sanitizer behavior. Also support both :logger (default) and :strict sanitizer behavior [Bogdan Gusiev]
-
-*Rails 3.1.0 (August 30, 2011)*
-
-* Alternate I18n namespace lookup is no longer supported.
- Instead of "activerecord.models.admins.post", do "activerecord.models.admins/post" instead [José Valim]
-
-* attr_accessible and friends now accepts :as as option to specify a role [Josh Kalderimis]
-
-* Add support for proc or lambda as an option for InclusionValidator,
- ExclusionValidator, and FormatValidator [Prem Sichanugrist]
-
- You can now supply Proc, lambda, or anything that respond to #call in those
- validations, and it will be called with current record as an argument.
- That given proc or lambda must returns an object which respond to #include? for
- InclusionValidator and ExclusionValidator, and returns a regular expression
- object for FormatValidator.
-
-* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH]
-
-* ActiveModel::AttributeMethods allows attributes to be defined on demand [Alexander Uvarov]
-
-* Add support for selectively enabling/disabling observers [Myron Marston]
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-*No changes.
-
-
-*Rails 3.0.6 (April 5, 2011)
-
-* Fix when database column name has some symbolic characters (e.g. Oracle CASE# VARCHAR2(20)) #5818 #6850 [Robert Pankowecki, Santiago Pastorino]
-
-* Fix length validation for fixnums #6556 [Andriy Tyurnikov]
-
-* Fix i18n key collision with namespaced models #6448 [yves.senn]
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* No changes.
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* No changes
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* No Changes, just a version bump.
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* Added ActiveModel::MassAssignmentSecurity [Eric Chapweske, Josh Kalderimis]
-
-* JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh]
-
-* #new_record? and #destroyed? were removed from ActiveModel::Lint. Use
- persisted? instead. A model is persisted if it's not a new_record? and it was
- not destroyed? [MG]
-
-* Added validations reflection in ActiveModel::Validations [JV]
-
- Model.validators
- Model.validators_on(:field)
-
-* #to_key was added to ActiveModel::Lint so we can generate DOM IDs for
- AMo objects with composite keys [MG]
-
-* ActiveModel::Observer#add_observer!
-
- It has a custom hook to define after_find that should really be in a
- ActiveRecord::Observer subclass:
-
- def add_observer!(klass)
- klass.add_observer(self)
- klass.class_eval 'def after_find() end' unless klass.respond_to?(:after_find)
- end
-
-* Change the ActiveModel::Base.include_root_in_json default to true for Rails 3 [DHH]
-
-* Add validates_format_of :without => /regexp/ option. #430 [Elliot Winkler, Peer Allan]
-
- Example :
-
- validates_format_of :subdomain, :without => /www|admin|mail/
-
-* Introduce validates_with to encapsulate attribute validations in a class. #2630 [Jeff Dean]
-
-* Extracted from Active Record and Active Resource.
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
new file mode 100644
index 0000000000..258c3681f6
--- /dev/null
+++ b/activemodel/CHANGELOG.md
@@ -0,0 +1,118 @@
+* Trim down Active Model API by removing `valid?` and `errors.full_messages` *José Valim*
+
+## Rails 3.2.0 (January 20, 2012) ##
+
+* Deprecated `define_attr_method` in `ActiveModel::AttributeMethods`, because this only existed to
+ support methods like `set_table_name` in Active Record, which are themselves being deprecated.
+
+ *Jon Leighton*
+
+* Add ActiveModel::Errors#added? to check if a specific error has been added *Martin Svalin*
+
+* Add ability to define strict validation(with :strict => true option) that always raises exception when fails *Bogdan Gusiev*
+
+* Deprecate "Model.model_name.partial_path" in favor of "model.to_partial_path" *Grant Hutchins, Peter Jaros*
+
+* Provide mass_assignment_sanitizer as an easy API to replace the sanitizer behavior. Also support both :logger (default) and :strict sanitizer behavior *Bogdan Gusiev*
+
+## Rails 3.1.0 (August 30, 2011) ##
+
+* Alternate I18n namespace lookup is no longer supported.
+ Instead of "activerecord.models.admins.post", do "activerecord.models.admins/post" instead *José Valim*
+
+* attr_accessible and friends now accepts :as as option to specify a role *Josh Kalderimis*
+
+* Add support for proc or lambda as an option for InclusionValidator,
+ ExclusionValidator, and FormatValidator *Prem Sichanugrist*
+
+ You can now supply Proc, lambda, or anything that respond to #call in those
+ validations, and it will be called with current record as an argument.
+ That given proc or lambda must returns an object which respond to #include? for
+ InclusionValidator and ExclusionValidator, and returns a regular expression
+ object for FormatValidator.
+
+* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with BCrypt encryption and salting *DHH*
+
+* ActiveModel::AttributeMethods allows attributes to be defined on demand *Alexander Uvarov*
+
+* Add support for selectively enabling/disabling observers *Myron Marston*
+
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* No changes.
+
+
+* Rails 3.0.6 (April 5, 2011)
+
+* Fix when database column name has some symbolic characters (e.g. Oracle CASE# VARCHAR2(20)) #5818 #6850 *Robert Pankowecki, Santiago Pastorino*
+
+* Fix length validation for fixnums #6556 *Andriy Tyurnikov*
+
+* Fix i18n key collision with namespaced models #6448 *yves.senn*
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* No changes.
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* No changes
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* No Changes, just a version bump.
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* Added ActiveModel::MassAssignmentSecurity *Eric Chapweske, Josh Kalderimis*
+
+* JSON supports a custom root option: to_json(:root => 'custom') #4515 *Jatinder Singh*
+
+* #new_record? and #destroyed? were removed from ActiveModel::Lint. Use
+ persisted? instead. A model is persisted if it's not a new_record? and it was
+ not destroyed? *MG*
+
+* Added validations reflection in ActiveModel::Validations *JV*
+
+ Model.validators
+ Model.validators_on(:field)
+
+* #to_key was added to ActiveModel::Lint so we can generate DOM IDs for
+ AMo objects with composite keys *MG*
+
+* ActiveModel::Observer#add_observer!
+
+ It has a custom hook to define after_find that should really be in a
+ ActiveRecord::Observer subclass:
+
+ def add_observer!(klass)
+ klass.add_observer(self)
+ klass.class_eval 'def after_find() end' unless klass.respond_to?(:after_find)
+ end
+
+* Change the ActiveModel::Base.include_root_in_json default to true for Rails 3 *DHH*
+
+* Add validates_format_of :without => /regexp/ option. #430 *Elliot Winkler, Peer Allan*
+
+ Example :
+
+ validates_format_of :subdomain, :without => /www|admin|mail/
+
+* Introduce validates_with to encapsulate attribute validations in a class. #2630 *Jeff Dean*
+
+* Extracted from Active Record and Active Resource.
diff --git a/activemodel/MIT-LICENSE b/activemodel/MIT-LICENSE
index 7ad1051066..810daf856c 100644
--- a/activemodel/MIT-LICENSE
+++ b/activemodel/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2011 David Heinemeier Hansson
+Copyright (c) 2004-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 67701bc422..a7ba27ba73 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -9,10 +9,12 @@ Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
interact with Action Pack helpers, it was required to either copy chunks of
code from Rails, or monkey patch entire helpers to make them handle objects
that did not exactly conform to the Active Record interface. This would result
-in code duplication and fragile applications that broke on upgrades.
+in code duplication and fragile applications that broke on upgrades. Active
+Model solves this by defining an explicit API. You can read more about the
+API in ActiveModel::Lint::Tests.
-Active Model solves this. You can include functionality from the following
-modules:
+Active Model also provides the following functionality to have ORM-like
+behavior out of the box:
* Add attribute magic to objects
@@ -87,10 +89,9 @@ modules:
errors.add(:name, "can not be nil") if name.nil?
end
- def ErrorsPerson.human_attribute_name(attr, options = {})
+ def self.human_attribute_name(attr, options = {})
"Name"
end
-
end
person.errors.full_messages
@@ -163,7 +164,7 @@ modules:
* Custom validators
- class Person
+ class ValidatorPerson
include ActiveModel::Validations
validates_with HasNameValidator
attr_accessor :name
@@ -171,7 +172,7 @@ modules:
class HasNameValidator < ActiveModel::Validator
def validate(record)
- record.errors[:name] = "must exist" if record.name.blank?
+ record.errors[:name] = "must exist" if record.name.blank?
end
end
@@ -182,7 +183,7 @@ modules:
p.valid? # => true
{Learn more}[link:classes/ActiveModel/Validator.html]
-
+
== Download and installation
@@ -197,7 +198,9 @@ Source code can be downloaded as part of the Rails project on GitHub
== License
-Active Model is released under the MIT license.
+Active Model is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index c4b020196d..fc5aaf9f8f 100755
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -8,6 +8,7 @@ Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort
t.warning = true
+ t.verbose = true
end
namespace :test do
diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index 260ad01b65..60c1d16934 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -7,16 +7,15 @@ Gem::Specification.new do |s|
s.summary = 'A toolkit for building modeling frameworks (part of Rails).'
s.description = 'A toolkit for building modeling frameworks like Active Record and Active Resource. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*']
+ s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
s.add_dependency('activesupport', version)
s.add_dependency('builder', '~> 3.0.0')
- s.add_dependency('i18n', '~> 0.6')
end
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index d0e2a6f39c..85514e63fd 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2011 David Heinemeier Hansson
+# Copyright (c) 2004-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -32,6 +32,7 @@ module ActiveModel
autoload :AttributeMethods
autoload :BlockValidator, 'active_model/validator'
autoload :Callbacks
+ autoload :Configuration
autoload :Conversion
autoload :Dirty
autoload :EachValidator, 'active_model/validator'
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index ef0b95424e..52f270ff33 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -57,72 +57,16 @@ module ActiveModel
module AttributeMethods
extend ActiveSupport::Concern
- COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
+ NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
+ CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
included do
- class_attribute :attribute_method_matchers, :instance_writer => false
+ extend ActiveModel::Configuration
+ config_attribute :attribute_method_matchers
self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
end
module ClassMethods
- # Defines an "attribute" method (like +inheritance_column+ or +table_name+).
- # A new (class) method will be created with the given name. If a value is
- # specified, the new method will return that value (as a string).
- # Otherwise, the given block will be used to compute the value of the
- # method.
- #
- # The original method will be aliased, with the new name being prefixed
- # with "original_". This allows the new method to access the original
- # value.
- #
- # Example:
- #
- # class Person
- #
- # include ActiveModel::AttributeMethods
- #
- # cattr_accessor :primary_key
- # cattr_accessor :inheritance_column
- #
- # define_attr_method :primary_key, "sysid"
- # define_attr_method( :inheritance_column ) do
- # original_inheritance_column + "_id"
- # end
- #
- # end
- #
- # Provides you with:
- #
- # Person.primary_key
- # # => "sysid"
- # Person.inheritance_column = 'address'
- # Person.inheritance_column
- # # => 'address_id'
- def define_attr_method(name, value=nil, &block)
- sing = singleton_class
- sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
- if method_defined?('original_#{name}')
- undef :'original_#{name}'
- end
- alias_method :'original_#{name}', :'#{name}'
- eorb
- if block_given?
- sing.send :define_method, name, &block
- else
- # If we can compile the method name, do it. Otherwise use define_method.
- # This is an important *optimization*, please don't change it. define_method
- # has slower dispatch and consumes more memory.
- if name =~ COMPILABLE_REGEXP
- sing.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end
- RUBY
- else
- value = value.to_s if value
- sing.send(:define_method, name) { value }
- end
- end
- end
-
# Declares a method available for all attributes with the given prefix.
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
#
@@ -240,18 +184,7 @@ module ActiveModel
attribute_method_matchers.each do |matcher|
matcher_new = matcher.method_name(new_name).to_s
matcher_old = matcher.method_name(old_name).to_s
-
- if matcher_new =~ COMPILABLE_REGEXP && matcher_old =~ COMPILABLE_REGEXP
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{matcher_new}(*args)
- send(:#{matcher_old}, *args)
- end
- RUBY
- else
- define_method(matcher_new) do |*args|
- send(matcher_old, *args)
- end
- end
+ define_optimized_call self, matcher_new, matcher_old
end
end
@@ -293,17 +226,7 @@ module ActiveModel
if respond_to?(generate_method)
send(generate_method, attr_name)
else
- if method_name =~ COMPILABLE_REGEXP
- defn = "def #{method_name}(*args)"
- else
- defn = "define_method(:'#{method_name}') do |*args|"
- end
-
- generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
- #{defn}
- send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
- end
- RUBY
+ define_optimized_call generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
end
end
end
@@ -342,11 +265,11 @@ module ActiveModel
# used to alleviate the GC, which ultimately also speeds up the app
# significantly (in our case our test suite finishes 10% faster with
# this cache).
- def attribute_method_matchers_cache
+ def attribute_method_matchers_cache #:nodoc:
@attribute_method_matchers_cache ||= {}
end
- def attribute_method_matcher(method_name)
+ def attribute_method_matcher(method_name) #:nodoc:
if attribute_method_matchers_cache.key?(method_name)
attribute_method_matchers_cache[method_name]
else
@@ -359,6 +282,31 @@ module ActiveModel
end
end
+ # Define a method `name` in `mod` that dispatches to `send`
+ # using the given `extra` args. This fallbacks `define_method`
+ # and `send` if the given names cannot be compiled.
+ def define_optimized_call(mod, name, send, *extra) #:nodoc:
+ if name =~ NAME_COMPILABLE_REGEXP
+ defn = "def #{name}(*args)"
+ else
+ defn = "define_method(:'#{name}') do |*args|"
+ end
+
+ extra = (extra.map(&:inspect) << "*args").join(", ")
+
+ if send =~ CALL_COMPILABLE_REGEXP
+ target = "#{send}(#{extra})"
+ else
+ target = "send(:'#{send}', #{extra})"
+ end
+
+ mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ #{defn}
+ #{target}
+ end
+ RUBY
+ end
+
class AttributeMethodMatcher
attr_reader :prefix, :suffix, :method_missing_target
@@ -377,14 +325,14 @@ module ActiveModel
end
@prefix, @suffix = options[:prefix] || '', options[:suffix] || ''
- @regex = /^(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})$/
+ @regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
@method_missing_target = "#{@prefix}attribute#{@suffix}"
@method_name = "#{prefix}%s#{suffix}"
end
def match(method_name)
if @regex =~ method_name
- AttributeMethodMatch.new(method_missing_target, $2, method_name)
+ AttributeMethodMatch.new(method_missing_target, $1, method_name)
else
nil
end
@@ -446,7 +394,7 @@ module ActiveModel
protected
def attribute_method?(attr_name)
- attributes.include?(attr_name)
+ respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
end
private
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index 37d0c9a0b9..ebb4b51aa3 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/callbacks'
module ActiveModel
@@ -41,7 +40,7 @@ module ActiveModel
# You can choose not to have all three callbacks by passing a hash to the
# define_model_callbacks method.
#
- # define_model_callbacks :create, :only => :after, :before
+ # define_model_callbacks :create, :only => [:after, :before]
#
# Would only create the after_create and before_create callback methods in your
# class.
@@ -89,11 +88,12 @@ module ActiveModel
options = callbacks.extract_options!
options = {
:terminator => "result == false",
+ :skip_after_callbacks_if_terminated => true,
:scope => [:kind, :name],
:only => [:before, :around, :after]
}.merge(options)
- types = Array.wrap(options.delete(:only))
+ types = Array(options.delete(:only))
callbacks.each do |callback|
define_callbacks(callback, options)
@@ -125,7 +125,7 @@ module ActiveModel
def self.after_#{callback}(*args, &block)
options = args.extract_options!
options[:prepend] = true
- options[:if] = Array.wrap(options[:if]) << "!halted && value != false"
+ options[:if] = Array(options[:if]) << "value != false"
set_callback(:#{callback}, :after, *(args << options), &block)
end
CALLBACK
diff --git a/activemodel/lib/active_model/configuration.rb b/activemodel/lib/active_model/configuration.rb
new file mode 100644
index 0000000000..1757c12ebf
--- /dev/null
+++ b/activemodel/lib/active_model/configuration.rb
@@ -0,0 +1,134 @@
+require 'active_support/concern'
+require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/class/attribute_accessors'
+
+module ActiveModel
+ # This API is for Rails' internal use and is not currently considered 'public', so
+ # it may change in the future without warning.
+ #
+ # It creates configuration attributes that can be inherited from a module down
+ # to a class that includes the module. E.g.
+ #
+ # module MyModel
+ # extend ActiveModel::Configuration
+ # config_attribute :awesome
+ # self.awesome = true
+ # end
+ #
+ # class Post
+ # include MyModel
+ # end
+ #
+ # Post.awesome # => true
+ #
+ # Post.awesome = false
+ # Post.awesome # => false
+ # MyModel.awesome # => true
+ #
+ # We assume that the module will have a ClassMethods submodule containing methods
+ # to be transferred to the including class' singleton class.
+ #
+ # Config options can also be defined directly on a class:
+ #
+ # class Post
+ # extend ActiveModel::Configuration
+ # config_attribute :awesome
+ # end
+ #
+ # So this allows us to define a module that doesn't care about whether it is being
+ # included in a class or a module:
+ #
+ # module Awesomeness
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # extend ActiveModel::Configuration
+ # config_attribute :awesome
+ # self.awesome = true
+ # end
+ # end
+ #
+ # class Post
+ # include Awesomeness
+ # end
+ #
+ # module AwesomeModel
+ # include Awesomeness
+ # end
+ module Configuration #:nodoc:
+ def config_attribute(name, options = {})
+ klass = self.is_a?(Class) ? ClassAttribute : ModuleAttribute
+ klass.new(self, name, options).define
+ end
+
+ class Attribute
+ attr_reader :host, :name, :options
+
+ def initialize(host, name, options)
+ @host, @name, @options = host, name, options
+ end
+
+ def instance_writer?
+ options.fetch(:instance_writer, false)
+ end
+ end
+
+ class ClassAttribute < Attribute
+ def define
+ if options[:global]
+ host.cattr_accessor name, :instance_writer => instance_writer?
+ else
+ host.class_attribute name, :instance_writer => instance_writer?
+ end
+ end
+ end
+
+ class ModuleAttribute < Attribute
+ def class_methods
+ @class_methods ||= begin
+ if host.const_defined?(:ClassMethods, false)
+ host.const_get(:ClassMethods)
+ else
+ host.const_set(:ClassMethods, Module.new)
+ end
+ end
+ end
+
+ def define
+ host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__
+ attr_accessor :#{name}
+ def #{name}?; !!#{name}; end
+ CODE
+
+ name, host = self.name, self.host
+
+ class_methods.class_eval do
+ define_method(name) { host.send(name) }
+ define_method("#{name}?") { !!send(name) }
+ end
+
+ host.class_eval <<-CODE
+ def #{name}; defined?(@#{name}) ? @#{name} : self.class.#{name}; end
+ def #{name}?; !!#{name}; end
+ CODE
+
+ if options[:global]
+ class_methods.class_eval do
+ define_method("#{name}=") { |val| host.send("#{name}=", val) }
+ end
+ else
+ class_methods.class_eval <<-CODE, __FILE__, __LINE__
+ def #{name}=(val)
+ singleton_class.class_eval do
+ remove_possible_method(:#{name})
+ define_method(:#{name}) { val }
+ end
+ end
+ CODE
+ end
+
+ host.send(:attr_writer, name) if instance_writer?
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index 80a3ba51c3..c7c805f1a2 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -39,11 +39,9 @@ module ActiveModel
# Returns an Enumerable of all key attributes if any is set, regardless
# if the object is persisted or not.
- #
- # Note the default implementation uses persisted? just because all objects
- # in Ruby 1.8.x responds to <tt>:id</tt>.
def to_key
- persisted? ? [id] : nil
+ key = respond_to?(:id) && id
+ key ? [key] : nil
end
# Returns a string representing the object's key suitable for use in URLs,
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 166cccf161..026f077ee7 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -98,7 +98,7 @@ module ActiveModel
# person.name = 'bob'
# person.changed? # => true
def changed?
- !changed_attributes.empty?
+ changed_attributes.any?
end
# List of attributes with unsaved changes.
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 8337b04c0d..e548aa975d 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -1,16 +1,14 @@
# -*- coding: utf-8 -*-
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/reverse_merge'
-require 'active_support/ordered_hash'
module ActiveModel
# == Active Model Errors
#
- # Provides a modified +OrderedHash+ that you can include in your object
+ # Provides a modified +Hash+ that you can include in your object
# for handling error messages and interacting with Action Pack helpers.
#
# A minimal implementation could be:
@@ -76,7 +74,12 @@ module ActiveModel
# end
def initialize(base)
@base = base
- @messages = ActiveSupport::OrderedHash.new
+ @messages = {}
+ end
+
+ def initialize_dup(other)
+ @messages = other.messages.dup
+ super
end
# Clear the messages
@@ -100,6 +103,11 @@ module ActiveModel
messages[key] = value
end
+ # Delete messages for +key+
+ def delete(key)
+ messages.delete(key)
+ end
+
# When passed a symbol or a name of a method, returns an array of errors
# for the method.
#
@@ -114,7 +122,7 @@ module ActiveModel
# p.errors[:name] = "must be set"
# p.errors[:name] # => ['must be set']
def []=(attribute, error)
- self[attribute.to_sym] << error
+ self[attribute] << error
end
# Iterates through each error key, value pair in the error messages hash.
@@ -176,8 +184,9 @@ module ActiveModel
end
# Returns true if no errors are found, false otherwise.
+ # If the error message is a string it can be empty.
def empty?
- all? { |k, v| v && v.empty? }
+ all? { |k, v| v && v.empty? && !v.is_a?(String) }
end
alias_method :blank?, :empty?
@@ -196,7 +205,7 @@ module ActiveModel
to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
end
- # Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
+ # Returns an Hash that can be used as the JSON representation for this object.
def as_json(options=nil)
to_hash
end
@@ -209,12 +218,12 @@ module ActiveModel
# +attribute+.
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
#
- # If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
+ # If +message+ is a symbol, it will be translated using the appropriate scope (see +generate_message+).
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
def add(attribute, message = nil, options = {})
message = normalize_message(attribute, message, options)
if options[:strict]
- raise ActiveModel::StrictValidationFailed, message
+ raise ActiveModel::StrictValidationFailed, full_message(attribute, message)
end
self[attribute] << message
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index bfe7ea1869..49ea894150 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -3,9 +3,13 @@ module ActiveModel
# == Active Model Lint Tests
#
# You can test whether an object is compliant with the Active Model API by
- # including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will include
- # tests that tell you whether your object is fully compliant, or if not,
- # which aspects of the API are not implemented.
+ # including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will
+ # include tests that tell you whether your object is fully compliant,
+ # or if not, which aspects of the API are not implemented.
+ #
+ # Note an object is not required to implement all APIs in order to work
+ # with Action Pack. This module only intends to provide guidance in case
+ # you want all features out of the box.
#
# These tests do not attempt to determine the semantic correctness of the
# returned values. For instance, you could implement valid? to always
@@ -19,7 +23,8 @@ module ActiveModel
# == Responds to <tt>to_key</tt>
#
# Returns an Enumerable of all (primary) key attributes
- # or nil if model.persisted? is false
+ # or nil if model.persisted? is false. This is used by
+ # dom_id to generate unique ids for the object.
def test_to_key
assert model.respond_to?(:to_key), "The model should respond to to_key"
def model.persisted?() false end
@@ -53,15 +58,6 @@ module ActiveModel
assert_kind_of String, model.to_partial_path
end
- # == Responds to <tt>valid?</tt>
- #
- # Returns a boolean that specifies whether the object is in a valid or invalid
- # state.
- def test_valid?
- assert model.respond_to?(:valid?), "The model should respond to valid?"
- assert_boolean model.valid?, "valid?"
- end
-
# == Responds to <tt>persisted?</tt>
#
# Returns a boolean that specifies whether the object has been persisted yet.
@@ -90,25 +86,15 @@ module ActiveModel
# == Errors Testing
#
- # Returns an object that has :[] and :full_messages defined on it. See below
- # for more details.
- #
- # Returns an Array of Strings that are the errors for the attribute in
- # question. If localization is used, the Strings should be localized
- # for the current locale. If no error is present, this method should
- # return an empty Array.
+ # Returns an object that implements [](attribute) defined which returns an
+ # Array of Strings that are the errors for the attribute in question.
+ # If localization is used, the Strings should be localized for the current
+ # locale. If no error is present, this method should return an empty Array.
def test_errors_aref
assert model.respond_to?(:errors), "The model should respond to errors"
assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
end
- # Returns an Array of all error messages for the object. Each message
- # should contain information about the field, if applicable.
- def test_errors_full_messages
- assert model.respond_to?(:errors), "The model should respond to errors"
- assert model.errors.full_messages.is_a?(Array), "errors#full_messages should return an Array"
- end
-
private
def model
assert @model.respond_to?(:to_model), "The object should respond_to to_model"
diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml
index 44425b4a28..ba49c6beaa 100644
--- a/activemodel/lib/active_model/locale/en.yml
+++ b/activemodel/lib/active_model/locale/en.yml
@@ -23,5 +23,6 @@ en:
equal_to: "must be equal to %{count}"
less_than: "must be less than %{count}"
less_than_or_equal_to: "must be less than or equal to %{count}"
+ other_than: "must be other than %{count}"
odd: "must be odd"
even: "must be even"
diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb
index 3f9feb7631..95de039676 100644
--- a/activemodel/lib/active_model/mass_assignment_security.rb
+++ b/activemodel/lib/active_model/mass_assignment_security.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/array/wrap'
require 'active_model/mass_assignment_security/permission_set'
require 'active_model/mass_assignment_security/sanitizer'
@@ -10,11 +9,13 @@ module ActiveModel
extend ActiveSupport::Concern
included do
- class_attribute :_accessible_attributes
- class_attribute :_protected_attributes
- class_attribute :_active_authorizer
+ extend ActiveModel::Configuration
- class_attribute :_mass_assignment_sanitizer
+ config_attribute :_accessible_attributes
+ config_attribute :_protected_attributes
+ config_attribute :_active_authorizer
+
+ config_attribute :_mass_assignment_sanitizer
self.mass_assignment_sanitizer = :logger
end
@@ -56,7 +57,7 @@ module ActiveModel
# You can specify your own sanitizer object eg. MySanitizer.new.
# See <tt>ActiveModel::MassAssignmentSecurity::LoggerSanitizer</tt> for example implementation.
#
- #
+ #
module ClassMethods
# Attributes named in this macro are protected from mass-assignment
# whenever attributes are sanitized before assignment. A role for the
@@ -71,10 +72,11 @@ module ActiveModel
# class Customer
# include ActiveModel::MassAssignmentSecurity
#
- # attr_accessor :name, :credit_rating
+ # attr_accessor :name, :email, :logins_count
#
- # attr_protected :credit_rating, :last_login
- # attr_protected :last_login, :as => :admin
+ # attr_protected :logins_count
+ # # Suppose that admin can not change email for customer
+ # attr_protected :logins_count, :email, :as => :admin
#
# def assign_attributes(values, options = {})
# sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
@@ -86,21 +88,21 @@ module ActiveModel
# When using the :default role :
#
# customer = Customer.new
- # customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default)
+ # customer.assign_attributes({ "name" => "David", "email" => "a@b.com", :logins_count => 5 }, :as => :default)
# customer.name # => "David"
- # customer.credit_rating # => nil
- # customer.last_login # => nil
- #
- # customer.credit_rating = "Average"
- # customer.credit_rating # => "Average"
+ # customer.email # => "a@b.com"
+ # customer.logins_count # => nil
#
# And using the :admin role :
#
# customer = Customer.new
- # customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin)
+ # customer.assign_attributes({ "name" => "David", "email" => "a@b.com", :logins_count => 5}, :as => :admin)
# customer.name # => "David"
- # customer.credit_rating # => "Excellent"
- # customer.last_login # => nil
+ # customer.email # => nil
+ # customer.logins_count # => nil
+ #
+ # customer.email = "c@d.com"
+ # customer.email # => "c@d.com"
#
# To start from an all-closed default and enable attributes as needed,
# have a look at +attr_accessible+.
@@ -113,7 +115,7 @@ module ActiveModel
self._protected_attributes = protected_attributes_configs.dup
- Array.wrap(role).each do |name|
+ Array(role).each do |name|
self._protected_attributes[name] = self.protected_attributes(name) + args
end
@@ -175,7 +177,7 @@ module ActiveModel
self._accessible_attributes = accessible_attributes_configs.dup
- Array.wrap(role).each do |name|
+ Array(role).each do |name|
self._accessible_attributes[name] = self.accessible_attributes(name) + args
end
@@ -224,12 +226,12 @@ module ActiveModel
protected
- def sanitize_for_mass_assignment(attributes, role = :default)
+ def sanitize_for_mass_assignment(attributes, role = nil)
_mass_assignment_sanitizer.sanitize(attributes, mass_assignment_authorizer(role))
end
- def mass_assignment_authorizer(role = :default)
- self.class.active_authorizer[role]
+ def mass_assignment_authorizer(role)
+ self.class.active_authorizer[role || :default]
end
end
end
diff --git a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
index a1fcdf1a38..9661349503 100644
--- a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
+++ b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
@@ -13,7 +13,7 @@ module ActiveModel
end
def deny?(key)
- raise NotImplementedError, "#deny?(key) suppose to be overwritten"
+ raise NotImplementedError, "#deny?(key) supposed to be overwritten"
end
protected
diff --git a/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb b/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
index bbdddfb50d..cfeb4aa7cd 100644
--- a/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
+++ b/activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
@@ -1,11 +1,6 @@
-require 'active_support/core_ext/module/delegation'
-
module ActiveModel
module MassAssignmentSecurity
class Sanitizer
- def initialize(target=nil)
- end
-
# Returns all attributes not denied by the authorizer.
def sanitize(attributes, authorizer)
sanitized_attributes = attributes.reject { |key, value| authorizer.deny?(key) }
@@ -26,11 +21,13 @@ module ActiveModel
end
class LoggerSanitizer < Sanitizer
- delegate :logger, :to => :@target
-
def initialize(target)
@target = target
- super
+ super()
+ end
+
+ def logger
+ @target.logger
end
def logger?
@@ -38,14 +35,18 @@ module ActiveModel
end
def process_removed_attributes(attrs)
- logger.debug "WARNING: Can't mass-assign protected attributes: #{attrs.join(', ')}" if logger?
+ logger.warn "Can't mass-assign protected attributes: #{attrs.join(', ')}" if logger?
end
end
class StrictSanitizer < Sanitizer
+ def initialize(target = nil)
+ super()
+ end
+
def process_removed_attributes(attrs)
return if (attrs - insensitive_attributes).empty?
- raise ActiveModel::MassAssignmentSecurity::Error, "Can't mass-assign protected attributes: #{attrs.join(', ')}"
+ raise ActiveModel::MassAssignmentSecurity::Error.new(attrs)
end
def insensitive_attributes
@@ -54,6 +55,9 @@ module ActiveModel
end
class Error < StandardError
+ def initialize(attrs)
+ super("Can't mass-assign protected attributes: #{attrs.join(', ')}")
+ end
end
end
end
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index f16459ede2..755e54efcd 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -5,26 +5,35 @@ require 'active_support/core_ext/module/deprecation'
module ActiveModel
class Name < String
- attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key
+ attr_reader :singular, :plural, :element, :collection, :partial_path,
+ :singular_route_key, :route_key, :param_key, :i18n_key
+
alias_method :cache_key, :collection
deprecate :partial_path => "ActiveModel::Name#partial_path is deprecated. Call #to_partial_path on model instances directly instead."
def initialize(klass, namespace = nil, name = nil)
name ||= klass.name
+
+ raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if name.blank?
+
super(name)
- @unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
- @klass = klass
- @singular = _singularize(self).freeze
- @plural = ActiveSupport::Inflector.pluralize(@singular).freeze
- @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
- @human = ActiveSupport::Inflector.humanize(@element).freeze
- @collection = ActiveSupport::Inflector.tableize(self).freeze
+ @unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
+ @klass = klass
+ @singular = _singularize(self).freeze
+ @plural = ActiveSupport::Inflector.pluralize(@singular).freeze
+ @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
+ @human = ActiveSupport::Inflector.humanize(@element).freeze
+ @collection = ActiveSupport::Inflector.tableize(self).freeze
@partial_path = "#{@collection}/#{@element}".freeze
- @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
- @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze
- @i18n_key = self.underscore.to_sym
+ @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
+ @i18n_key = self.underscore.to_sym
+
+ @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
+ @singular_route_key = ActiveSupport::Inflector.singularize(@route_key).freeze
+ @route_key << "_index" if @plural == @singular
+ @route_key.freeze
end
# Transform the model name into a more humane format, using I18n. By default,
@@ -68,8 +77,8 @@ module ActiveModel
# BookCover.model_name # => "BookCover"
# BookCover.model_name.human # => "Book cover"
#
- # BookCover.model_name.i18n_key # => "book_cover"
- # BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover"
+ # BookCover.model_name.i18n_key # => :book_cover
+ # BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
#
# Providing the functionality that ActiveModel::Naming provides in your object
# is required to pass the Active Model Lint test. So either extending the provided
@@ -79,7 +88,9 @@ module ActiveModel
# used to retrieve all kinds of naming-related information.
def model_name
@_model_name ||= begin
- namespace = self.parents.detect { |n| n.respond_to?(:_railtie) }
+ namespace = self.parents.detect do |n|
+ n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
+ end
ActiveModel::Name.new(self, namespace)
end
end
@@ -112,10 +123,25 @@ module ActiveModel
# namespaced models regarding whether it's inside isolated engine.
#
# For isolated engine:
+ # ActiveModel::Naming.route_key(Blog::Post) #=> post
+ #
+ # For shared engine:
+ # ActiveModel::Naming.route_key(Blog::Post) #=> blog_post
+ def self.singular_route_key(record_or_class)
+ model_name_from_record_or_class(record_or_class).singular_route_key
+ end
+
+ # Returns string to use while generating route names. It differs for
+ # namespaced models regarding whether it's inside isolated engine.
+ #
+ # For isolated engine:
# ActiveModel::Naming.route_key(Blog::Post) #=> posts
#
# For shared engine:
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
+ #
+ # The route key also considers if the noun is uncountable and, in
+ # such cases, automatically appends _index.
def self.route_key(record_or_class)
model_name_from_record_or_class(record_or_class).route_key
end
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index 7a910d18e7..32f2aa46bd 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -1,6 +1,5 @@
require 'singleton'
require 'active_model/observer_array'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/string/inflections'
@@ -64,7 +63,7 @@ module ActiveModel
# raises an +ArgumentError+ exception.
def add_observer(observer)
unless observer.respond_to? :update
- raise ArgumentError, "observer needs to respond to `update'"
+ raise ArgumentError, "observer needs to respond to 'update'"
end
observer_instances << observer
end
@@ -187,8 +186,7 @@ module ActiveModel
def observe(*models)
models.flatten!
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
- remove_possible_method(:observed_classes)
- define_method(:observed_classes) { models }
+ redefine_method(:observed_classes) { models }
end
# Returns an array of Classes to observe.
@@ -201,7 +199,7 @@ module ActiveModel
# end
# end
def observed_classes
- Array.wrap(observed_class)
+ Array(observed_class)
end
# The class observed by default is inferred from the observer's class name:
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index db78864c67..e7a57cf691 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -29,7 +29,7 @@ module ActiveModel
# user.save # => true
# user.authenticate("notright") # => false
# user.authenticate("mUc3m00RsqyRe") # => user
- # User.find_by_name("david").try(:authenticate, "notright") # => nil
+ # User.find_by_name("david").try(:authenticate, "notright") # => false
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
def has_secure_password
# Load bcrypt-ruby only when has_secure_password is used.
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index a4b58ab456..51f078e662 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -17,7 +17,7 @@ module ActiveModel
# attr_accessor :name
#
# def attributes
- # {'name' => name}
+ # {'name' => nil}
# end
#
# end
@@ -29,8 +29,11 @@ module ActiveModel
# person.name = "Bob"
# person.serializable_hash # => {"name"=>"Bob"}
#
- # You need to declare some sort of attributes hash which contains the attributes
- # you want to serialize and their current value.
+ # You need to declare an attributes hash which contains the attributes
+ # you want to serialize. When called, serializable hash will use
+ # instance methods that match the name of the attributes hash's keys.
+ # In order to override this behavior, take a look at the private
+ # method read_attribute_for_serialization.
#
# Most of the time though, you will want to include the JSON or XML
# serializations. Both of these modules automatically include the
@@ -47,7 +50,7 @@ module ActiveModel
# attr_accessor :name
#
# def attributes
- # {'name' => name}
+ # {'name' => nil}
# end
#
# end
@@ -73,16 +76,16 @@ module ActiveModel
attribute_names = attributes.keys.sort
if only = options[:only]
- attribute_names &= Array.wrap(only).map(&:to_s)
+ attribute_names &= Array(only).map(&:to_s)
elsif except = options[:except]
- attribute_names -= Array.wrap(except).map(&:to_s)
+ attribute_names -= Array(except).map(&:to_s)
end
hash = {}
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
- method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
- method_names.each { |n| hash[n] = send(n) }
+ method_names = Array(options[:methods]).select { |n| respond_to?(n) }
+ method_names.each { |n| hash[n.to_s] = send(n) }
serializable_add_includes(options) do |association, records, opts|
hash[association] = if records.is_a?(Enumerable)
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index c845440120..63ab8e7edc 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -10,8 +10,9 @@ module ActiveModel
included do
extend ActiveModel::Naming
+ extend ActiveModel::Configuration
- class_attribute :include_root_in_json
+ config_attribute :include_root_in_json
self.include_root_in_json = true
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index d61d9d7119..5084298210 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/hash/conversions'
@@ -56,7 +55,7 @@ module ActiveModel
end
def serializable_collection
- methods = Array.wrap(options[:methods]).map(&:to_s)
+ methods = Array(options[:methods]).map(&:to_s)
serializable_hash.map do |name, value|
name = name.to_s
if methods.include?(name)
@@ -146,7 +145,7 @@ module ActiveModel
def add_procs
if procs = options.delete(:procs)
- Array.wrap(procs).each do |proc|
+ Array(procs).each do |proc|
if proc.arity == 1
proc.call(options)
else
diff --git a/activemodel/lib/active_model/test_case.rb b/activemodel/lib/active_model/test_case.rb
index 6328807ad7..5004855d56 100644
--- a/activemodel/lib/active_model/test_case.rb
+++ b/activemodel/lib/active_model/test_case.rb
@@ -1,16 +1,4 @@
module ActiveModel #:nodoc:
class TestCase < ActiveSupport::TestCase #:nodoc:
- def with_kcode(kcode)
- if RUBY_VERSION < '1.9'
- orig_kcode, $KCODE = $KCODE, kcode
- begin
- yield
- ensure
- $KCODE = orig_kcode
- end
- else
- yield
- end
- end
end
end
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 6d64c81b5f..02b7c54d61 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -43,13 +43,25 @@ module ActiveModel
#
# Specify +options+ with additional translating options.
def human_attribute_name(attribute, options = {})
- defaults = lookup_ancestors.map do |klass|
- :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
+ defaults = []
+ parts = attribute.to_s.split(".", 2)
+ attribute = parts.pop
+ namespace = parts.pop
+
+ if namespace
+ lookup_ancestors.each do |klass|
+ defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
+ end
+ defaults << :"#{self.i18n_scope}.attributes.#{namespace}.#{attribute}"
+ else
+ lookup_ancestors.each do |klass|
+ defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
+ end
end
defaults << :"attributes.#{attribute}"
defaults << options.delete(:default) if options[:default]
- defaults << attribute.to_s.humanize
+ defaults << attribute.humanize
options.reverse_merge! :count => 1, :default => defaults
I18n.translate(defaults.shift, options)
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 8ed392abca..0e15155b85 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/array/extract_options'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/except'
@@ -34,7 +33,7 @@ module ActiveModel
# person.first_name = 'zoolander'
# person.valid? # => false
# person.invalid? # => true
- # person.errors # => #<OrderedHash {:first_name=>["starts with z."]}>
+ # person.errors # => #<Hash {:first_name=>["starts with z."]}>
#
# Note that <tt>ActiveModel::Validations</tt> automatically adds an +errors+ method
# to your instances initialized with a new <tt>ActiveModel::Errors</tt> object, so
@@ -42,9 +41,9 @@ module ActiveModel
#
module Validations
extend ActiveSupport::Concern
- include ActiveSupport::Callbacks
included do
+ extend ActiveModel::Callbacks
extend ActiveModel::Translation
extend HelperMethods
@@ -53,7 +52,8 @@ module ActiveModel
attr_accessor :validation_context
define_callbacks :validate, :scope => :name
- class_attribute :_validators
+ extend ActiveModel::Configuration
+ config_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
end
@@ -132,7 +132,7 @@ module ActiveModel
options = args.extract_options!
if options.key?(:on)
options = options.dup
- options[:if] = Array.wrap(options[:if])
+ options[:if] = Array(options[:if])
options[:if].unshift("validation_context == :#{options[:on]}")
end
args << options
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index 22a77320dc..c39c85e1af 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -23,14 +23,14 @@ module ActiveModel
included do
include ActiveSupport::Callbacks
- define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
+ define_callbacks :validation, :terminator => "result == false", :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name]
end
module ClassMethods
def before_validation(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
- options[:if] = Array.wrap(options[:if])
+ options[:if] = Array(options[:if])
options[:if].unshift("self.validation_context == :#{options[:on]}")
end
set_callback(:validation, :before, *args, &block)
@@ -39,8 +39,7 @@ module ActiveModel
def after_validation(*args, &block)
options = args.extract_options!
options[:prepend] = true
- options[:if] = Array.wrap(options[:if])
- options[:if] << "!halted"
+ options[:if] = Array(options[:if])
options[:if].unshift("self.validation_context == :#{options[:on]}") if options[:on]
set_callback(:validation, :after, *(args << options), &block)
end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 6573a7d264..e8526303e2 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -37,7 +37,7 @@ module ActiveModel
# attribute.
#
# NOTE: This check is performed only if +password_confirmation+ is not
- # +nil+, and by default only on save. To require confirmation, make sure
+ # +nil+. To require confirmation, make sure
# to add a presence check for the confirmation attribute:
#
# validates_presence_of :password_confirmation, :if => :password_changed?
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index eb7aac709d..f1beddf6d4 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -6,14 +6,12 @@ module ActiveModel
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
- DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
def initialize(options)
if range = (options.delete(:in) || options.delete(:within))
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
- options[:minimum], options[:maximum] = range.begin, range.end
- options[:maximum] -= 1 if range.exclude_end?
+ options[:minimum], options[:maximum] = range.min, range.max
end
super
@@ -23,27 +21,24 @@ module ActiveModel
keys = CHECKS.keys & options.keys
if keys.empty?
- raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
+ raise ArgumentError, 'Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option.'
end
keys.each do |key|
value = options[key]
- unless value.is_a?(Integer) && value >= 0
- raise ArgumentError, ":#{key} must be a nonnegative Integer"
+ unless value.is_a?(Integer) && value >= 0 or value == Float::INFINITY
+ raise ArgumentError, ":#{key} must be a nonnegative Integer or Infinity"
end
end
end
def validate_each(record, attribute, value)
- value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
+ value = tokenize(value)
+ value_length = value.respond_to?(:length) ? value.length : value.to_s.length
CHECKS.each do |key, validity_check|
next unless check_value = options[key]
-
- value ||= [] if key == :maximum
-
- value_length = value.respond_to?(:length) ? value.length : value.to_s.length
next if value_length.send(validity_check, check_value)
errors_options = options.except(*RESERVED_OPTIONS)
@@ -55,6 +50,14 @@ module ActiveModel
record.errors.add(attribute, MESSAGES[key], errors_options)
end
end
+
+ private
+
+ def tokenize(value)
+ if options[:tokenizer] && value.kind_of?(String)
+ options[:tokenizer].call(value)
+ end || value
+ end
end
module HelperMethods
@@ -96,7 +99,7 @@ module ActiveModel
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
# count words as in above example.)
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
def validates_length_of(*attr_names)
validates_with LengthValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 34d447a0fa..bb9f9679fc 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -5,7 +5,7 @@ module ActiveModel
class NumericalityValidator < EachValidator
CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
:equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
- :odd => :odd?, :even => :even? }.freeze
+ :odd => :odd?, :even => :even?, :other_than => :!= }.freeze
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
@@ -99,6 +99,7 @@ module ActiveModel
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
# * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
# * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
+ # * <tt>:other_than</tt> - Specifies the value must be other than the supplied value.
# * <tt>:odd</tt> - Specifies the value must be an odd number.
# * <tt>:even</tt> - Specifies the value must be an even number.
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
@@ -107,7 +108,7 @@ module ActiveModel
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information
#
# The following checks can also be supplied with a proc or a symbol which corresponds to a method:
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index 35af7152db..9a643a6f5c 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -25,14 +25,14 @@ module ActiveModel
# This is due to the way Object#blank? handles boolean values: <tt>false.blank? # => true</tt>.
#
# Configuration options:
- # * <tt>message</tt> - A custom error message (default is: "can't be blank").
+ # * <tt>:message</tt> - A custom error message (default is: "can't be blank").
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
# The method, proc or string should return or evaluate to a true or false value.
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
# The method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index fbceb81e8f..d94c4e3f4f 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/hash/slice'
module ActiveModel
-
# == Active Model validates method
module Validations
module ClassMethods
@@ -57,9 +56,9 @@ module ActiveModel
#
# Additionally validator classes may be in another namespace and still used within any class.
#
- # validates :name, :'file/title' => true
+ # validates :name, :'film/title' => true
#
- # The validators hash can also handle regular expressions, ranges,
+ # The validators hash can also handle regular expressions, ranges,
# arrays and strings in shortcut form, e.g.
#
# validates :email, :format => /@/
@@ -70,7 +69,7 @@ module ActiveModel
# validator's initializer as +options[:in]+ while other types including
# regular expressions and strings are passed as +options[:with]+
#
- # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+ and +:strict+
+ # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+ and +:strict+
# can be given to one specific validator, as a hash:
#
# validates :password, :presence => { :if => :password_required? }, :confirmation => true
@@ -80,7 +79,7 @@ module ActiveModel
# validates :password, :presence => true, :confirmation => true, :if => :password_required?
#
def validates(*attributes)
- defaults = attributes.extract_options!
+ defaults = attributes.extract_options!.dup
validations = defaults.slice!(*_validates_default_keys)
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
@@ -101,12 +100,12 @@ module ActiveModel
end
end
- # This method is used to define validation that can not be corrected by end user
- # and is considered exceptional.
- # So each validator defined with bang or <tt>:strict</tt> option set to <tt>true</tt>
- # will always raise <tt>ActiveModel::InternalValidationFailed</tt> instead of adding error
- # when validation fails
- # See <tt>validates</tt> for more information about validation itself.
+ # This method is used to define validations that cannot be corrected by end
+ # users and are considered exceptional. So each validator defined with bang
+ # or <tt>:strict</tt> option set to <tt>true</tt> will always raise
+ # <tt>ActiveModel::StrictValidationFailed</tt> instead of adding error
+ # when validation fails.
+ # See <tt>validates</tt> for more information about the validation itself.
def validates!(*attributes)
options = attributes.extract_options!
options[:strict] = true
@@ -118,7 +117,7 @@ module ActiveModel
# When creating custom validators, it might be useful to be able to specify
# additional default keys. This can be done by overwriting this method.
def _validates_default_keys
- [ :if, :unless, :on, :allow_blank, :allow_nil , :strict]
+ [:if, :unless, :on, :allow_blank, :allow_nil , :strict]
end
def _parse_validates_options(options) #:nodoc:
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 93a340eb39..72b8562b93 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -56,7 +56,7 @@ module ActiveModel
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
# or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
# The method, proc or string should return or evaluate to a true or false value.
- # * <tt>unless</tt> - Specifies a method, proc or string to call to
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
# determine if the validation should not occur
# (e.g. <tt>:unless => :skip_validation</tt>, or
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 35ec98c822..2953126c3c 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require "active_support/core_ext/module/anonymous"
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/inclusion'
@@ -137,7 +136,7 @@ module ActiveModel #:nodoc:
# +options+ reader, however the <tt>:attributes</tt> option will be removed
# and instead be made available through the +attributes+ reader.
def initialize(options)
- @attributes = Array.wrap(options.delete(:attributes))
+ @attributes = Array(options.delete(:attributes))
raise ":attributes cannot be blank" if @attributes.empty?
super
check_validity!
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index dbda55ca7c..e195c12a4d 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -1,7 +1,7 @@
module ActiveModel
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb
index 67471ed497..9406328d3e 100644
--- a/activemodel/test/cases/attribute_methods_test.rb
+++ b/activemodel/test/cases/attribute_methods_test.rb
@@ -76,7 +76,15 @@ private
end
end
+class ModelWithoutAttributesMethod
+ include ActiveModel::AttributeMethods
+end
+
class AttributeMethodsTest < ActiveModel::TestCase
+ test 'method missing works correctly even if attributes method is not defined' do
+ assert_raises(NoMethodError) { ModelWithoutAttributesMethod.new.foo }
+ end
+
test 'unrelated classes should not share attribute method matchers' do
assert_not_equal ModelWithAttributes.send(:attribute_method_matchers),
ModelWithAttributes2.send(:attribute_method_matchers)
@@ -133,24 +141,6 @@ class AttributeMethodsTest < ActiveModel::TestCase
assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar')
end
- test '#define_attr_method generates attribute method' do
- ModelWithAttributes.define_attr_method(:bar, 'bar')
-
- assert_respond_to ModelWithAttributes, :bar
- assert_equal "original bar", ModelWithAttributes.original_bar
- assert_equal "bar", ModelWithAttributes.bar
- ModelWithAttributes.define_attr_method(:bar)
- assert !ModelWithAttributes.bar
- end
-
- test '#define_attr_method generates attribute method with invalid identifier characters' do
- ModelWithWeirdNamesAttributes.define_attr_method(:'c?d', 'c?d')
-
- assert_respond_to ModelWithWeirdNamesAttributes, :'c?d'
- assert_equal "original c?d", ModelWithWeirdNamesAttributes.send('original_c?d')
- assert_equal "c?d", ModelWithWeirdNamesAttributes.send('c?d')
- end
-
test '#alias_attribute works with attributes with spaces in their names' do
ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar'])
ModelWithAttributesWithSpaces.alias_attribute(:'foo_bar', :'foo bar')
diff --git a/activemodel/test/cases/configuration_test.rb b/activemodel/test/cases/configuration_test.rb
new file mode 100644
index 0000000000..a172fa26a3
--- /dev/null
+++ b/activemodel/test/cases/configuration_test.rb
@@ -0,0 +1,154 @@
+require 'cases/helper'
+
+class ConfigurationOnModuleTest < ActiveModel::TestCase
+ def setup
+ @mod = mod = Module.new do
+ extend ActiveSupport::Concern
+ extend ActiveModel::Configuration
+
+ config_attribute :omg
+ self.omg = "default"
+
+ config_attribute :wtf, global: true
+ self.wtf = "default"
+
+ config_attribute :boolean
+
+ config_attribute :lol, instance_writer: true
+ end
+
+ @klass = Class.new do
+ include mod
+ end
+
+ @subklass = Class.new(@klass)
+ end
+
+ test "default" do
+ assert_equal "default", @mod.omg
+ assert_equal "default", @klass.omg
+ assert_equal "default", @klass.new.omg
+ end
+
+ test "setting" do
+ @mod.omg = "lol"
+ assert_equal "lol", @mod.omg
+ end
+
+ test "setting on class including the module" do
+ @klass.omg = "lol"
+ assert_equal "lol", @klass.omg
+ assert_equal "lol", @klass.new.omg
+ assert_equal "default", @mod.omg
+ end
+
+ test "setting on subclass of class including the module" do
+ @subklass.omg = "lol"
+ assert_equal "lol", @subklass.omg
+ assert_equal "default", @klass.omg
+ assert_equal "default", @mod.omg
+ end
+
+ test "setting on instance" do
+ assert !@klass.new.respond_to?(:omg=)
+
+ @klass.lol = "lol"
+ obj = @klass.new
+ assert_equal "lol", obj.lol
+ obj.lol = "omg"
+ assert_equal "omg", obj.lol
+ assert_equal "lol", @klass.lol
+ assert_equal "lol", @klass.new.lol
+ obj.lol = false
+ assert !obj.lol?
+ end
+
+ test "global attribute" do
+ assert_equal "default", @mod.wtf
+ assert_equal "default", @klass.wtf
+
+ @mod.wtf = "wtf"
+
+ assert_equal "wtf", @mod.wtf
+ assert_equal "wtf", @klass.wtf
+
+ @klass.wtf = "lol"
+
+ assert_equal "lol", @mod.wtf
+ assert_equal "lol", @klass.wtf
+ end
+
+ test "boolean" do
+ assert_equal false, @mod.boolean?
+ assert_equal false, @klass.new.boolean?
+ @mod.boolean = true
+ assert_equal true, @mod.boolean?
+ assert_equal true, @klass.new.boolean?
+ end
+end
+
+class ConfigurationOnClassTest < ActiveModel::TestCase
+ def setup
+ @klass = Class.new do
+ extend ActiveModel::Configuration
+
+ config_attribute :omg
+ self.omg = "default"
+
+ config_attribute :wtf, global: true
+ self.wtf = "default"
+
+ config_attribute :omg2, instance_writer: true
+ config_attribute :wtf2, instance_writer: true, global: true
+ end
+
+ @subklass = Class.new(@klass)
+ end
+
+ test "defaults" do
+ assert_equal "default", @klass.omg
+ assert_equal "default", @klass.wtf
+ assert_equal "default", @subklass.omg
+ assert_equal "default", @subklass.wtf
+ end
+
+ test "changing" do
+ @klass.omg = "lol"
+ assert_equal "lol", @klass.omg
+ assert_equal "lol", @subklass.omg
+ end
+
+ test "changing in subclass" do
+ @subklass.omg = "lol"
+ assert_equal "lol", @subklass.omg
+ assert_equal "default", @klass.omg
+ end
+
+ test "changing global" do
+ @klass.wtf = "wtf"
+ assert_equal "wtf", @klass.wtf
+ assert_equal "wtf", @subklass.wtf
+
+ @subklass.wtf = "lol"
+ assert_equal "lol", @klass.wtf
+ assert_equal "lol", @subklass.wtf
+ end
+
+ test "instance_writer" do
+ obj = @klass.new
+
+ @klass.omg2 = "omg"
+ @klass.wtf2 = "wtf"
+
+ assert_equal "omg", obj.omg2
+ assert_equal "wtf", obj.wtf2
+
+ obj.omg2 = "lol"
+ obj.wtf2 = "lol"
+
+ assert_equal "lol", obj.omg2
+ assert_equal "lol", obj.wtf2
+ assert_equal "omg", @klass.omg2
+ assert_equal "lol", @klass.wtf2
+ end
+end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 8ddedb160a..7a610e0c2c 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -27,12 +27,27 @@ class ErrorsTest < ActiveModel::TestCase
end
end
+ def test_delete
+ errors = ActiveModel::Errors.new(self)
+ errors[:foo] = 'omg'
+ errors.delete(:foo)
+ assert_empty errors[:foo]
+ end
+
def test_include?
errors = ActiveModel::Errors.new(self)
errors[:foo] = 'omg'
assert errors.include?(:foo), 'errors should include :foo'
end
+ def test_dup
+ errors = ActiveModel::Errors.new(self)
+ errors[:foo] = 'bar'
+ errors_dup = errors.dup
+ errors_dup[:bar] = 'omg'
+ assert_not_same errors_dup.messages, errors.messages
+ end
+
def test_has_key?
errors = ActiveModel::Errors.new(self)
errors[:foo] = 'omg'
@@ -136,10 +151,10 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a
end
- test 'to_hash should return an ordered hash' do
+ test 'to_hash should return a hash' do
person = Person.new
person.errors.add(:name, "can not be blank")
- assert_instance_of ActiveSupport::OrderedHash, person.errors.to_hash
+ assert_instance_of ::Hash, person.errors.to_hash
end
test 'full_messages should return an array of error messages, with the attribute name included' do
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index 2e860272a4..4347b17cbc 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -10,4 +10,4 @@ require 'active_support/core_ext/string/access'
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
-require 'test/unit'
+require 'minitest/autorun'
diff --git a/activemodel/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb
index 68372160cd..8faf93c056 100644
--- a/activemodel/test/cases/lint_test.rb
+++ b/activemodel/test/cases/lint_test.rb
@@ -7,14 +7,10 @@ class LintTest < ActiveModel::TestCase
extend ActiveModel::Naming
include ActiveModel::Conversion
- def valid?() true end
def persisted?() false end
def errors
- obj = Object.new
- def obj.[](key) [] end
- def obj.full_messages() [] end
- obj
+ Hash.new([])
end
end
diff --git a/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb b/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb
index 676937b5e1..3660b9b1e5 100644
--- a/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb
+++ b/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'logger'
+require 'active_support/logger'
require 'active_support/core_ext/object/inclusion'
class SanitizerTest < ActiveModel::TestCase
@@ -28,7 +28,7 @@ class SanitizerTest < ActiveModel::TestCase
test "debug mass assignment removal with LoggerSanitizer" do
original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' }
log = StringIO.new
- self.logger = Logger.new(log)
+ self.logger = ActiveSupport::Logger.new(log)
@logger_sanitizer.sanitize(original_attributes, @authorizer)
assert_match(/admin/, log.string, "Should log removed attributes: #{log.string}")
end
diff --git a/activemodel/test/cases/mass_assignment_security_test.rb b/activemodel/test/cases/mass_assignment_security_test.rb
index be07e59a2f..a197dbe748 100644
--- a/activemodel/test/cases/mass_assignment_security_test.rb
+++ b/activemodel/test/cases/mass_assignment_security_test.rb
@@ -19,6 +19,13 @@ class MassAssignmentSecurityTest < ActiveModel::TestCase
assert_equal expected, sanitized
end
+ def test_attribute_protection_when_role_is_nil
+ user = User.new
+ expected = { "name" => "John Smith", "email" => "john@smith.com" }
+ sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true), nil)
+ assert_equal expected, sanitized
+ end
+
def test_only_moderator_role_attribute_accessible
user = SpecialUser.new
expected = { "name" => "John Smith", "email" => "john@smith.com" }
diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb
index 5f943729dd..1e14d83bcb 100644
--- a/activemodel/test/cases/naming_test.rb
+++ b/activemodel/test/cases/naming_test.rb
@@ -34,6 +34,10 @@ class NamingTest < ActiveModel::TestCase
def test_human
assert_equal 'Track back', @model_name.human
end
+
+ def test_i18n_key
+ assert_equal :"post/track_back", @model_name.i18n_key
+ end
end
class NamingWithNamespacedModelInIsolatedNamespaceTest < ActiveModel::TestCase
@@ -75,8 +79,8 @@ class NamingWithNamespacedModelInIsolatedNamespaceTest < ActiveModel::TestCase
assert_equal 'post', @model_name.param_key
end
- def test_recognizing_namespace
- assert_equal 'Post', Blog::Post.model_name.instance_variable_get("@unnamespaced")
+ def test_i18n_key
+ assert_equal :"blog/post", @model_name.i18n_key
end
end
@@ -118,6 +122,10 @@ class NamingWithNamespacedModelInSharedNamespaceTest < ActiveModel::TestCase
def test_param_key
assert_equal 'blog_post', @model_name.param_key
end
+
+ def test_i18n_key
+ assert_equal :"blog/post", @model_name.i18n_key
+ end
end
class NamingWithSuppliedModelNameTest < ActiveModel::TestCase
@@ -158,15 +166,58 @@ class NamingWithSuppliedModelNameTest < ActiveModel::TestCase
def test_param_key
assert_equal 'article', @model_name.param_key
end
+
+ def test_i18n_key
+ assert_equal :"article", @model_name.i18n_key
+ end
+end
+
+class NamingUsingRelativeModelNameTest < ActiveModel::TestCase
+ def setup
+ @model_name = Blog::Post.model_name
+ end
+
+ def test_singular
+ assert_equal 'blog_post', @model_name.singular
+ end
+
+ def test_plural
+ assert_equal 'blog_posts', @model_name.plural
+ end
+
+ def test_element
+ assert_equal 'post', @model_name.element
+ end
+
+ def test_collection
+ assert_equal 'blog/posts', @model_name.collection
+ end
+
+ def test_human
+ assert_equal 'Post', @model_name.human
+ end
+
+ def test_route_key
+ assert_equal 'posts', @model_name.route_key
+ end
+
+ def test_param_key
+ assert_equal 'post', @model_name.param_key
+ end
+
+ def test_i18n_key
+ assert_equal :"blog/post", @model_name.i18n_key
+ end
end
-class NamingHelpersTest < Test::Unit::TestCase
+class NamingHelpersTest < ActiveModel::TestCase
def setup
@klass = Contact
@record = @klass.new
@singular = 'contact'
@plural = 'contacts'
@uncountable = Sheep
+ @singular_route_key = 'contact'
@route_key = 'contacts'
@param_key = 'contact'
end
@@ -193,10 +244,12 @@ class NamingHelpersTest < Test::Unit::TestCase
def test_route_key
assert_equal @route_key, route_key(@record)
+ assert_equal @singular_route_key, singular_route_key(@record)
end
def test_route_key_for_class
assert_equal @route_key, route_key(@klass)
+ assert_equal @singular_route_key, singular_route_key(@klass)
end
def test_param_key
@@ -212,8 +265,26 @@ class NamingHelpersTest < Test::Unit::TestCase
assert !uncountable?(@klass), "Expected 'contact' to be countable"
end
+ def test_uncountable_route_key
+ assert_equal "sheep", singular_route_key(@uncountable)
+ assert_equal "sheep_index", route_key(@uncountable)
+ end
+
private
def method_missing(method, *args)
ActiveModel::Naming.send(method, *args)
end
end
+
+class NameWithAnonymousClassTest < ActiveModel::TestCase
+ def test_anonymous_class_without_name_argument
+ assert_raises(ArgumentError) do
+ ActiveModel::Name.new(Class.new)
+ end
+ end
+
+ def test_anonymous_class_with_name_argument
+ model_name = ActiveModel::Name.new(Class.new, nil, "Anonymous")
+ assert_equal "Anonymous", model_name
+ end
+end
diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb
index b8dad9d51f..3b201a70f5 100644
--- a/activemodel/test/cases/serialization_test.rb
+++ b/activemodel/test/cases/serialization_test.rb
@@ -43,38 +43,38 @@ class SerializationTest < ActiveModel::TestCase
end
def test_method_serializable_hash_should_work
- expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"}
- assert_equal expected , @user.serializable_hash
+ expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"}
+ assert_equal expected, @user.serializable_hash
end
def test_method_serializable_hash_should_work_with_only_option
- expected = {"name"=>"David"}
- assert_equal expected , @user.serializable_hash(:only => [:name])
+ expected = {"name"=>"David"}
+ assert_equal expected, @user.serializable_hash(:only => [:name])
end
def test_method_serializable_hash_should_work_with_except_option
- expected = {"gender"=>"male", "email"=>"david@example.com"}
- assert_equal expected , @user.serializable_hash(:except => [:name])
+ expected = {"gender"=>"male", "email"=>"david@example.com"}
+ assert_equal expected, @user.serializable_hash(:except => [:name])
end
def test_method_serializable_hash_should_work_with_methods_option
- expected = {"name"=>"David", "gender"=>"male", :foo=>"i_am_foo", "email"=>"david@example.com"}
- assert_equal expected , @user.serializable_hash(:methods => [:foo])
+ expected = {"name"=>"David", "gender"=>"male", "foo"=>"i_am_foo", "email"=>"david@example.com"}
+ assert_equal expected, @user.serializable_hash(:methods => [:foo])
end
def test_method_serializable_hash_should_work_with_only_and_methods
- expected = {:foo=>"i_am_foo"}
- assert_equal expected , @user.serializable_hash(:only => [], :methods => [:foo])
+ expected = {"foo"=>"i_am_foo"}
+ assert_equal expected, @user.serializable_hash(:only => [], :methods => [:foo])
end
def test_method_serializable_hash_should_work_with_except_and_methods
- expected = {"gender"=>"male", :foo=>"i_am_foo"}
- assert_equal expected , @user.serializable_hash(:except => [:name, :email], :methods => [:foo])
+ expected = {"gender"=>"male", "foo"=>"i_am_foo"}
+ assert_equal expected, @user.serializable_hash(:except => [:name, :email], :methods => [:foo])
end
def test_should_not_call_methods_that_dont_respond
- expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"}
- assert_equal expected , @user.serializable_hash(:methods => [:bar])
+ expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"}
+ assert_equal expected, @user.serializable_hash(:methods => [:bar])
end
def test_should_use_read_attribute_for_serialization
@@ -87,65 +87,64 @@ class SerializationTest < ActiveModel::TestCase
end
def test_include_option_with_singular_association
- expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com",
- :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}}
- assert_equal expected , @user.serializable_hash(:include => :address)
+ expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com",
+ :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}}
+ assert_equal expected, @user.serializable_hash(:include => :address)
end
def test_include_option_with_plural_association
- expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
- {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected , @user.serializable_hash(:include => :friends)
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
+ assert_equal expected, @user.serializable_hash(:include => :friends)
end
def test_include_option_with_empty_association
@user.friends = []
- expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]}
- assert_equal expected , @user.serializable_hash(:include => :friends)
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]}
+ assert_equal expected, @user.serializable_hash(:include => :friends)
end
def test_multiple_includes
- expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111},
- :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
- {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected , @user.serializable_hash(:include => [:address, :friends])
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111},
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
+ assert_equal expected, @user.serializable_hash(:include => [:address, :friends])
end
def test_include_with_options
- expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- :address=>{"street"=>"123 Lane"}}
- assert_equal expected , @user.serializable_hash(:include => {:address => {:only => "street"}})
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :address=>{"street"=>"123 Lane"}}
+ assert_equal expected, @user.serializable_hash(:include => {:address => {:only => "street"}})
end
def test_nested_include
@user.friends.first.friends = [@user]
- expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male',
- :friends => [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]},
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male',
+ :friends => [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]},
{"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', :friends => []}]}
- assert_equal expected , @user.serializable_hash(:include => {:friends => {:include => :friends}})
+ assert_equal expected, @user.serializable_hash(:include => {:friends => {:include => :friends}})
end
def test_only_include
expected = {"name"=>"David", :friends => [{"name" => "Joe"}, {"name" => "Sue"}]}
- assert_equal expected , @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}})
+ assert_equal expected, @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}})
end
def test_except_include
expected = {"name"=>"David", "email"=>"david@example.com",
- :friends => [{"name" => 'Joe', "email" => 'joe@example.com'},
- {"name" => "Sue", "email" => 'sue@example.com'}]}
- assert_equal expected , @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}})
+ :friends => [{"name" => 'Joe', "email" => 'joe@example.com'},
+ {"name" => "Sue", "email" => 'sue@example.com'}]}
+ assert_equal expected, @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}})
end
def test_multiple_includes_with_options
- expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- :address=>{"street"=>"123 Lane"},
- :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
- {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected , @user.serializable_hash(:include => [{:address => {:only => "street"}}, :friends])
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :address=>{"street"=>"123 Lane"},
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
+ assert_equal expected, @user.serializable_hash(:include => [{:address => {:only => "street"}}, :friends])
end
-
end
diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb
index a754d610b9..7160635eb4 100644
--- a/activemodel/test/cases/serializers/json_serialization_test.rb
+++ b/activemodel/test/cases/serializers/json_serialization_test.rb
@@ -14,9 +14,11 @@ class Contact
end
end
+ remove_method :attributes if method_defined?(:attributes)
+
def attributes
instance_values
- end unless method_defined?(:attributes)
+ end
end
class JsonSerializationTest < ActiveModel::TestCase
@@ -128,13 +130,13 @@ class JsonSerializationTest < ActiveModel::TestCase
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
end
- test "should return OrderedHash for errors" do
+ test "should return Hash for errors" do
contact = Contact.new
contact.errors.add :name, "can't be blank"
contact.errors.add :name, "is too short (minimum is 2 characters)"
contact.errors.add :age, "must be 16 or over"
- hash = ActiveSupport::OrderedHash.new
+ hash = {}
hash[:name] = ["can't be blank", "is too short (minimum is 2 characters)"]
hash[:age] = ["must be 16 or over"]
assert_equal hash.to_json, contact.errors.to_json
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index fc73d9dcd8..38aecf51ff 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -9,6 +9,8 @@ class Contact
attr_accessor :address, :friends
+ remove_method :attributes if method_defined?(:attributes)
+
def attributes
instance_values.except("address", "friends")
end
diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb
index 1b1d972d5c..54e86d48db 100644
--- a/activemodel/test/cases/translation_test.rb
+++ b/activemodel/test/cases/translation_test.rb
@@ -56,6 +56,16 @@ class ActiveModelI18nTests < ActiveModel::TestCase
assert_equal 'person gender attribute', Person::Gender.human_attribute_name('attribute')
end
+ def test_translated_nested_model_attributes
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:"person/addresses" => {:street => 'Person Address Street'}}}
+ assert_equal 'Person Address Street', Person.human_attribute_name('addresses.street')
+ end
+
+ def test_translated_nested_model_attributes_with_namespace_fallback
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:addresses => {:street => 'Cool Address Street'}}}
+ assert_equal 'Cool Address Street', Person.human_attribute_name('addresses.street')
+ end
+
def test_translated_model_names
I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} }
assert_equal 'person model', Person.model_name.human
diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb
index 1cf09758f9..e4f602bd80 100644
--- a/activemodel/test/cases/validations/callbacks_test.rb
+++ b/activemodel/test/cases/validations/callbacks_test.rb
@@ -5,11 +5,10 @@ class Dog
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
- attr_accessor :name
- attr_writer :history
+ attr_accessor :name, :history
- def history
- @history ||= []
+ def initialize
+ @history = []
end
end
diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb
index 72a383f128..adab8ccb2b 100644
--- a/activemodel/test/cases/validations/exclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/exclusion_validation_test.rb
@@ -46,12 +46,12 @@ class ExclusionValidationTest < ActiveModel::TestCase
def test_validates_exclusion_of_with_lambda
Topic.validates_exclusion_of :title, :in => lambda{ |topic| topic.author_name == "sikachu" ? %w( monkey elephant ) : %w( abe wasabi ) }
- p = Topic.new
- p.title = "elephant"
- p.author_name = "sikachu"
- assert p.invalid?
+ t = Topic.new
+ t.title = "elephant"
+ t.author_name = "sikachu"
+ assert t.invalid?
- p.title = "wasabi"
- assert p.valid?
+ t.title = "wasabi"
+ assert t.valid?
end
end
diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb
index 2ce714fef0..41a1131bcb 100644
--- a/activemodel/test/cases/validations/format_validation_test.rb
+++ b/activemodel/test/cases/validations/format_validation_test.rb
@@ -101,25 +101,25 @@ class PresenceValidationTest < ActiveModel::TestCase
def test_validates_format_of_with_lambda
Topic.validates_format_of :content, :with => lambda{ |topic| topic.title == "digit" ? /\A\d+\Z/ : /\A\S+\Z/ }
- p = Topic.new
- p.title = "digit"
- p.content = "Pixies"
- assert p.invalid?
+ t = Topic.new
+ t.title = "digit"
+ t.content = "Pixies"
+ assert t.invalid?
- p.content = "1234"
- assert p.valid?
+ t.content = "1234"
+ assert t.valid?
end
def test_validates_format_of_without_lambda
Topic.validates_format_of :content, :without => lambda{ |topic| topic.title == "characters" ? /\A\d+\Z/ : /\A\S+\Z/ }
- p = Topic.new
- p.title = "characters"
- p.content = "1234"
- assert p.invalid?
+ t = Topic.new
+ t.title = "characters"
+ t.content = "1234"
+ assert t.invalid?
- p.content = "Pixies"
- assert p.valid?
+ t.content = "Pixies"
+ assert t.valid?
end
def test_validates_format_of_for_ruby_class
diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb
index 413da92de4..851d345eab 100644
--- a/activemodel/test/cases/validations/inclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/inclusion_validation_test.rb
@@ -78,12 +78,12 @@ class InclusionValidationTest < ActiveModel::TestCase
def test_validates_inclusion_of_with_lambda
Topic.validates_inclusion_of :title, :in => lambda{ |topic| topic.author_name == "sikachu" ? %w( monkey elephant ) : %w( abe wasabi ) }
- p = Topic.new
- p.title = "wasabi"
- p.author_name = "sikachu"
- assert p.invalid?
+ t = Topic.new
+ t.title = "wasabi"
+ t.author_name = "sikachu"
+ assert t.invalid?
- p.title = "elephant"
- assert p.valid?
+ t.title = "elephant"
+ assert t.valid?
end
end
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index 44048a9c1d..113bfd6337 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -260,74 +260,64 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_minimum_utf8
- with_kcode('UTF8') do
- Topic.validates_length_of :title, :minimum => 5
+ Topic.validates_length_of :title, :minimum => 5
- t = Topic.new("title" => "一二三四五", "content" => "whatever")
- assert t.valid?
+ t = Topic.new("title" => "一二三四五", "content" => "whatever")
+ assert t.valid?
- t.title = "一二三四"
- assert t.invalid?
- assert t.errors[:title].any?
- assert_equal ["is too short (minimum is 5 characters)"], t.errors["title"]
- end
+ t.title = "一二三四"
+ assert t.invalid?
+ assert t.errors[:title].any?
+ assert_equal ["is too short (minimum is 5 characters)"], t.errors["title"]
end
def test_validates_length_of_using_maximum_utf8
- with_kcode('UTF8') do
- Topic.validates_length_of :title, :maximum => 5
+ Topic.validates_length_of :title, :maximum => 5
- t = Topic.new("title" => "一二三四五", "content" => "whatever")
- assert t.valid?
+ t = Topic.new("title" => "一二三四五", "content" => "whatever")
+ assert t.valid?
- t.title = "一二34五六"
- assert t.invalid?
- assert t.errors[:title].any?
- assert_equal ["is too long (maximum is 5 characters)"], t.errors["title"]
- end
+ t.title = "一二34五六"
+ assert t.invalid?
+ assert t.errors[:title].any?
+ assert_equal ["is too long (maximum is 5 characters)"], t.errors["title"]
end
def test_validates_length_of_using_within_utf8
- with_kcode('UTF8') do
- Topic.validates_length_of(:title, :content, :within => 3..5)
-
- t = Topic.new("title" => "一二", "content" => "12三四五六七")
- assert t.invalid?
- assert_equal ["is too short (minimum is 3 characters)"], t.errors[:title]
- assert_equal ["is too long (maximum is 5 characters)"], t.errors[:content]
- t.title = "一二三"
- t.content = "12三"
- assert t.valid?
- end
+ Topic.validates_length_of(:title, :content, :within => 3..5)
+
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
+ assert t.invalid?
+ assert_equal ["is too short (minimum is 3 characters)"], t.errors[:title]
+ assert_equal ["is too long (maximum is 5 characters)"], t.errors[:content]
+ t.title = "一二三"
+ t.content = "12三"
+ assert t.valid?
end
def test_optionally_validates_length_of_using_within_utf8
- with_kcode('UTF8') do
- Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
+ Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
- t = Topic.new(:title => "一二三四五")
- assert t.valid?, t.errors.inspect
+ t = Topic.new(:title => "一二三四五")
+ assert t.valid?, t.errors.inspect
- t = Topic.new(:title => "一二三")
- assert t.valid?, t.errors.inspect
+ t = Topic.new(:title => "一二三")
+ assert t.valid?, t.errors.inspect
- t.title = nil
- assert t.valid?, t.errors.inspect
- end
+ t.title = nil
+ assert t.valid?, t.errors.inspect
end
def test_validates_length_of_using_is_utf8
- with_kcode('UTF8') do
- Topic.validates_length_of :title, :is => 5
+ Topic.validates_length_of :title, :is => 5
- t = Topic.new("title" => "一二345", "content" => "whatever")
- assert t.valid?
+ t = Topic.new("title" => "一二345", "content" => "whatever")
+ assert t.valid?
- t.title = "一二345六"
- assert t.invalid?
- assert t.errors[:title].any?
- assert_equal ["is the wrong length (should be 5 characters)"], t.errors["title"]
- end
+ t.title = "一二345六"
+ assert t.invalid?
+ assert t.errors[:title].any?
+ assert_equal ["is the wrong length (should be 5 characters)"], t.errors["title"]
end
def test_validates_length_of_with_block
@@ -367,4 +357,22 @@ class LengthValidationTest < ActiveModel::TestCase
ensure
Person.reset_callbacks(:validate)
end
+
+ def test_validates_length_of_for_infinite_maxima
+ Topic.validates_length_of(:title, :within => 5..Float::INFINITY)
+
+ t = Topic.new("title" => "1234")
+ assert t.invalid?
+ assert t.errors[:title].any?
+
+ t.title = "12345"
+ assert t.valid?
+
+ Topic.validates_length_of(:author_name, :maximum => Float::INFINITY)
+
+ assert t.valid?
+
+ t.author_name = "A very long author name that should still be valid." * 100
+ assert t.valid?
+ end
end
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index 08f6169ca5..6742a4bab0 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -106,6 +106,13 @@ class NumericalityValidationTest < ActiveModel::TestCase
valid!([2])
end
+ def test_validates_numericality_with_other_than
+ Topic.validates_numericality_of :approved, :other_than => 0
+
+ invalid!([0, 0.0])
+ valid!([-1, 42])
+ end
+
def test_validates_numericality_with_proc
Topic.send(:define_method, :min_approved, lambda { 5 })
Topic.validates_numericality_of :approved, :greater_than_or_equal_to => Proc.new {|topic| topic.min_approved }
diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb
index 779f6c8448..575154ffbd 100644
--- a/activemodel/test/cases/validations/validates_test.rb
+++ b/activemodel/test/cases/validations/validates_test.rb
@@ -16,6 +16,12 @@ class ValidatesTest < ActiveModel::TestCase
PersonWithValidator.reset_callbacks(:validate)
end
+ def test_validates_with_messages_empty
+ Person.validates :title, :presence => {:message => "" }
+ person = Person.new
+ assert !person.valid?, 'person should not be valid.'
+ end
+
def test_validates_with_built_in_validation
Person.validates :title, :numericality => true
person = Person.new
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 2f4376bd41..a716d0896e 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -58,8 +58,7 @@ class ValidationsTest < ActiveModel::TestCase
r = Reply.new
r.valid?
- errors = []
- r.errors.each {|attr, messages| errors << [attr.to_s, messages] }
+ errors = r.errors.collect {|attr, messages| [attr.to_s, messages]}
assert errors.include?(["title", "is Empty"])
assert errors.include?(["content", "is Empty"])
@@ -181,7 +180,7 @@ class ValidationsTest < ActiveModel::TestCase
assert_match %r{<error>Title can't be blank</error>}, xml
assert_match %r{<error>Content can't be blank</error>}, xml
- hash = ActiveSupport::OrderedHash.new
+ hash = {}
hash[:title] = ["can't be blank"]
hash[:content] = ["can't be blank"]
assert_equal t.errors.to_json, hash.to_json
@@ -311,7 +310,7 @@ class ValidationsTest < ActiveModel::TestCase
end
def test_strict_validation_particular_validator
- Topic.validates :title, :presence => {:strict => true}
+ Topic.validates :title, :presence => { :strict => true }
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
@@ -330,4 +329,19 @@ class ValidationsTest < ActiveModel::TestCase
Topic.new.valid?
end
end
+
+ def test_strict_validation_error_message
+ Topic.validates :title, :strict => true, :presence => true
+
+ exception = assert_raises(ActiveModel::StrictValidationFailed) do
+ Topic.new.valid?
+ end
+ assert_equal "Title can't be blank", exception.message
+ end
+
+ def test_does_not_modify_options_argument
+ options = { :presence => true }
+ Topic.validates :title, options
+ assert_equal({ :presence => true }, options)
+ end
end
diff --git a/activemodel/test/models/blog_post.rb b/activemodel/test/models/blog_post.rb
index d289177259..46eba857df 100644
--- a/activemodel/test/models/blog_post.rb
+++ b/activemodel/test/models/blog_post.rb
@@ -1,10 +1,6 @@
module Blog
- def self._railtie
- Object.new
- end
-
- def self.table_name_prefix
- "blog_"
+ def self.use_relative_model_naming?
+ true
end
class Post
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
deleted file mode 100644
index 798d875034..0000000000
--- a/activerecord/CHANGELOG
+++ /dev/null
@@ -1,6572 +0,0 @@
-*Rails 3.2.0 (unreleased)*
-
-* In development mode the db:drop task also drops the test database. For symmetry with
- the db:create task. [Dmitriy Kiriyenko]
-
-* Added ActiveRecord::Base.store for declaring simple single-column key/value stores [DHH]
-
- class User < ActiveRecord::Base
- store :settings, accessors: [ :color, :homepage ]
- end
-
- u = User.new(color: 'black', homepage: '37signals.com')
- u.color # Accessor stored attribute
- u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
-
-
-* MySQL: case-insensitive uniqueness validation avoids calling LOWER when
- the column already uses a case-insensitive collation. Fixes #561.
-
- [Joseph Palermo]
-
-* Transactional fixtures enlist all active database connections. You can test
- models on different connections without disabling transactional fixtures.
-
- [Jeremy Kemper]
-
-* Add first_or_create, first_or_create!, first_or_initialize methods to Active Record. This is a
- better approach over the old find_or_create_by dynamic methods because it's clearer which
- arguments are used to find the record and which are used to create it:
-
- User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson")
-
- [Andrés Mejía]
-
-* Fix nested attributes bug where _destroy parameter is taken into account
- during :reject_if => :all_blank (fixes #2937)
-
- [Aaron Christy]
-
-*Rails 3.1.1 (October 7, 2011)*
-
-* Add deprecation for the preload_associations method. Fixes #3022.
-
- [Jon Leighton]
-
-* Don't require a DB connection when loading a model that uses set_primary_key. GH #2807.
-
- [Jon Leighton]
-
-* Fix using select() with a habtm association, e.g. Person.friends.select(:name). GH #3030 and
- #2923.
-
- [Hendy Tanata]
-
-* Fix belongs_to polymorphic with custom primary key on target. GH #3104.
-
- [Jon Leighton]
-
-* CollectionProxy#replace should change the DB records rather than just mutating the array.
- Fixes #3020.
-
- [Jon Leighton]
-
-* LRU cache in mysql and sqlite are now per-process caches.
-
- * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache
- keys are per process id.
- * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto
-
- [Aaron Patterson]
-
-* Support bulk change_table in mysql2 adapter, as well as the mysql one. [Jon Leighton]
-
-* If multiple parameters are sent representing a date, and some are blank, the
-resulting object is nil. In previous releases those values defaulted to 1. This
-only affects existing but blank parameters, missing ones still raise an error.
-[Akira Matsuda]
-
-* ActiveRecord::Base.establish_connection now takes a string that contains
-a URI that specifies the connection configuration. For example:
-
- ActiveRecord::Base.establish_connection 'postgres://localhost/foo'
-
-* Active Record's dynamic finder will now raise the error if you passing in less number of arguments than what you call in method signature.
-
- So if you were doing this and expecting the second argument to be nil:
-
- User.find_by_username_and_group("sikachu")
-
- You'll now get `ArgumentError: wrong number of arguments (1 for 2).` You'll then have to do this:
-
- User.find_by_username_and_group("sikachu", nil)
-
- [Prem Sichanugrist]
-
-
-*Rails 3.1.0 (August 30, 2011)*
-
-* Add a proxy_association method to association proxies, which can be called by association
- extensions to access information about the association. This replaces proxy_owner etc with
- proxy_association.owner.
-
- [Jon Leighton]
-
-* ActiveRecord::MacroReflection::AssociationReflection#build_record has a new method signature.
-
- Before: def build_association(*options)
- After: def build_association(*options, &block)
-
- Users who are redefining this method to extend functionality should ensure that the block is
- passed through to ActiveRecord::Base#new.
-
- This change is necessary to fix https://github.com/rails/rails/issues/1842.
-
- [Jon Leighton]
-
-* AR#pluralize_table_names can be used to singularize/pluralize table name of an individual model:
-
- class User < ActiveRecord::Base
- self.pluralize_table_names = false
- end
-
- Previously this could only be set globally for all models through ActiveRecord::Base.pluralize_table_names. [Guillermo Iguaran]
-
-* Add block setting of attributes to singular associations:
-
- class User < ActiveRecord::Base
- has_one :account
- end
-
- user.build_account{ |a| a.credit_limit => 100.0 }
-
- The block is called after the instance has been initialized. [Andrew White]
-
-* Add ActiveRecord::Base.attribute_names to return a list of attribute names. This will return an empty array if the model is abstract or table does not exists. [Prem Sichanugrist]
-
-* CSV Fixtures are deprecated and support will be removed in Rails 3.2.0
-
-* AR#new, AR#create, AR#create!, AR#update_attributes and AR#update_attributes! all accept a second hash as option that allows you
- to specify which role to consider when assigning attributes. This is built on top of ActiveModel's
- new mass assignment capabilities:
-
- class Post < ActiveRecord::Base
- attr_accessible :title
- attr_accessible :title, :published_at, :as => :admin
- end
-
- Post.new(params[:post], :as => :admin)
-
- assign_attributes() with similar API was also added and attributes=(params, guard) was deprecated.
-
- Please note that this changes the method signatures for AR#new, AR#create, AR#create!, AR#update_attributes and AR#update_attributes!. If you have overwritten these methods you should update them accordingly.
-
- [Josh Kalderimis]
-
-* default_scope can take a block, lambda, or any other object which responds to `call` for lazy
- evaluation:
-
- default_scope { ... }
- default_scope lambda { ... }
- default_scope method(:foo)
-
- This feature was originally implemented by Tim Morgan, but was then removed in favour of
- defining a 'default_scope' class method, but has now been added back in by Jon Leighton.
- The relevant lighthouse ticket is #1812.
-
-* Default scopes are now evaluated at the latest possible moment, to avoid problems where
- scopes would be created which would implicitly contain the default scope, which would then
- be impossible to get rid of via Model.unscoped.
-
- Note that this means that if you are inspecting the internal structure of an
- ActiveRecord::Relation, it will *not* contain the default scope, though the resulting
- query will do. You can get a relation containing the default scope by calling
- ActiveRecord#with_default_scope, though this is not part of the public API.
-
- [Jon Leighton]
-
-* If you wish to merge default scopes in special ways, it is recommended to define your default
- scope as a class method and use the standard techniques for sharing code (inheritance, mixins,
- etc.):
-
- class Post < ActiveRecord::Base
- def self.default_scope
- where(:published => true).where(:hidden => false)
- end
- end
-
- [Jon Leighton]
-
-* PostgreSQL adapter only supports PostgreSQL version 8.2 and higher.
-
-* ConnectionManagement middleware is changed to clean up the connection pool
- after the rack body has been flushed.
-
-* Added an update_column method on ActiveRecord. This new method updates a given attribute on an object, skipping validations and callbacks.
- It is recommended to use #update_attribute unless you are sure you do not want to execute any callback, including the modification of
- the updated_at column. It should not be called on new records.
- Example:
-
- User.first.update_column(:name, "sebastian") # => true
-
- [Sebastian Martinez]
-
-* Associations with a :through option can now use *any* association as the
- through or source association, including other associations which have a
- :through option and has_and_belongs_to_many associations
-
- [Jon Leighton]
-
-* The configuration for the current database connection is now accessible via
- ActiveRecord::Base.connection_config. [fxn]
-
-* limits and offsets are removed from COUNT queries unless both are supplied.
- For example:
-
- People.limit(1).count # => 'SELECT COUNT(*) FROM people'
- People.offset(1).count # => 'SELECT COUNT(*) FROM people'
- People.limit(1).offset(1).count # => 'SELECT COUNT(*) FROM people LIMIT 1 OFFSET 1'
-
- [lighthouse #6262]
-
-* ActiveRecord::Associations::AssociationProxy has been split. There is now an Association class
- (and subclasses) which are responsible for operating on associations, and then a separate,
- thin wrapper called CollectionProxy, which proxies collection associations.
-
- This prevents namespace pollution, separates concerns, and will allow further refactorings.
-
- Singular associations (has_one, belongs_to) no longer have a proxy at all. They simply return
- the associated record or nil. This means that you should not use undocumented methods such
- as bob.mother.create - use bob.create_mother instead.
-
- [Jon Leighton]
-
-* Make has_many :through associations work correctly when you build a record and then save it. This
- requires you to set the :inverse_of option on the source reflection on the join model, like so:
-
- class Post < ActiveRecord::Base
- has_many :taggings
- has_many :tags, :through => :taggings
- end
-
- class Tagging < ActiveRecord::Base
- belongs_to :post
- belongs_to :tag, :inverse_of => :tagging # :inverse_of must be set!
- end
-
- class Tag < ActiveRecord::Base
- has_many :taggings
- has_many :posts, :through => :taggings
- end
-
- post = Post.first
- tag = post.tags.build :name => "ruby"
- tag.save # will save a Taggable linking to the post
-
- [Jon Leighton]
-
-* Support the :dependent option on has_many :through associations. For historical and practical
- reasons, :delete_all is the default deletion strategy employed by association.delete(*records),
- despite the fact that the default strategy is :nullify for regular has_many. Also, this only
- works at all if the source reflection is a belongs_to. For other situations, you should directly
- modify the through association.
-
- [Jon Leighton]
-
-* Changed the behaviour of association.destroy for has_and_belongs_to_many and has_many :through.
- From now on, 'destroy' or 'delete' on an association will be taken to mean 'get rid of the link',
- not (necessarily) 'get rid of the associated records'.
-
- Previously, has_and_belongs_to_many.destroy(*records) would destroy the records themselves. It
- would not delete any records in the join table. Now, it deletes the records in the join table.
-
- Previously, has_many_through.destroy(*records) would destroy the records themselves, and the
- records in the join table. [Note: This has not always been the case; previous version of Rails
- only deleted the records themselves.] Now, it destroys only the records in the join table.
-
- Note that this change is backwards-incompatible to an extent, but there is unfortunately no
- way to 'deprecate' it before changing it. The change is being made in order to have
- consistency as to the meaning of 'destroy' or 'delete' across the different types of associations.
-
- If you wish to destroy the records themselves, you can do records.association.each(&:destroy)
-
- [Jon Leighton]
-
-* Add :bulk => true option to change_table to make all the schema changes defined in change_table block using a single ALTER statement. [Pratik Naik]
-
- Example:
-
- change_table(:users, :bulk => true) do |t|
- t.string :company_name
- t.change :birthdate, :datetime
- end
-
- This will now result in:
-
- ALTER TABLE `users` ADD COLUMN `company_name` varchar(255), CHANGE `updated_at` `updated_at` datetime DEFAULT NULL
-
-* Removed support for accessing attributes on a has_and_belongs_to_many join table. This has been
- documented as deprecated behaviour since April 2006. Please use has_many :through instead.
- [Jon Leighton]
-
-* Added a create_association! method for has_one and belongs_to associations. [Jon Leighton]
-
-* Migration files generated from model and constructive migration generators
- (for example, add_name_to_users) use the reversible migration's `change`
- method instead of the ordinary `up` and `down` methods. [Prem Sichanugrist]
-
-* Removed support for interpolating string SQL conditions on associations. Instead, you should
- use a proc, like so:
-
- Before:
-
- has_many :things, :conditions => 'foo = #{bar}'
-
- After:
-
- has_many :things, :conditions => proc { "foo = #{bar}" }
-
- Inside the proc, 'self' is the object which is the owner of the association, unless you are
- eager loading the association, in which case 'self' is the class which the association is within.
-
- You can have any "normal" conditions inside the proc, so the following will work too:
-
- has_many :things, :conditions => proc { ["foo = ?", bar] }
-
- Previously :insert_sql and :delete_sql on has_and_belongs_to_many association allowed you to call
- 'record' to get the record being inserted or deleted. This is now passed as an argument to
- the proc.
-
-* Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH]. Example:
-
- # Schema: User(name:string, password_digest:string, password_salt:string)
- class User < ActiveRecord::Base
- has_secure_password
- end
-
- user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch")
- user.save # => false, password required
- user.password = "mUc3m00RsqyRe"
- user.save # => false, confirmation doesn't match
- user.password_confirmation = "mUc3m00RsqyRe"
- user.save # => true
- user.authenticate("notright") # => false
- user.authenticate("mUc3m00RsqyRe") # => user
- User.find_by_name("david").try(:authenticate, "notright") # => nil
- User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
-
-
-* When a model is generated add_index is added by default for belongs_to or references columns
-
- rails g model post user:belongs_to will generate the following:
-
- class CreatePosts < ActiveRecord::Migration
- def up
- create_table :posts do |t|
- t.belongs_to :user
- t.timestamps
- end
-
- add_index :posts, :user_id
- end
-
- def down
- drop_table :posts
- end
- end
-
- [Santiago Pastorino]
-
-* Setting the id of a belongs_to object will update the reference to the
-object. [#2989 state:resolved]
-
-* ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed
-to closer match normal Ruby dup and clone semantics.
-
-* Calling ActiveRecord::Base#clone will result in a shallow copy of the record,
-including copying the frozen state. No callbacks will be called.
-
-* Calling ActiveRecord::Base#dup will duplicate the record, including calling
-after initialize hooks. Frozen state will not be copied, and all associations
-will be cleared. A duped record will return true for new_record?, have a nil
-id field, and is saveable.
-
-* Migrations can be defined as reversible, meaning that the migration system
-will figure out how to reverse your migration. To use reversible migrations,
-just define the "change" method. For example:
-
- class MyMigration < ActiveRecord::Migration
- def change
- create_table(:horses) do
- t.column :content, :text
- t.column :remind_at, :datetime
- end
- end
- end
-
-Some things cannot be automatically reversed for you. If you know how to
-reverse those things, you should define 'up' and 'down' in your migration. If
-you define something in `change` that cannot be reversed, an
-IrreversibleMigration exception will be raised when going down.
-
-* Migrations should use instance methods rather than class methods:
- class FooMigration < ActiveRecord::Migration
- def up
- ...
- end
- end
-
- [Aaron Patterson]
-
-* has_one maintains the association with separate after_create/after_update instead
- of a single after_save. [fxn]
-
-* The following code:
-
- Model.limit(10).scoping { Model.count }
-
- now generates the following SQL:
-
- SELECT COUNT(*) FROM models LIMIT 10
-
- This may not return what you want. Instead, you may with to do something
- like this:
-
- Model.limit(10).scoping { Model.all.size }
-
- [Aaron Patterson]
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-* Destroying records via nested attributes works independent of reject_if LH #6006 [Durran Jordan]
-
-* Delegate any? and many? to Model.scoped for consistency [Andrew White]
-
-* Quote the ORDER BY clause in batched finds - fixes #6620 [Andrew White]
-
-* Change exists? so records are not instantiated - fixes #6127. This prevents after_find
- and after_initialize callbacks being triggered when checking for record existence.
- [Andrew White]
-
-* Fix performance bug with attribute accessors which only occurred on Ruby 1.8.7, and ensure we
- cache type-casted values when the column returned from the db contains non-standard chars.
- [Jon Leighton]
-
-* Fix a performance regression introduced here 86acbf1cc050c8fa8c74a10c735e467fb6fd7df8
- related to read_attribute method [Stian Grytøyr]
-
-
-*Rails 3.0.6 (April 5, 2011)*
-
-* Un-deprecate reorder method [Sebastian Martinez]
-
-* Extensions are applied when calling +except+ or +only+ on relations.
- Thanks to Iain Hecker.
-
-* Schemas set in set_table_name are respected by the mysql adapter. LH #5322
-
-* Fixed a bug when empty? was called on a grouped Relation that wasn't loaded.
- LH #5829
-
-* Reapply extensions when using except and only. Thanks Iain Hecker.
-
-* Binary data is escaped when being inserted to SQLite3 Databases. Thanks
- Naruse!
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* Model.where(:column => 1).where(:column => 2) will always produce an AND
-query.
-
- [Aaron Patterson]
-
-* Deprecated support for interpolated association conditions in the form of :conditions => 'foo = #{bar}'.
-
- Instead, you should use a proc, like so:
-
- Before:
-
- has_many :things, :conditions => 'foo = #{bar}'
-
- After:
-
- has_many :things, :conditions => proc { "foo = #{bar}" }
-
- Inside the proc, 'self' is the object which is the owner of the association, unless you are
- eager loading the association, in which case 'self' is the class which the association is within.
-
- You can have any "normal" conditions inside the proc, so the following will work too:
-
- has_many :things, :conditions => proc { ["foo = ?", bar] }
-
- Previously :insert_sql and :delete_sql on has_and_belongs_to_many association allowed you to call
- 'record' to get the record being inserted or deleted. This is now passed as an argument to
- the proc.
-
- [Jon Leighton]
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* Added deprecation warning for has_and_belongs_to_many associations where the join table has
- additional attributes other than the keys. Access to these attributes is removed in 3.1.
- Please use has_many :through instead. [Jon Leighton]
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* Support find by class like this: Post.where(:name => Post)
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* Dramatic speed increase (see: http://engineering.attinteractive.com/2010/10/arel-two-point-ohhhhh-yaaaaaa/) [Aaron Patterson]
-
-* reorder is deprecated in favor of except(:order).order(...) [Santiago Pastorino]
-
-* except is now AR public API
-
- Model.order('name').except(:order).order('salary')
-
- generates:
-
- SELECT * FROM models ORDER BY salary
-
- [Santiago Pastorino]
-
-* The following code:
-
- Model.limit(10).scoping { Model.count }
-
- now generates the following SQL:
-
- SELECT COUNT(*) FROM models LIMIT 10
-
- This may not return what you want. Instead, you may with to do something
- like this:
-
- Model.limit(10).scoping { Model.all.size }
-
- [Aaron Patterson]
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* Introduce a fix for CVE-2010-3993
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* Changed update_attribute to not run callbacks and update the record directly in the database [Neeraj Singh]
-
-* Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope [José Valim]
-
-* New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund]
-
-* select and order for ActiveRecord now always concatenate nested calls. Use reorder if you want the original order to be overwritten [Santiago Pastorino]
-
-* PostgreSQL: ensure the database time zone matches Ruby's time zone #4895 [Aaron Patterson]
-
-* Fixed that ActiveRecord::Base.compute_type would swallow NoMethodError #4751 [Andrew Bloomgarden, Andrew White]
-
-* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik]
-
- Example:
-
- add_index(:accounts, :name, :name => 'by_name', :length => 10)
- => CREATE INDEX by_name ON accounts(name(10))
-
- add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
- => CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
-
-* find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune]
-
-* New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 [Brian Durand]
-
-* Serialized attributes are not converted to YAML if they are any of the formats that can be serialized to XML (like Hash, Array and Strings). [José Valim]
-
-* Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne]
-
-* PostgreSQL: drop support for old postgres driver. Use pg 0.9.0 or later. [Jeremy Kemper]
-
-* Observers can prevent records from saving by returning false, just like before_save and friends. #4087 [Mislav Marohnić]
-
-* Add Relation extensions. [Pratik Naik]
-
- users = User.where(:admin => true).extending(User::AdminPowers)
-
- latest_users = User.order('created_at DESC') do
- def posts_count
- Post.count(:user_id => to_a.map(&:id))
- end
- end
-
-* To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White]
-
-* Silenced "SHOW FIELDS" and "SET SQL_AUTO_IS_NULL=0" statements from the MySQL driver to improve log signal to noise ration in development [DHH]
-
-* PostgreSQLAdapter: set time_zone to UTC when Base.default_timezone == :utc so that Postgres doesn't incorrectly offset-adjust values inserted into TIMESTAMP WITH TIME ZONE columns. #3777 [Jack Christensen]
-
-* Allow relations to be used as scope.
-
- class Item
- scope :red, where(:colour => 'red')
- end
-
- Item.red.limit(10) # Ten red items
-
-* Rename named_scope to scope. [Pratik Naik]
-
-* Changed ActiveRecord::Base.store_full_sti_class to be true by default reflecting the previously announced Rails 3 default [DHH]
-
-* Add Relation#except. [Pratik Naik]
-
- one_red_item = Item.where(:colour => 'red').limit(1)
- all_items = one_red_item.except(:where, :limit)
-
-* Add Relation#delete_all. [Pratik Naik]
-
- Item.where(:colour => 'red').delete_all
-
-* Add Model.having and Relation#having. [Pratik Naik]
-
- Developer.group("salary").having("sum(salary) > 10000").select("salary")
-
-* Add Relation#count. [Pratik Naik]
-
- legends = People.where("age > 100")
- legends.count
- legends.count(:age, :distinct => true)
- legends.select('id').count
-
-* Add Model.readonly and association_collection#readonly finder method. [Pratik Naik]
-
- Post.readonly.to_a # Load all posts in readonly mode
- @user.items.readonly(false).to_a # Load all the user items in writable mode
-
-* Add .lock finder method [Pratik Naik]
-
- User.lock.where(:name => 'lifo').to_a
-
- old_items = Item.where("age > 100")
- old_items.lock.each {|i| .. }
-
-* Add Model.from and association_collection#from finder methods [Pratik Naik]
-
- user = User.scoped
- user.select('*').from('users, items')
-
-* Add relation.destroy_all [Pratik Naik]
-
- old_items = Item.where("age > 100")
- old_items.destroy_all
-
-* Add relation.exists? [Pratik Naik]
-
- red_items = Item.where(:colours => 'red')
- red_items.exists?
- red_items.exists?(1)
-
-* Add find(ids) to relations. [Pratik Naik]
-
- old_users = User.order("age DESC")
- old_users.find(1)
- old_users.find(1, 2, 3)
-
-* Add new finder methods to association collection. [Pratik Naik]
-
- class User < ActiveRecord::Base
- has_many :items
- end
-
- user = User.first
- user.items.where(:items => {:colour => 'red'})
- user.items.select('items.id')
-
-* Add relation.reload to force reloading the records. [Pratik Naik]
-
- topics = Topic.scoped
- topics.to_a # force load
- topics.first # returns a cached record
- topics.reload
- topics.first # Fetches a new record from the database
-
-* Rename Model.conditions and relation.conditions to .where. [Pratik Naik]
-
- Before :
- User.conditions(:name => 'lifo')
- User.select('id').conditions(["age > ?", 21])
-
- Now :
- User.where(:name => 'lifo')
- User.select('id').where(["age > ?", 21])
-
-* Add Model.select/group/order/limit/joins/conditions/preload/eager_load class methods returning a lazy relation. [Pratik Naik]
-
- Examples :
-
- posts = Post.select('id).order('name') # Returns a lazy relation
- posts.each {|p| puts p.id } # Fires "select id from posts order by name"
-
-* Model.scoped now returns a relation if invoked without any arguments. [Pratik Naik]
-
- Example :
-
- posts = Post.scoped
- posts.size # Fires "select count(*) from posts" and returns the count
- posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
-
-* Association inverses for belongs_to, has_one, and has_many. Optimization to reduce database queries. #3533 [Murray Steele]
-
- # post.comments sets each comment's post without needing to :include
- class Post < ActiveRecord::Base
- has_many :comments, :inverse_of => :post
- end
-
-* MySQL: add_ and change_column support positioning. #3286 [Ben Marini]
-
-* Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen, Gabe da Silveira]
-
-* Remove support for SQLite 2. Please upgrade to SQLite 3+ or install the plugin from git://github.com/rails/sqlite2_adapter.git [Pratik Naik]
-
-* PostgreSQL: XML datatype support. #1874 [Leonardo Borges]
-
-* quoted_date converts time-like objects to ActiveRecord::Base.default_timezone before serialization. This allows you to use Time.now in find conditions and have it correctly be serialized as the current time in UTC when default_timezone == :utc. #2946 [Geoff Buesing]
-
-* SQLite: drop support for 'dbfile' option in favor of 'database.' #2363 [Paul Hinze, Jeremy Kemper]
-
-* Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha]
- # employees.company_name references companies.name
- Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name'
-
-* 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]
-
-
-*2.3.2 [Final] (March 15, 2009)*
-
-* Added ActiveRecord::Base.find_each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
-
-* Added that ActiveRecord::Base.exists? can be called with no arguments #1817 [Scott Taylor]
-
-* Add Support for updating deeply nested models from a single form. #1202 [Eloy Duran]
-
- class Book < ActiveRecord::Base
- has_one :author
- has_many :pages
-
- accepts_nested_attributes_for :author, :pages
- end
-
-* Make after_save callbacks fire only if the record was successfully saved. #1735 [Michael Lovitt]
-
- Previously the callbacks would fire if a before_save cancelled saving.
-
-* Support nested transactions using database savepoints. #383 [Jonathan Viney, Hongli Lai]
-
-* Added dynamic scopes ala dynamic finders #1648 [Yaroslav Markin]
-
-* Fixed that ActiveRecord::Base#new_record? should return false (not nil) for existing records #1219 [Yaroslav Markin]
-
-* I18n the word separator for error messages. Introduces the activerecord.errors.format.separator translation key. #1294 [Akira Matsuda]
-
-* Add :having as a key to find and the relevant associations. [Emilio Tagua]
-
-* Added default_scope to Base #1381 [Paweł Kondzior]. Example:
-
- class Person < ActiveRecord::Base
- default_scope :order => 'last_name, first_name'
- end
-
- class Company < ActiveRecord::Base
- has_many :people
- end
-
- Person.all # => Person.find(:all, :order => 'last_name, first_name')
- Company.find(1).people # => Person.find(:all, :order => 'last_name, first_name', :conditions => { :company_id => 1 })
-
-
-*2.2.1 [RC2] (November 14th, 2008)*
-
-* Ensure indices don't flip order in schema.rb #1266 [Jordi Bunster]
-
-* Fixed that serialized strings should never be type-casted (i.e. turning "Yes" to a boolean) #857 [Andreas Korth]
-
-
-*2.2.0 [RC1] (October 24th, 2008)*
-
-* Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]
-
-* Add Model#delete instance method, similar to Model.delete class method. #1086 [Hongli Lai (Phusion)]
-
-* MySQL: cope with quirky default values for not-null text columns. #1043 [Frederick Cheung]
-
-* Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] [Geoff Buesing]
-
-* Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] [Emilio Tagua]
-
-* Added find_last_by dynamic finder #762 [Emilio Tagua]
-
-* Internal API: configurable association options and build_association method for reflections so plugins may extend and override. #985 [Hongli Lai (Phusion)]
-
-* Changed benchmarks to be reported in milliseconds [David Heinemeier Hansson]
-
-* Connection pooling. #936 [Nick Sieger]
-
-* Merge scoped :joins together instead of overwriting them. May expose scoping bugs in your code! #501 [Andrew White]
-
-* before_save, before_validation and before_destroy callbacks that return false will now ROLLBACK the transaction. Previously this would have been committed before the processing was aborted. #891 [Xavier Noria]
-
-* Transactional migrations for databases which support them. #834 [divoxx, Adam Wiggins, Tarmo Tänav]
-
-* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
-
-* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
-
-* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
-
-* Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David Lowenfels] Example :
-
- # Ensure essay contains at least 100 words.
- validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
-
-* Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example:
-
- User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } }
- Item.first :conditions => { :items => { :color => 'red' } }
-
-* Always treat integer :limit as byte length. #420 [Tarmo Tänav]
-
-* Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison]
-
-* Fix column collision with named_scope and :joins. #46 [Duncan Beevers, Mark Catley]
-
-* db:migrate:down and :up update schema_migrations. #369 [Michael Raidel, RaceCondition]
-
-* PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a bind variable. [Tarmo Tänav]
-
-* MySQL: rename_column preserves column defaults. #466 [Diego Algorta]
-
-* Add :from option to calculations. #397 [Ben Munat]
-
-* Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. [Jan De Poorter]
-
-* PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later. [Jeremy Kemper]
-
-* Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess]
-
-
-*2.1.0 (May 31st, 2008)*
-
-* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [Rick Olson]
-
-* Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
-
-* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess]
-
-* Added first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
-
-* Ensure hm:t preloading honours reflection options. Resolves #137. [Frederick Cheung]
-
-* Added protection against duplicate migration names (Aslak Hellesøy) [#112]
-
-* Base#instantiate_time_object: eliminate check for Time.zone, since we can assume this is set if time_zone_aware_attributes is set to true [Geoff Buesing]
-
-* Time zone aware attribute methods use Time.zone.parse instead of #to_time for String arguments, so that offset information in String is respected. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
-
-* Added change_table for migrations (Jeff Dean) [#71]. Example:
-
- change_table :videos do |t|
- t.timestamps # adds created_at, updated_at
- t.belongs_to :goat # adds goat_id integer
- t.string :name, :email, :limit => 20 # adds name and email both with a 20 char limit
- t.remove :name, :email # removes the name and email columns
- end
-
-* Fixed has_many :through .create with no parameters caused a "can't dup NilClass" error (Steven Soroka) [#85]
-
-* Added block-setting of attributes for Base.create like Base.new already has (Adam Meehan) [#39]
-
-* Fixed that pessimistic locking you reference the quoted table name (Josh Susser) [#67]
-
-* Fixed that change_column should be able to use :null => true on a field that formerly had false [Nate Wiger] [#26]
-
-* Added that the MySQL adapter should map integer to either smallint, int, or bigint depending on the :limit just like PostgreSQL [David Heinemeier Hansson]
-
-* Change validates_uniqueness_of :case_sensitive option default back to true (from [9160]). Love your database columns, don't LOWER them. [Rick Olson]
-
-* Add support for interleaving migrations by storing which migrations have run in the new schema_migrations table. Closes #11493 [Jordi Bunster]
-
-* ActiveRecord::Base#sum defaults to 0 if no rows are returned. Closes #11550 [Kamal Fariz Mahyuddin]
-
-* Ensure that respond_to? considers dynamic finder methods. Closes #11538. [James Mead]
-
-* Ensure that save on parent object fails for invalid has_one association. Closes #10518. [Pratik Naik]
-
-* Remove duplicate code from associations. [Pratik Naik]
-
-* Refactor HasManyThroughAssociation to inherit from HasManyAssociation. Association callbacks and <association>_ids= now work with hm:t. #11516 [Ruy Asan]
-
-* Ensure HABTM#create and HABTM#build do not load entire association. [Pratik Naik]
-
-* Improve documentation. [Xavier Noria, Jack Danger Canty, leethal]
-
-* Tweak ActiveRecord::Base#to_json to include a root value in the returned hash: {"post": {"title": ...}} [Rick Olson]
-
- Post.find(1).to_json # => {"title": ...}
- config.active_record.include_root_in_json = true
- Post.find(1).to_json # => {"post": {"title": ...}}
-
-* Add efficient #include? to AssociationCollection (for has_many/has_many :through/habtm). [stopdropandrew]
-
-* PostgreSQL: create_ and drop_database support. #9042 [ez, pedz, Nick Sieger]
-
-* Ensure that validates_uniqueness_of works with with_scope. Closes #9235. [Nik Wakelin, cavalle]
-
-* Partial updates include only unsaved attributes. Off by default; set YourClass.partial_updates = true to enable. [Jeremy Kemper]
-
-* Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled [Geoff Buesing]
-
-* Fixed that validates_size_of :within works in associations #11295, #10019 [cavalle]
-
-* Track changes to unsaved attributes. [Jeremy Kemper]
-
-* Switched to UTC-timebased version numbers for migrations and the schema. This will as good as eliminate the problem of multiple migrations getting the same version assigned in different branches. Also added rake db:migrate:up/down to apply individual migrations that may need to be run when you merge branches #11458 [John Barnette]
-
-* Fixed that has_many :through would ignore the hash conditions #11447 [Emilio Tagua]
-
-* Fix issue where the :uniq option of a has_many :through association is ignored when find(:all) is called. Closes #9407 [cavalle]
-
-* Fix duplicate table alias error when including an association with a has_many :through association on the same join table. Closes #7310 [cavalle]
-
-* More efficient association preloading code that compacts a through_records array in a central location. Closes #11427 [Jack Danger Canty]
-
-* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-
-* Fixed that ActiveRecord#Base.find_or_create/initialize would not honor attr_protected/accessible when used with a hash #11422 [Emilio Tagua]
-
-* Added ActiveRecord#Base.all/first/last as aliases for find(:all/:first/:last) #11413 [nkallen, Chris O'Sullivan]
-
-* Merge the has_finder gem, renamed as 'named_scope'. #11404 [nkallen]
-
- class Article < ActiveRecord::Base
- named_scope :published, :conditions => {:published => true}
- named_scope :popular, :conditions => ...
- end
-
- Article.published.paginate(:page => 1)
- Article.published.popular.count
- Article.popular.find(:first)
- Article.popular.find(:all, :conditions => {...})
-
- See http://pivots.pivotallabs.com/users/nick/blog/articles/284-hasfinder-it-s-now-easier-than-ever-to-create-complex-re-usable-sql-queries
-
-* Add has_one :through support. #4756 [Chris O'Sullivan]
-
-* Migrations: create_table supports primary_key_prefix_type. #10314 [student, Chris O'Sullivan]
-
-* Added logging for dependency load errors with fixtures #11056 [stuthulhu]
-
-* Time zone aware attributes use Time#in_time_zone [Geoff Buesing]
-
-* Fixed that scoped joins would not always be respected #6821 [Theory/Jack Danger Canty]
-
-* Ensure that ActiveRecord::Calculations disambiguates field names with the table name. #11027 [cavalle]
-
-* Added add/remove_timestamps to the schema statements for adding the created_at/updated_at columns on existing tables #11129 [jramirez]
-
-* Added ActiveRecord::Base.find(:last) #11338 [Emilio Tagua]
-
-* test_native_types expects DateTime.local_offset instead of DateTime.now.offset; fixes test breakage due to dst transition [Geoff Buesing]
-
-* Add :readonly option to HasManyThrough associations. #11156 [Emilio Tagua]
-
-* Improve performance on :include/:conditions/:limit queries by selectively joining in the pre-query. #9560 [dasil003]
-
-* Perf fix: Avoid the use of named block arguments. Closes #11109 [adymo]
-
-* PostgreSQL: support server versions 7.4 through 8.0 and the ruby-pg driver. #11127 [jdavis]
-
-* Ensure association preloading doesn't break when an association returns nil. ##11145 [GMFlash]
-
-* Make dynamic finders respect the :include on HasManyThrough associations. #10998. [cpytel]
-
-* Base#instantiate_time_object only uses Time.zone when Base.time_zone_aware_attributes is true; leverages Time#time_with_datetime_fallback for readability [Geoff Buesing]
-
-* Refactor ConnectionAdapters::Column.new_time: leverage DateTime failover behavior of Time#time_with_datetime_fallback [Geoff Buesing]
-
-* Improve associations performance by using symbol callbacks instead of string callbacks. #11108 [adymo]
-
-* Optimise the BigDecimal conversion code. #11110 [adymo]
-
-* Introduce the :readonly option to all associations. Records from the association cannot be saved. #11084 [Emilio Tagua]
-
-* Multiparameter attributes for time columns fail over to DateTime when out of range of Time [Geoff Buesing]
-
-* Base#instantiate_time_object uses Time.zone.local() [Geoff Buesing]
-
-* Add timezone-aware attribute readers and writers. #10982 [Geoff Buesing]
-
-* Instantiating time objects in multiparameter attributes uses Time.zone if available. #10982 [Rick Olson]
-
-* Add note about how ActiveRecord::Observer classes are initialized in a Rails app. #10980 [Xavier Noria]
-
-* MySQL: omit text/blob defaults from the schema instead of using an empty string. #10963 [mdeiters]
-
-* belongs_to supports :dependent => :destroy and :delete. #10592 [Jonathan Viney]
-
-* Introduce preload query strategy for eager :includes. #9640 [Frederick Cheung, Aliaksey Kandratsenka, codafoo]
-
-* Support aggregations in finder conditions. #10572 [Ryan Kinderman]
-
-* Organize and clean up the Active Record test suite. #10742 [John Barnette]
-
-* Ensure that modifying has_and_belongs_to_many actions clear the query cache. Closes #10840 [john.andrews]
-
-* Fix issue where Table#references doesn't pass a :null option to a *_type attribute for polymorphic associations. Closes #10753 [railsjitsu]
-
-* Fixtures: removed support for the ancient pre-YAML file format. #10736 [John Barnette]
-
-* More thoroughly quote table names. #10698 [dimdenis, lotswholetime, Jeremy Kemper]
-
-* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement. #10686 [Brendan Ribera]
-
-* Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the new ActiveSupport::Cache::* libraries [David Heinemeier Hansson]
-
-* Make sure CSV fixtures are compatible with ruby 1.9's new csv implementation. [JEG2]
-
-* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 [Sam]
-
-* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 [jamesh, Frederick Cheung, protocool]
-
-* Don't unnecessarily load has_many associations in after_update callbacks. Closes #6822 [stopdropandrew, canadaduane]
-
-* Eager belongs_to :include infers the foreign key from the association name rather than the class name. #10517 [Jonathan Viney]
-
-* SQLite: fix rename_ and remove_column for columns with unique indexes. #10576 [Brandon Keepers]
-
-* Ruby 1.9 compatibility. #10655 [Jeremy Kemper, Dirkjan Bussink]
-
-
-*2.0.2* (December 16th, 2007)
-
-* Ensure optimistic locking handles nil #lock_version values properly. Closes #10510 [Rick Olson]
-
-* Make the Fixtures Test::Unit enhancements more supporting for double-loaded test cases. Closes #10379 [brynary]
-
-* Fix that validates_acceptance_of still works for non-existent tables (useful for bootstrapping new databases). Closes #10474 [Josh Susser]
-
-* Ensure that the :uniq option for has_many :through associations retains the order. #10463 [remvee]
-
-* Base.exists? doesn't rescue exceptions to avoid hiding SQL errors. #10458 [Michael Klishin]
-
-* Documentation: Active Record exceptions, destroy_all and delete_all. #10444, #10447 [Michael Klishin]
-
-
-*2.0.1* (December 7th, 2007)
-
-* Removed query cache rescue as it could cause code to be run twice (closes #10408) [David Heinemeier Hansson]
-
-
-*2.0.0* (December 6th, 2007)
-
-* Anchor DateTimeTest to fixed DateTime instead of a variable value based on Time.now#advance#to_datetime, so that this test passes on 64-bit platforms running Ruby 1.8.6+ [Geoff Buesing]
-
-* Fixed that the Query Cache should just be ignored if the database is misconfigured (so that the "About your applications environment" works even before the database has been created) [David Heinemeier Hansson]
-
-* Fixed that the truncation of strings longer than 50 chars should use inspect
-so newlines etc are escaped #10385 [Norbert Crombach]
-
-* Fixed that habtm associations should be able to set :select as part of their definition and have that honored [David Heinemeier Hansson]
-
-* Document how the :include option can be used in Calculations::calculate. Closes #7446 [adamwiggins, ultimoamore]
-
-* Fix typo in documentation for polymorphic associations w/STI. Closes #7461 [johnjosephbachir]
-
-* Reveal that the type option in migrations can be any supported column type for your database but also include caveat about agnosticism. Closes #7531 [adamwiggins, mikong]
-
-* More complete documentation for find_by_sql. Closes #7912 [fearoffish]
-
-* Added ActiveRecord::Base#becomes to turn a record into one of another class (mostly relevant for STIs) [David Heinemeier Hansson]. Example:
-
- render :partial => @client.becomes(Company) # renders companies/company instead of clients/client
-
-* Fixed that to_xml should not automatically pass :procs to associations included with :include #10162 [Cheah Chu Yeow]
-
-* Fix documentation typo introduced in [8250]. Closes #10339 [Henrik N]
-
-* Foxy fixtures: support single-table inheritance. #10234 [tom]
-
-* Foxy fixtures: allow mixed usage to make migration easier and more attractive. #10004 [lotswholetime]
-
-* Make the record_timestamps class-inheritable so it can be set per model. #10004 [tmacedo]
-
-* Allow validates_acceptance_of to use a real attribute instead of only virtual (so you can record that the acceptance occured) #7457 [ambethia]
-
-* DateTimes use Ruby's default calendar reform setting. #10201 [Geoff Buesing]
-
-* Dynamic finders on association collections respect association :order and :limit. #10211, #10227 [Patrick Joyce, Rick Olson, Jack Danger Canty]
-
-* Add 'foxy' support for fixtures of polymorphic associations. #10183 [John Barnette, David Lowenfels]
-
-* validates_inclusion_of and validates_exclusion_of allow formatted :message strings. #8132 [devrieda, Mike Naberezny]
-
-* attr_readonly behaves well with optimistic locking. #10188 [Nick Bugajski]
-
-* Base#to_xml supports the nil="true" attribute like Hash#to_xml. #8268 [Jonathan del Strother]
-
-* Change plings to the more conventional quotes in the documentation. Closes #10104 [Jack Danger Canty]
-
-* Fix HasManyThrough Association so it uses :conditions on the HasMany Association. Closes #9729 [Jack Danger Canty]
-
-* Ensure that column names are quoted. Closes #10134 [wesley.moxam]
-
-* Smattering of grammatical fixes to documentation. Closes #10083 [Bob Silva]
-
-* Enhance explanation with more examples for attr_accessible macro. Closes #8095 [fearoffish, Marcel Molina Jr.]
-
-* Update association/method mapping table to refected latest collection methods for has_many :through. Closes #8772 [Pratik Naik]
-
-* Explain semantics of having several different AR instances in a transaction block. Closes #9036 [jacobat, Marcel Molina Jr.]
-
-* Update Schema documentation to use updated sexy migration notation. Closes #10086 [Sam Granieri]
-
-* Make fixtures work with the new test subclasses. [Tarmo Tänav, Michael Koziarski]
-
-* Introduce finder :joins with associations. Same :include syntax but with inner rather than outer joins. #10012 [RubyRedRick]
- # Find users with an avatar
- User.find(:all, :joins => :avatar)
-
- # Find posts with a high-rated comment.
- Post.find(:all, :joins => :comments, :conditions => 'comments.rating > 3')
-
-* Associations: speedup duplicate record check. #10011 [Pratik Naik]
-
-* Make sure that << works on has_many associations on unsaved records. Closes #9989 [Josh Susser]
-
-* Allow association redefinition in subclasses. #9346 [wildchild]
-
-* Fix has_many :through delete with custom foreign keys. #6466 [naffis]
-
-* Foxy fixtures, from rathole (http://svn.geeksomnia.com/rathole/trunk/README)
- - stable, autogenerated IDs
- - specify associations (belongs_to, has_one, has_many) by label, not ID
- - specify HABTM associations as inline lists
- - autofill timestamp columns
- - support YAML defaults
- - fixture label interpolation
- Enabled for fixtures that correspond to a model class and don't specify a primary key value. #9981 [John Barnette]
-
-* Add docs explaining how to protect all attributes using attr_accessible with no arguments. Closes #9631 [boone, rmm5t]
-
-* Update add_index documentation to use new options api. Closes #9787 [Kamal Fariz Mahyuddin]
-
-* Allow find on a has_many association defined with :finder_sql to accept id arguments as strings like regular find does. Closes #9916 [krishna]
-
-* Use VALID_FIND_OPTIONS when resolving :find scoping rather than hard coding the list of valid find options. Closes #9443 [sur]
-
-* Limited eager loading no longer ignores scoped :order. Closes #9561 [Jack Danger Canty, Josh Peek]
-
-* Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation. #6322 [brandon, Chris Cruft]
-
-* Assigning nil to a composed_of aggregate also sets its immediate value to nil. #9843 [Chris Cruft]
-
-* Ensure that mysql quotes table names with database names correctly. Closes #9911 [crayz]
-
- "foo.bar" => "`foo`.`bar`"
-
-* Complete the assimilation of Sexy Migrations from ErrFree [Chris Wanstrath, PJ Hyett]
- http://errtheblog.com/post/2381
-
-* Qualified column names work in hash conditions, like :conditions => { 'comments.created_at' => ... }. #9733 [Jack Danger Canty]
-
-* Fix regression where the association would not construct new finder SQL on save causing bogus queries for "WHERE owner_id = NULL" even after owner was saved. #8713 [Bryan Helmkamp]
-
-* Refactor association create and build so before & after callbacks behave consistently. #8854 [Pratik Naik, mortent]
-
-* Quote table names. Defaults to column quoting. #4593 [Justin Lynn, gwcoffey, eadz, Dmitry V. Sabanin, Jeremy Kemper]
-
-* Alias association #build to #new so it behaves predictably. #8787 [Pratik Naik]
-
-* Add notes to documentation regarding attr_readonly behavior with counter caches and polymorphic associations. Closes #9835 [saimonmoore, Rick Olson]
-
-* Observers can observe model names as symbols properly now. Closes #9869 [queso]
-
-* find_and_(initialize|create)_by methods can now properly initialize protected attributes [Tobias Lütke]
-
-* belongs_to infers the foreign key from the association name instead of from the class name. [Jeremy Kemper]
-
-* PostgreSQL: support multiline default values. #7533 [Carl Lerche, aguynamedryan, Rein Henrichs, Tarmo Tänav]
-
-* MySQL: fix change_column on not-null columns that don't accept dfeault values of ''. #6663 [Jonathan Viney, Tarmo Tänav]
-
-* validates_uniqueness_of behaves well with abstract superclasses and
-single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Beausoleil, Josh Peek, Tarmo Tänav, pat]
-
-* Warn about protected attribute assigments in development and test environments when mass-assigning to an attr_protected attribute. #9802 [Henrik N]
-
-* Speedup database date/time parsing. [Jeremy Kemper, Tarmo Tänav]
-
-* Fix calling .clear on a has_many :dependent=>:delete_all association. [Tarmo Tänav]
-
-* Allow change_column to set NOT NULL in the PostgreSQL adapter [Tarmo Tänav]
-
-* Fix that ActiveRecord would create attribute methods and override custom attribute getters if the method is also defined in Kernel.methods. [Rick Olson]
-
-* Don't call attr_readonly on polymorphic belongs_to associations, in case it matches the name of some other non-ActiveRecord class/module. [Rick Olson]
-
-* Try loading activerecord-<adaptername>-adapter gem before trying a plain require so you can use custom gems for the bundled adapters. Also stops gems from requiring an adapter from an old Active Record gem. [Jeremy Kemper, Derrick Spell]
-
-
-*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3]
-
-* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [Dan Manges]
-
- class Comment < ActiveRecord::Base
- # Automatically sets Article#comments_count as readonly.
- belongs_to :article, :counter_cache => :comments_count
- end
-
- class Article < ActiveRecord::Base
- attr_readonly :approved_comments_count
- end
-
-* Make size for has_many :through use counter cache if it exists. Closes #9734 [Xavier Shay]
-
-* Remove DB2 adapter since IBM chooses to maintain their own adapter instead. [Jeremy Kemper]
-
-* Extract Oracle, SQLServer, and Sybase adapters into gems. [Jeremy Kemper]
-
-* Added fixture caching that'll speed up a normal fixture-powered test suite between 50% and 100% #9682 [Frederick Cheung]
-
-* Correctly quote id list for limited eager loading. #7482 [tmacedo]
-
-* Fixed that using version-targetted migrates would fail on loggers other than the default one #7430 [valeksenko]
-
-* Fixed rename_column for SQLite when using symbols for the column names #8616 [drodriguez]
-
-* Added the possibility of using symbols in addition to concrete classes with ActiveRecord::Observer#observe. #3998 [Robby Russell, Tarmo Tänav]
-
-* Added ActiveRecord::Base#to_json/from_json [David Heinemeier Hansson, Cheah Chu Yeow]
-
-* Added ActiveRecord::Base#from_xml [David Heinemeier Hansson]. Example:
-
- xml = "<person><name>David</name></person>"
- Person.new.from_xml(xml).name # => "David"
-
-* Define dynamic finders as real methods after first usage. [bscofield]
-
-* Deprecation: remove deprecated threaded_connections methods. Use allow_concurrency instead. [Jeremy Kemper]
-
-* Associations macros accept extension blocks alongside modules. #9346 [Josh Peek]
-
-* Speed up and simplify query caching. [Jeremy Kemper]
-
-* connection.select_rows 'sql' returns an array (rows) of arrays (field values). #2329 [Michael Schuerig]
-
-* Eager loading respects explicit :joins. #9496 [dasil003]
-
-* Extract Firebird, FrontBase, and OpenBase adapters into gems. #9508, #9509, #9510 [Jeremy Kemper]
-
-* RubyGem database adapters: expects a gem named activerecord-<database>-adapter with active_record/connection_adapters/<database>_adapter.rb in its load path. [Jeremy Kemper]
-
-* Fixed that altering join tables in migrations would fail w/ sqlite3 #7453 [TimoMihaljov/brandon]
-
-* Fix association writer with :dependent => :nullify. #7314 [Jonathan Viney]
-
-* OpenBase: update for new lib and latest Rails. Support migrations. #8748 [dcsesq]
-
-* Moved acts_as_tree into a plugin of the same name on the official Rails svn. #9514 [Pratik Naik]
-
-* Moved acts_as_nested_set into a plugin of the same name on the official Rails svn. #9516 [Josh Peek]
-
-* Moved acts_as_list into a plugin of the same name on the official Rails svn. [Josh Peek]
-
-* Explicitly require active_record/query_cache before using it. [Jeremy Kemper]
-
-* Fix bug where unserializing an attribute attempts to modify a frozen @attributes hash for a deleted record. [Rick Olson, marclove]
-
-* Performance: absorb instantiate and initialize_with_callbacks into the Base methods. [Jeremy Kemper]
-
-* Fixed that eager loading queries and with_scope should respect the :group option [David Heinemeier Hansson]
-
-* Improve performance and functionality of the postgresql adapter. Closes #8049 [roderickvd]
-
- For more information see: http://dev.rubyonrails.org/ticket/8049
-
-* Don't clobber includes passed to has_many.count [Jack Danger Canty]
-
-* Make sure has_many uses :include when counting [Jack Danger Canty]
-
-* Change the implementation of ActiveRecord's attribute reader and writer methods [Michael Koziarski]
- - Generate Reader and Writer methods which cache attribute values in hashes. This is to avoid repeatedly parsing the same date or integer columns.
- - Change exception raised when users use find with :select then try to access a skipped column. Plugins could override missing_attribute() to lazily load the columns.
- - Move method definition to the class, instead of the instance
- - Always generate the readers, writers and predicate methods.
-
-* Perform a deep #dup on query cache results so that modifying activerecord attributes does not modify the cached attributes. [Rick Olson]
-
-# Ensure that has_many :through associations use a count query instead of loading the target when #size is called. Closes #8800 [Pratik Naik]
-
-* Added :unless clause to validations #8003 [monki]. Example:
-
- def using_open_id?
- !identity_url.blank?
- end
-
- validates_presence_of :identity_url, :if => using_open_id?
- validates_presence_of :username, :unless => using_open_id?
- validates_presence_of :password, :unless => using_open_id?
-
-* Fix #count on a has_many :through association so that it recognizes the :uniq option. Closes #8801 [Pratik Naik]
-
-* Fix and properly document/test count(column_name) usage. Closes #8999 [Pratik Naik]
-
-* Remove deprecated count(conditions=nil, joins=nil) usage. Closes #8993 [Pratik Naik]
-
-* Change belongs_to so that the foreign_key assumption is taken from the association name, not the class name. Closes #8992 [Josh Susser]
-
- OLD
- belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id
-
- NEW
- belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is visitor_id
-
-* Remove spurious tests from deprecated_associations_test, most of these aren't deprecated, and are duplicated in associations_test. Closes #8987 [Pratik Naik]
-
-* Make create! on a has_many :through association return the association object. Not the collection. Closes #8786 [Pratik Naik]
-
-* Move from select * to select tablename.* to avoid clobbering IDs. Closes #8889 [dasil003]
-
-* Don't call unsupported methods on associated objects when using :include, :method with to_xml #7307, [Manfred Stienstra, jwilger]
-
-* Define collection singular ids method for has_many :through associations. #8763 [Pratik Naik]
-
-* Array attribute conditions work with proxied association collections. #8318 [Kamal Fariz Mahyuddin, theamazingrando]
-
-* Fix polymorphic has_one associations declared in an abstract class. #8638 [Pratik Naik, Dax Huiberts]
-
-* Fixed validates_associated should not stop on the first error. #4276 [mrj, Manfred Stienstra, Josh Peek]
-
-* Rollback if commit raises an exception. #8642 [kik, Jeremy Kemper]
-
-* Update tests' use of fixtures for the new collections api. #8726 [Kamal Fariz Mahyuddin]
-
-* Save associated records only if the association is already loaded. #8713 [Blaine]
-
-* MySQL: fix show_variable. #8448 [matt, Jeremy Kemper]
-
-* Fixtures: correctly delete and insert fixtures in a single transaction. #8553 [Michael Schuerig]
-
-* Fixtures: people(:technomancy, :josh) returns both fixtures. #7880 [technomancy, Josh Peek]
-
-* Calculations support non-numeric foreign keys. #8154 [Kamal Fariz Mahyuddin]
-
-* with_scope is protected. #8524 [Josh Peek]
-
-* Quickref for association methods. #7723 [marclove, Mindsweeper]
-
-* Calculations: return nil average instead of 0 when there are no rows to average. #8298 [davidw]
-
-* acts_as_nested_set: direct_children is sorted correctly. #4761 [Josh Peek, rails@33lc0.net]
-
-* Raise an exception if both attr_protected and attr_accessible are declared. #8507 [stellsmi]
-
-* SQLite, MySQL, PostgreSQL, Oracle: quote column names in column migration SQL statements. #8466 [marclove, lorenjohnson]
-
-* Allow nil serialized attributes with a set class constraint. #7293 [sandofsky]
-
-* Oracle: support binary fixtures. #7987 [Michael Schoen]
-
-* Fixtures: pull fixture insertion into the database adapters. #7987 [Michael Schoen]
-
-* Announce migration versions as they're performed. [Jeremy Kemper]
-
-* find gracefully copes with blank :conditions. #7599 [Dan Manges, johnnyb]
-
-* validates_numericality_of takes :greater_than, :greater_than_or_equal_to, :equal_to, :less_than, :less_than_or_equal_to, :odd, and :even options. #3952 [Bob Silva, Dan Kubb, Josh Peek]
-
-* MySQL: create_database takes :charset and :collation options. Charset defaults to utf8. #8448 [matt]
-
-* Find with a list of ids supports limit/offset. #8437 [hrudududu]
-
-* Optimistic locking: revert the lock version when an update fails. #7840 [plang]
-
-* Migrations: add_column supports custom column types. #7742 [jsgarvin, Theory]
-
-* Load database adapters on demand. Eliminates config.connection_adapters and RAILS_CONNECTION_ADAPTERS. Add your lib directory to the $LOAD_PATH and put your custom adapter in lib/active_record/connection_adapters/adaptername_adapter.rb. This way you can provide custom adapters as plugins or gems without modifying Rails. [Jeremy Kemper]
-
-* Ensure that associations with :dependent => :delete_all respect :conditions option. Closes #8034 [Jack Danger Canty, Josh Peek, Rick Olson]
-
-* belongs_to assignment creates a new proxy rather than modifying its target in-place. #8412 [mmangino@elevatedrails.com]
-
-* Fix column type detection while loading fixtures. Closes #7987 [roderickvd]
-
-* Document deep eager includes. #6267 [Josh Susser, Dan Manges]
-
-* Document warning that associations names shouldn't be reserved words. #4378 [murphy@cYcnus.de, Josh Susser]
-
-* Sanitize Base#inspect. #8392, #8623 [Nik Wakelin, jnoon]
-
-* Replace the transaction {|transaction|..} semantics with a new Exception ActiveRecord::Rollback. [Michael Koziarski]
-
-* Oracle: extract column length for CHAR also. #7866 [ymendel]
-
-* Document :allow_nil option for validates_acceptance_of since it defaults to true. [tzaharia]
-
-* Update documentation for :dependent declaration so that it explicitly uses the non-deprecated API. [Jack Danger Canty]
-
-* Add documentation caveat about when to use count_by_sql. [fearoffish]
-
-* Enhance documentation for increment_counter and decrement_counter. [fearoffish]
-
-* Provide brief introduction to what optimistic locking is. [fearoffish]
-
-* Add documentation for :encoding option to mysql adapter. [marclove]
-
-* Added short-hand declaration style to migrations (inspiration from Sexy Migrations, http://errtheblog.com/post/2381) [David Heinemeier Hansson]. Example:
-
- create_table "products" do |t|
- t.column "shop_id", :integer
- t.column "creator_id", :integer
- t.column "name", :string, :default => "Untitled"
- t.column "value", :string, :default => "Untitled"
- t.column "created_at", :datetime
- t.column "updated_at", :datetime
- end
-
- ...can now be written as:
-
- create_table :products do |t|
- t.integer :shop_id, :creator_id
- t.string :name, :value, :default => "Untitled"
- t.timestamps
- end
-
-* Use association name for the wrapper element when using .to_xml. Previous behavior lead to non-deterministic situations with STI and polymorphic associations. [Michael Koziarski, jstrachan]
-
-* Improve performance of calling .create on has_many :through associations. [evan]
-
-* Improved cloning performance by relying less on exception raising #8159 [Blaine]
-
-* Added ActiveRecord::Base.inspect to return a column-view like #<Post id:integer, title:string, body:text> [David Heinemeier Hansson]
-
-* Added yielding of Builder instance for ActiveRecord::Base#to_xml calls [David Heinemeier Hansson]
-
-* Small additions and fixes for ActiveRecord documentation. Closes #7342 [Jeremy McAnally]
-
-* Add helpful debugging info to the ActiveRecord::StatementInvalid exception in ActiveRecord::ConnectionAdapters::SqliteAdapter#table_structure. Closes #7925. [court3nay]
-
-* SQLite: binary escaping works with $KCODE='u'. #7862 [tsuka]
-
-* Base#to_xml supports serialized attributes. #7502 [jonathan]
-
-* Base.update_all :order and :limit options. Useful for MySQL updates that must be ordered to avoid violating unique constraints. [Jeremy Kemper]
-
-* Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 [Michael Koziarski, Jeremy Kemper]
-
-* PostgreSQL: remove DateTime -> Time downcast. Warning: do not enable translate_results for the C bindings if you have timestamps outside Time's domain. [Jeremy Kemper]
-
-* find_or_create_by_* takes a hash so you can create with more attributes than are in the method name. For example, Person.find_or_create_by_name(:name => 'Henry', :comments => 'Hi new user!') is equivalent to Person.find_by_name('Henry') || Person.create(:name => 'Henry', :comments => 'Hi new user!'). #7368 [Josh Susser]
-
-* Make sure with_scope takes both :select and :joins into account when setting :readonly. Allows you to save records you retrieve using method_missing on a has_many :through associations. [Michael Koziarski]
-
-* Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
-
-* Consistent public/protected/private visibility for chained methods. #7813 [Dan Manges]
-
-* Oracle: fix quoted primary keys and datetime overflow. #7798 [Michael Schoen]
-
-* Consistently quote primary key column names. #7763 [toolmantim]
-
-* Fixtures: fix YAML ordered map support. #2665 [Manuel Holtgrewe, nfbuckley]
-
-* DateTimes assume the default timezone. #7764 [Geoff Buesing]
-
-* Sybase: hide timestamp columns since they're inherently read-only. #7716 [Mike Joyce]
-
-* Oracle: overflow Time to DateTime. #7718 [Michael Schoen]
-
-* PostgreSQL: don't use async_exec and async_query with postgres-pr. #7727, #7762 [flowdelic, toolmantim]
-
-* Fix has_many :through << with custom foreign keys. #6466, #7153 [naffis, Rich Collins]
-
-* Test DateTime native type in migrations, including an edge case with dates
-during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
-
-* SQLServer: correctly schema-dump tables with no indexes or descending indexes. #7333, #7703 [Jakob Skjerning, Tom Ward]
-
-* SQLServer: recognize real column type as Ruby float. #7057 [sethladd, Tom Ward]
-
-* Added fixtures :all as a way of loading all fixtures in the fixture directory at once #7214 [Manfred Stienstra]
-
-* Added database connection as a yield parameter to ActiveRecord::Base.transaction so you can manually rollback [David Heinemeier Hansson]. Example:
-
- transaction do |transaction|
- david.withdrawal(100)
- mary.deposit(100)
- transaction.rollback! # rolls back the transaction that was otherwise going to be successful
- end
-
-* Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method [Jamis Buck]
-
-* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Lütke]
- Task.cache { Task.find(1); Task.find(1) } # => 1 query
-
-* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
-
-* Oracle: fix lob and text default handling. #7344 [gfriedrich, Michael Schoen]
-
-* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob Skjerning]
-
-* MySQL: blob and text columns may not have defaults in 5.x. Update fixtures schema for strict mode. #6695 [Dan Kubb]
-
-* update_all can take a Hash argument. sanitize_sql splits into two methods for conditions and assignment since NULL values and delimiters are handled differently. #6583, #7365 [sandofsky, Assaf]
-
-* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id. #6778 [Jonathan Viney, timc]
-
-* Use Date#to_s(:db) for quoted dates. #7411 [Michael Schoen]
-
-* Don't create instance writer methods for class attributes. Closes #7401 [Rick Olson]
-
-* Docs: validations examples. #7343 [zackchandler]
-
-* Add missing tests ensuring callbacks work with class inheritance. Closes #7339 [sandofsky]
-
-* Fixtures use the table name and connection from set_fixture_class. #7330 [Anthony Eden]
-
-* Remove useless code in #attribute_present? since 0 != blank?. Closes #7249 [Josh Susser]
-
-* Fix minor doc typos. Closes #7157 [Josh Susser]
-
-* Fix incorrect usage of #classify when creating the eager loading join statement. Closes #7044 [Josh Susser]
-
-* SQLServer: quote table name in indexes query. #2928 [keithm@infused.org]
-
-* Subclasses of an abstract class work with single-table inheritance. #5704, #7284 [BertG, nick+rails@ag.arizona.edu]
-
-* Make sure sqlite3 driver closes open connections on disconnect [Rob Rasmussen]
-
-* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [Jeremy McAnally]
-
-* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 [Dan Manges, Jeremy Kemper]
-
-* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, Manfred Stienstra, altano@bigfoot.com]
-
-* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 [Michael Schoen]
-
-* MySQL: retain SSL settings on reconnect. #6976 [randyv2]
-
-* Apply scoping during initialize instead of create. Fixes setting of foreign key when using find_or_initialize_by with scoping. [Cody Fauser]
-
-* SQLServer: handle [quoted] table names. #6635 [rrich]
-
-* acts_as_nested_set works with single-table inheritance. #6030 [Josh Susser]
-
-* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order. #4668, #7021 [eventualbuddha, Michael Schoen]
-
-* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 [Dan Manges]
- Student.find(:all, :conditions => { :grade => 9..12 })
-
-* Fix the Oracle adapter for serialized attributes stored in CLOBs. Closes #6825 [mschoen, tdfowler]
-
-* [DOCS] Apply more documentation for ActiveRecord Reflection. Closes #4055 [Robby Russell]
-
-* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 [Caio Chassot]
-
-* Bring the sybase adapter up to scratch for 1.2 release. [jsheets]
-
-* Rollback new_record? and id when an exception is raised in a save callback. #6910 [Ben Curren, outerim]
-
-* Pushing a record on an association collection doesn't unnecessarily load all the associated records. [Obie Fernandez, Jeremy Kemper]
-
-* Oracle: fix connection reset failure. #6846 [leonlleslie]
-
-* Subclass instantiation doesn't try to explicitly require the corresponding subclass. #6840 [leei, Jeremy Kemper]
-
-* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 [protocool]
-
-* Consolidated different create and create! versions to call through to the base class with scope. This fixes inconsistencies, especially related to protected attribtues. Closes #5847 [Alexander Dymo, Tobias Lütke]
-
-* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using. #6764 [vitaly, Jeremy Kemper]
-
-* Add AssociationCollection#create! to be consistent with AssociationCollection#create when dealing with a foreign key that is a protected attribute [Cody Fauser]
-
-* Added counter optimization for AssociationCollection#any? so person.friends.any? won't actually load the full association if we have the count in a cheaper form [David Heinemeier Hansson]
-
-* Change fixture_path to a class inheritable accessor allowing test cases to have their own custom set of fixtures. #6672 [Zach Dennis]
-
-* Quote ActiveSupport::Multibyte::Chars. #6653 [Julian Tarkhanov]
-
-* Simplify query_attribute by typecasting the attribute value and checking whether it's nil, false, zero or blank. #6659 [Jonathan Viney]
-
-* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string. #5716 [Andreas Schwarz]
-
-* Run validations in the order they were declared. #6657 [obrie]
-
-* MySQL: detect when a NOT NULL column without a default value is misreported as default ''. Can't detect for string, text, and binary columns since '' is a legitimate default. #6156 [simon@redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper]
-
-* Simplify association proxy implementation by factoring construct_scope out of method_missing. #6643 [martin]
-
-* Oracle: automatically detect the primary key. #6594 [vesaria, Michael Schoen]
-
-* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml. #6607 [philbogle@gmail.com, ray.fortna@jobster.com, Michael Schoen]
-
-* Don't inspect unloaded associations. #2905 [lmarlow]
-
-* SQLite: use AUTOINCREMENT primary key in >= 3.1.0. #6588, #6616 [careo, lukfugl]
-
-* Cache inheritance_column. #6592 [Stefan Kaes]
-
-* Firebird: decimal/numeric support. #6408 [macrnic]
-
-* make add_order a tad faster. #6567 [Stefan Kaes]
-
-* Find with :include respects scoped :order. #5850
-
-* Support nil and Array in :conditions => { attr => value } hashes. #6548 [Assaf, Jeremy Kemper]
- find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
-
-* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column. #6495 [Si]
-
-* SQLite: fix calculations workaround, remove count(distinct) query rewrite, cleanup test connection scripts. [Jeremy Kemper]
-
-* SQLite: count(distinct) queries supported in >= 3.2.6. #6544 [Bob Silva]
-
-* Dynamically generate reader methods for serialized attributes. #6362 [Stefan Kaes]
-
-* Deprecation: object transactions warning. [Jeremy Kemper]
-
-* has_one :dependent => :nullify ignores nil associates. #4848, #6528 [bellis@deepthought.org, janovetz, Jeremy Kemper]
-
-* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters. #6515 [Michael Schoen]
-
-* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 [obrie]
-
-* Document other options available to migration's add_column. #6419 [grg]
-
-* MySQL: all_hashes compatibility with old MysqlRes class. #6429, #6601 [Jeremy Kemper]
-
-* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
-
-* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick Olson]
-
-* Restore eager condition interpolation, document it's differences [Rick Olson]
-
-* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 [Jacob Fugal, Jeremy Kemper]
-
-* Add #delete support to has_many :through associations. Closes #6049 [Martin Landers]
-
-* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick Olson]
-
-* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip@turnipspatch.com]
-
-* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 [wreese@gmail.com]
-
-* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
-
-* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [Bob Silva]
-
-* The has_many create method works with polymorphic associations. #6361 [Dan Peterson]
-
-* MySQL: introduce Mysql::Result#all_hashes to support further optimization. #5581 [Stefan Kaes]
-
-* save! shouldn't validate twice. #6324 [maiha, Bob Silva]
-
-* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values. #1887, #5780 [Michael Schuerig]
-
-* Add an attribute reader method for ActiveRecord::Base.observers [Rick Olson]
-
-* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins. #6287 [Bob Silva]
-
-* has_one associations with a nil target may be safely marshaled. #6279 [norbauer, Jeremy Kemper]
-
-* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Michael Koziarski]
-
-* Add a :namespace option to AR::Base#to_xml [Michael Koziarski]
-
-* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
-
-* Mock Time.now for more accurate Touch mixin tests. #6213 [Dan Peterson]
-
-* Improve yaml fixtures error reporting. #6205 [Bruce Williams]
-
-* Rename AR::Base#quote so people can use that name in their models. #3628 [Michael Koziarski]
-
-* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
-
-* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 [jimw@mysql.com]
-
-* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true. #6024 [Josh Susser]
-
-* Document validates_presences_of behavior with booleans: you probably want validates_inclusion_of :attr, :in => [true, false]. #2253 [Bob Silva]
-
-* Optimistic locking: gracefully handle nil versions, treat as zero. #5908 [Tom Ward]
-
-* to_xml: the :methods option works on arrays of records. #5845 [Josh Starcher]
-
-* Deprecation: update docs. #5998 [Jakob Skjerning, Kevin Clark]
-
-* Add some XmlSerialization tests for ActiveRecord [Rick Olson]
-
-* has_many :through conditions are sanitized by the associating class. #5971 [martin.emde@gmail.com]
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Fix spurious newlines and spaces in AR::Base#to_xml output [Jamis Buck]
-
-* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database. #5927 [Chris Mear, Jonathan Viney]
-
-* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name. #5911 [Jonathan Viney]
-
-* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german. #5894 [Tom Ward, kruth@bfpi]
-
-* SQLServer: fix eager association test. #5901 [Tom Ward]
-
-* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance. #5838 [Kevin Clark]
-
-* Fixtures: correct escaping of \n and \r. #5859 [evgeny.zislis@gmail.com]
-
-* Migrations: gracefully handle missing migration files. #5857 [eli.gordon@gmail.com]
-
-* MySQL: update test schema for MySQL 5 strict mode. #5861 [Tom Ward]
-
-* to_xml: correct naming of included associations. #5831 [Josh Starcher]
-
-* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [Josh Susser]
-
-* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
-
- # Create a tagging to associate the post and tag.
- post.tags << Tag.find_by_name('old')
- post.tags.create! :name => 'general'
-
- # Would have been:
- post.taggings.create!(:tag => Tag.find_by_name('finally')
- transaction do
- post.taggings.create!(:tag => Tag.create!(:name => 'general'))
- end
-
-* Cache nil results for :included has_one associations also. #5787 [Michael Schoen]
-
-* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Lütke]
-
-* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
- Example: Invoice::Lineitem is given table name invoice_lineitems
-
-* Migrations: uniquely name multicolumn indexes so you don't have to. [Jeremy Kemper]
- # people_active_last_name_index, people_active_deactivated_at_index
- add_index :people, [:active, :last_name]
- add_index :people, [:active, :deactivated_at]
- remove_index :people, [:active, :last_name]
- remove_index :people, [:active, :deactivated_at]
-
- WARNING: backward-incompatibility. Multicolumn indexes created before this
- revision were named using the first column name only. Now they're uniquely
- named using all indexed columns.
-
- To remove an old multicolumn index, remove_index :table_name, :first_column
-
-* Fix for deep includes on the same association. [richcollins@gmail.com]
-
-* Tweak fixtures so they don't try to use a non-ActiveRecord class. [Kevin Clark]
-
-* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore. [Rick Olson]
-
-* Document find's :from option. Closes #5762. [andrew@redlinesoftware.com]
-
-* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema. #5280 [guy.naor@famundo.com]
-
-* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-
-* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael Schoen]
-
-* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.]
-
-* Don't save has_one associations unnecessarily. #5735 [Jonathan Viney]
-
-* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. [Rick Olson]
-
-* Formally deprecate the deprecated finders. [Michael Koziarski]
-
-* Formally deprecate rich associations. [Michael Koziarski]
-
-* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk@yahoo.com]
-
-* Fix announcement of very long migration names. #5722 [blake@near-time.com]
-
-* The exists? class method should treat a string argument as an id rather than as conditions. #5698 [jeremy@planetargon.com]
-
-* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 [alexkwolfe@gmail.com]
-
-* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
-
- assert (Topic.exists?(:author_name => "David"))
- assert (Topic.exists?(:author_name => "Mary", :approved => true))
- assert (Topic.exists?(["parent_id = ?", 1]))
-
-* Schema dumper quotes date :default values. [Dave Thomas]
-
-* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. [Dan Peterson]
-
-* Factor the attribute#{suffix} methods out of method_missing for easier extension. [Jeremy Kemper]
-
-* Patch sql injection vulnerability when using integer or float columns. [Jamis Buck]
-
-* Allow #count through a has_many association to accept :include. [Dan Peterson]
-
-* create_table rdoc: suggest :id => false for habtm join tables. [Zed Shaw]
-
-* PostgreSQL: return array fields as strings. #4664 [Robby Russell]
-
-* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 [Tom Ward]
-
-* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
-
-* Oracle: BigDecimal support. #5667 [Michael Schoen]
-
-* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2@gentoo.org, work@ashleymoran.me.uk]
-
-* Firebird migrations support. #5337 [Ken Kunz <kennethkunz@gmail.com>]
-
-* PostgreSQL: create/drop as postgres user. #4790 [mail@matthewpainter.co.uk, mlaster@metavillage.com]
-
-* Update callbacks documentation. #3970 [Robby Russell <robby@planetargon.com>]
-
-* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 [tietew@tietew.net]
-
-* PostgreSQL: correctly quote microseconds in timestamps. #5641 [rick@rickbradley.com]
-
-* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt@mattmargolis.net]
-
-* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [Michael Schoen]
-
-* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [Michael Schoen]
-
-* Added :group to available options for finds done on associations #5516 [mike@michaeldewey.org]
-
-* Minor tweak to improve performance of ActiveRecord::Base#to_param.
-
-* Observers also watch subclasses created after they are declared. #5535 [daniels@pronto.com.au]
-
-* Removed deprecated timestamps_gmt class methods. [Jeremy Kemper]
-
-* rake build_mysql_database grants permissions to rails@localhost. #5501 [brianegge@yahoo.com]
-
-* PostgreSQL: support microsecond time resolution. #5492 [alex@msgpad.com]
-
-* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
-
-* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. [Sam Stephenson]
-
-* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). [Shugo Maeda]
- # Obtain an exclusive lock on person 1 so we can safely increment visits.
- Person.transaction do
- # select * from people where id=1 for update
- person = Person.find(1, :lock => true)
- person.visits += 1
- person.save!
- end
-
-* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. [Jeremy Kemper]
-
-* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. [Jeremy Kemper]
-
-* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. [Nicholas Seckar]
-
-* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
-
-* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [David Heinemeier Hansson]
-
-* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
-
- Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
-
-...is the same as:
-
- Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
-
- This makes it easier to pass in the options from a form or otherwise outside.
-
-
-* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com]
-
-* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 [alex@purefiction.net]
-
-* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 [greg@lapcominc.com]
-
-* Fixed problems with eager loading and counting on SQL Server #5212 [kajism@yahoo.com]
-
-* Fixed that count distinct should use the selected column even when using :include #5251 [anna@wota.jp]
-
-* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 [alex@purefiction.net]
-
-* Allow models to override to_xml. #4989 [Blair Zajac <blair@orcaware.com>]
-
-* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket. #5247 [shimbo@is.naist.jp]
-
-* Records and arrays of records are bound as quoted ids. [Jeremy Kemper]
- Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
- Foo.find(:first, :conditions => ['bar_id = ?', bar])
-
-* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [David Heinemeier Hansson]
-
-* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick Olson]
-
-* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser]
-
-* Provide Association Extensions access to the instance that the association is being accessed from.
- Closes #4433 [Josh Susser]
-
-* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
-
-* Add a quick note about :select and eagerly included associations. [Rick Olson]
-
-* Add docs for the :as option in has_one associations. Closes #5144 [cdcarter@gmail.com]
-
-* Fixed that has_many collections shouldn't load the entire association to do build or create [David Heinemeier Hansson]
-
-* Added :allow_nil option for aggregations #5091 [Ian White]
-
-* Fix Oracle boolean support and tests. Closes #5139. [Michael Schoen]
-
-* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
-
-* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com]
-
-* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
-
-* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org]
-
-* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 [kajism@yahoo.com]
-
-* Allow :uniq => true with has_many :through associations. [Jeremy Kemper]
-
-* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
-
-* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
-
-* Replace superfluous name_to_class_name variant with camelize. [Marcel Molina Jr.]
-
-* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
-
-* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-
-* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac <blair@orcaware.com>]
-
-* Update FrontBase adapter to check binding version. Closes #4920. [mlaster@metavillage.com]
-
-* New Frontbase connections don't start in auto-commit mode. Closes #4922. [mlaster@metavillage.com]
-
-* When grouping, use the appropriate option key. [Marcel Molina Jr.]
-
-* Only modify the sequence name in the FrontBase adapter if the FrontBase adapter is actually being used. [Marcel Molina Jr.]
-
-* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. [mlaster@metavillage.com]
-
-* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil@gmail.com>]
-
-* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
-
-* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
-
-* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com]
-
-* Add support for :order option to with_scope. Closes #3887. [eric.daspet@survol.net]
-
-* Prettify output of schema_dumper by making things line up. Closes #4241 [Caio Chassot <caio@v2studio.com>]
-
-* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. [mlaster@metavillage.com]
-
-* Sybase Adapter type conversion cleanup. Closes #4736. [dev@metacasa.net]
-
-* Fix bug where calculations with long alias names return null. [Rick Olson]
-
-* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick Olson]
-
- @post.tags << @tag # BAD
- @post.taggings.create(:tag => @tag) # GOOD
-
-* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick Olson]
-
-* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
-
-* Add ActiveRecord::Errors#to_xml [Jamis Buck]
-
-* Properly quote index names in migrations (closes #4764) [John Long]
-
-* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick Olson]
-
-* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick Olson]
-
-* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick Olson]
-
-* DRY up association collection reader method generation. [Marcel Molina Jr.]
-
-* DRY up and tweak style of the validation error object. [Marcel Molina Jr.]
-
-* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick Olson]
-
- class Account < ActiveRecord::Base
- validates_uniqueness_of :email, :case_sensitive => false
- end
-
-* Allow multiple association extensions with :extend option (closes #4666) [Josh Susser]
-
- class Account < ActiveRecord::Base
- has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
- end
-
- *1.15.3* (March 12th, 2007)
-
- * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
-
- * Consistently quote primary key column names. #7763 [toolmantim]
-
- * Fixtures: fix YAML ordered map support. #2665 [Manuel Holtgrewe, nfbuckley]
-
- * Fix has_many :through << with custom foreign keys. #6466, #7153 [naffis, Rich Collins]
-
-
-*1.15.2* (February 5th, 2007)
-
-* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 [Dan Manges]
- Student.find(:all, :conditions => { :grade => 9..12 })
-
-* Don't create instance writer methods for class attributes. [Rick Olson]
-
-* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
-
-* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob Skjerning]
-
-* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column. #6495 [Si]
-
-* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id. #6778 [Jonathan Viney, timc]
-
-* Fixtures use the table name and connection from set_fixture_class. #7330 [Anthony Eden]
-
-* SQLServer: quote table name in indexes query. #2928 [keithm@infused.org]
-
-
-*1.15.1* (January 17th, 2007)
-
-* Fix nodoc breaking of adapters
-
-
-*1.15.0* (January 16th, 2007)
-
-* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [Jeremy McAnally]
-
-* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 [Dan Manges, Jeremy Kemper]
-
-* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, Manfred Stienstra, altano@bigfoot.com]
-
-* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 [Michael Schoen]
-
-* MySQL: retain SSL settings on reconnect. #6976 [randyv2]
-
-* SQLServer: handle [quoted] table names. #6635 [rrich]
-
-* acts_as_nested_set works with single-table inheritance. #6030 [Josh Susser]
-
-* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order. #4668, #7021 [eventualbuddha, Michael Schoen]
-
-* Fix the Oracle adapter for serialized attributes stored in CLOBs. Closes #6825 [mschoen, tdfowler]
-
-* [DOCS] Apply more documentation for ActiveRecord Reflection. Closes #4055 [Robby Russell]
-
-* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 [Caio Chassot]
-
-* Bring the sybase adapter up to scratch for 1.2 release. [jsheets]
-
-* Oracle: fix connection reset failure. #6846 [leonlleslie]
-
-* Subclass instantiation doesn't try to explicitly require the corresponding subclass. #6840 [leei, Jeremy Kemper]
-
-* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 [protocool]
-
-* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using. #6764 [vitaly, Jeremy Kemper]
-
-* Support nil and Array in :conditions => { attr => value } hashes. #6548 [Assaf, Jeremy Kemper]
- find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
-
-* Quote ActiveSupport::Multibyte::Chars. #6653 [Julian Tarkhanov]
-
-* MySQL: detect when a NOT NULL column without a default value is misreported as default ''. Can't detect for string, text, and binary columns since '' is a legitimate default. #6156 [simon@redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper]
-
-* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string. #5716 [Andreas Schwarz]
-
-* Oracle: automatically detect the primary key. #6594 [vesaria, Michael Schoen]
-
-* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml. #6607 [philbogle@gmail.com, ray.fortna@jobster.com, Michael Schoen]
-
-* Firebird: decimal/numeric support. #6408 [macrnic]
-
-* Find with :include respects scoped :order. #5850
-
-* Dynamically generate reader methods for serialized attributes. #6362 [Stefan Kaes]
-
-* Deprecation: object transactions warning. [Jeremy Kemper]
-
-* has_one :dependent => :nullify ignores nil associates. #6528 [janovetz, Jeremy Kemper]
-
-* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters. #6515 [Michael Schoen]
-
-* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 [obrie]
-
-* Document other options available to migration's add_column. #6419 [grg]
-
-* MySQL: all_hashes compatibility with old MysqlRes class. #6429, #6601 [Jeremy Kemper]
-
-* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
-
-* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick Olson]
-
-* Restore eager condition interpolation, document it's differences [Rick Olson]
-
-* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 [Jacob Fugal, Jeremy Kemper]
-
-* Add #delete support to has_many :through associations. Closes #6049 [Martin Landers]
-
-* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick Olson]
-
-* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip@turnipspatch.com]
-
-* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 [wreese@gmail.com]
-
-* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
-
-* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [Bob Silva]
-
-* The has_many create method works with polymorphic associations. #6361 [Dan Peterson]
-
-* MySQL: introduce Mysql::Result#all_hashes to support further optimization. #5581 [Stefan Kaes]
-
-* save! shouldn't validate twice. #6324 [maiha, Bob Silva]
-
-* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values. #1887, #5780 [Michael Schuerig]
-
-* Add an attribute reader method for ActiveRecord::Base.observers [Rick Olson]
-
-* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins. #6287 [Bob Silva]
-
-* has_one associations with a nil target may be safely marshaled. #6279 [norbauer, Jeremy Kemper]
-
-* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Michael Koziarski]
-
-* Add a :namespace option to AR::Base#to_xml [Michael Koziarski]
-
-* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
-
-* Mock Time.now for more accurate Touch mixin tests. #6213 [Dan Peterson]
-
-* Improve yaml fixtures error reporting. #6205 [Bruce Williams]
-
-* Rename AR::Base#quote so people can use that name in their models. #3628 [Michael Koziarski]
-
-* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
-
-* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 [jimw@mysql.com]
-
-* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true. #6024 [Josh Susser]
-
-* Optimistic locking: gracefully handle nil versions, treat as zero. #5908 [Tom Ward]
-
-* to_xml: the :methods option works on arrays of records. #5845 [Josh Starcher]
-
-* has_many :through conditions are sanitized by the associating class. #5971 [martin.emde@gmail.com]
-
-* Fix spurious newlines and spaces in AR::Base#to_xml output [Jamis Buck]
-
-* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database. #5927 [Chris Mear, Jonathan Viney]
-
-* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name. #5911 [Jonathan Viney]
-
-* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german. #5894 [Tom Ward, kruth@bfpi]
-
-* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance. #5838 [Kevin Clark]
-
-* Fixtures: correct escaping of \n and \r. #5859 [evgeny.zislis@gmail.com]
-
-* Migrations: gracefully handle missing migration files. #5857 [eli.gordon@gmail.com]
-
-* MySQL: update test schema for MySQL 5 strict mode. #5861 [Tom Ward]
-
-* to_xml: correct naming of included associations. #5831 [Josh Starcher]
-
-* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [Josh Susser]
-
-* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
-
- # Create a tagging to associate the post and tag.
- post.tags << Tag.find_by_name('old')
- post.tags.create! :name => 'general'
-
- # Would have been:
- post.taggings.create!(:tag => Tag.find_by_name('finally')
- transaction do
- post.taggings.create!(:tag => Tag.create!(:name => 'general'))
- end
-
-* Cache nil results for :included has_one associations also. #5787 [Michael Schoen]
-
-* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Lütke]
-
-* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
- Example: Invoice::Lineitem is given table name invoice_lineitems
-
-* Migrations: uniquely name multicolumn indexes so you don't have to. [Jeremy Kemper]
- # people_active_last_name_index, people_active_deactivated_at_index
- add_index :people, [:active, :last_name]
- add_index :people, [:active, :deactivated_at]
- remove_index :people, [:active, :last_name]
- remove_index :people, [:active, :deactivated_at]
-
- WARNING: backward-incompatibility. Multicolumn indexes created before this
- revision were named using the first column name only. Now they're uniquely
- named using all indexed columns.
-
- To remove an old multicolumn index, remove_index :table_name, :first_column
-
-* Fix for deep includes on the same association. [richcollins@gmail.com]
-
-* Tweak fixtures so they don't try to use a non-ActiveRecord class. [Kevin Clark]
-
-* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore. [Rick Olson]
-
-* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema. #5280 [guy.naor@famundo.com]
-
-* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-
-* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael Schoen]
-
-* Don't save has_one associations unnecessarily. #5735 [Jonathan Viney]
-
-* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. [Rick Olson]
-
-* Formally deprecate the deprecated finders. [Michael Koziarski]
-
-* Formally deprecate rich associations. [Michael Koziarski]
-
-* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk@yahoo.com]
-
-* Fix announcement of very long migration names. #5722 [blake@near-time.com]
-
-* The exists? class method should treat a string argument as an id rather than as conditions. #5698 [jeremy@planetargon.com]
-
-* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 [alexkwolfe@gmail.com]
-
-* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
-
- assert (Topic.exists?(:author_name => "David"))
- assert (Topic.exists?(:author_name => "Mary", :approved => true))
- assert (Topic.exists?(["parent_id = ?", 1]))
-
-* Schema dumper quotes date :default values. [Dave Thomas]
-
-* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. [Dan Peterson]
-
-* Factor the attribute#{suffix} methods out of method_missing for easier extension. [Jeremy Kemper]
-
-* Patch sql injection vulnerability when using integer or float columns. [Jamis Buck]
-
-* Allow #count through a has_many association to accept :include. [Dan Peterson]
-
-* create_table rdoc: suggest :id => false for habtm join tables. [Zed Shaw]
-
-* PostgreSQL: return array fields as strings. #4664 [Robby Russell]
-
-* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 [Tom Ward]
-
-* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
-
-* Oracle: BigDecimal support. #5667 [Michael Schoen]
-
-* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2@gentoo.org, work@ashleymoran.me.uk]
-
-* Firebird migrations support. #5337 [Ken Kunz <kennethkunz@gmail.com>]
-
-* PostgreSQL: create/drop as postgres user. #4790 [mail@matthewpainter.co.uk, mlaster@metavillage.com]
-
-* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 [tietew@tietew.net]
-
-* PostgreSQL: correctly quote microseconds in timestamps. #5641 [rick@rickbradley.com]
-
-* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt@mattmargolis.net]
-
-* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [Michael Schoen]
-
-* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [Michael Schoen]
-
-* Added :group to available options for finds done on associations #5516 [mike@michaeldewey.org]
-
-* Observers also watch subclasses created after they are declared. #5535 [daniels@pronto.com.au]
-
-* Removed deprecated timestamps_gmt class methods. [Jeremy Kemper]
-
-* rake build_mysql_database grants permissions to rails@localhost. #5501 [brianegge@yahoo.com]
-
-* PostgreSQL: support microsecond time resolution. #5492 [alex@msgpad.com]
-
-* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
-
-* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. [Sam Stephenson]
-
-* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). [Shugo Maeda]
- # Obtain an exclusive lock on person 1 so we can safely increment visits.
- Person.transaction do
- # select * from people where id=1 for update
- person = Person.find(1, :lock => true)
- person.visits += 1
- person.save!
- end
-
-* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. [Jeremy Kemper]
-
-* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. [Jeremy Kemper]
-
-* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. [Nicholas Seckar]
-
-* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
-
-* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [David Heinemeier Hansson]
-
-* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
-
- Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
-
-...is the same as:
-
- Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
-
- This makes it easier to pass in the options from a form or otherwise outside.
-
-
-* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com]
-
-* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 [alex@purefiction.net]
-
-* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 [greg@lapcominc.com]
-
-* Fixed problems with eager loading and counting on SQL Server #5212 [kajism@yahoo.com]
-
-* Fixed that count distinct should use the selected column even when using :include #5251 [anna@wota.jp]
-
-* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 [alex@purefiction.net]
-
-* Allow models to override to_xml. #4989 [Blair Zajac <blair@orcaware.com>]
-
-* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket. #5247 [shimbo@is.naist.jp]
-
-* Records and arrays of records are bound as quoted ids. [Jeremy Kemper]
- Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
- Foo.find(:first, :conditions => ['bar_id = ?', bar])
-
-* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [David Heinemeier Hansson]
-
-* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick Olson]
-
-* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser]
-
-* Provide Association Extensions access to the instance that the association is being accessed from.
- Closes #4433 [Josh Susser]
-
-* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
-
-* Add a quick note about :select and eagerly included associations. [Rick Olson]
-
-* Add docs for the :as option in has_one associations. Closes #5144 [cdcarter@gmail.com]
-
-* Fixed that has_many collections shouldn't load the entire association to do build or create [David Heinemeier Hansson]
-
-* Added :allow_nil option for aggregations #5091 [Ian White]
-
-* Fix Oracle boolean support and tests. Closes #5139. [Michael Schoen]
-
-* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
-
-* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com]
-
-* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
-
-* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org]
-
-* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 [kajism@yahoo.com]
-
-* Allow :uniq => true with has_many :through associations. [Jeremy Kemper]
-
-* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
-
-* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
-
-* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac <blair@orcaware.com>]
-
-* When grouping, use the appropriate option key. [Marcel Molina Jr.]
-
-* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. [mlaster@metavillage.com]
-
-* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil@gmail.com>]
-
-* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
-
-* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
-
-* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com]
-
-* Add support for :order option to with_scope. Closes #3887. [eric.daspet@survol.net]
-
-* Prettify output of schema_dumper by making things line up. Closes #4241 [Caio Chassot <caio@v2studio.com>]
-
-* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. [mlaster@metavillage.com]
-
-* Sybase Adapter type conversion cleanup. Closes #4736. [dev@metacasa.net]
-
-* Fix bug where calculations with long alias names return null. [Rick Olson]
-
-* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick Olson]
-
- @post.tags << @tag # BAD
- @post.taggings.create(:tag => @tag) # GOOD
-
-* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick Olson]
-
-* Add ActiveRecord::Errors#to_xml [Jamis Buck]
-
-* Properly quote index names in migrations (closes #4764) [John Long]
-
-* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick Olson]
-
-* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick Olson]
-
-* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick Olson]
-
-* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick Olson]
-
- class Account < ActiveRecord::Base
- validates_uniqueness_of :email, :case_sensitive => false
- end
-
-* Allow multiple association extensions with :extend option (closes #4666) [Josh Susser]
-
- class Account < ActiveRecord::Base
- has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
- end
-
-
-*1.14.4* (August 8th, 2006)
-
-* Add warning about the proper way to validate the presence of a foreign key. #4147 [Francois Beausoleil <francois.beausoleil@gmail.com>]
-
-* Fix syntax error in documentation. #4679 [Mislav Marohnić]
-
-* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
-
-
-*1.14.3* (June 27th, 2006)
-
-* Fix announcement of very long migration names. #5722 [blake@near-time.com]
-
-* Update callbacks documentation. #3970 [Robby Russell <robby@planetargon.com>]
-
-* Properly quote index names in migrations (closes #4764) [John Long]
-
-* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick Olson]
-
-* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick Olson]
-
-
-*1.14.2* (April 9th, 2006)
-
-* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
-
-
-*1.14.1* (April 6th, 2006)
-
-* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
-
-* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
-
-* Enable Limit/Offset in Calculations (closes #4558) [lmarlow]
-
-* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick Olson]
-
-* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue@free.fr]
-
-* Allow AR::Base#respond_to? to behave when @attributes is nil [Ryan Davis]
-
-* Support eager includes when going through a polymorphic has_many association. [Rick Olson]
-
-* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick Olson]
-
- class Post < ActiveRecord::Base
- has_one :tagging, :as => :taggable
- end
-
- Post.find :all, :include => :tagging
-
-* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick Olson]
-
-* Added support for going through a polymorphic has_many association: (closes #4401) [Rick Olson]
-
- class PhotoCollection < ActiveRecord::Base
- has_many :photos, :as => :photographic
- belongs_to :firm
- end
-
- class Firm < ActiveRecord::Base
- has_many :photo_collections
- has_many :photos, :through => :photo_collections
- end
-
-* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. [ruben.nine@gmail.com]
-
-* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. [Florian Weber]
-
-* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) [Stefan]
-
-* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick Olson]
-
-* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [David Heinemeier Hansson]
-
-* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [David Heinemeier Hansson]
-
-* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
-
-* Fixed broken OCIAdapter #4457 [Michael Schoen]
-
-
-*1.14.0* (March 27th, 2006)
-
-* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
-
-* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick Olson]
-
-* Add support for :include to with_scope [andrew@redlinesoftware.com]
-
-* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [Michael Schoen]
-
-* Change periods (.) in table aliases to _'s. Closes #4251 [jeff@ministrycentered.com]
-
-* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick Olson]
-
-* Fixed issue that kept :select options from being scoped [Rick Olson]
-
-* Fixed db_schema_import when binary types are present #3101 [David Heinemeier Hansson]
-
-* Fixed that MySQL enums should always be returned as strings #3501 [David Heinemeier Hansson]
-
-* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. [Rick Olson]
-
- class Connection < ActiveRecord::Base
- belongs_to :user
- belongs_to :channel
- end
-
- class Channel < ActiveRecord::Base
- has_many :connections
- has_many :contacts, :through => :connections, :class_name => 'User' # OLD
- has_many :contacts, :through => :connections, :source => :user # NEW
- end
-
-* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 [contact@maik-schmidt.de]
-
-* Allow overriding of find parameters in scoped has_many :through calls [Rick Olson]
-
- In this example, :include => false disables the default eager association from loading. :select changes the standard
- select clause. :joins specifies a join that is added to the end of the has_many :through query.
-
- class Post < ActiveRecord::Base
- has_many :tags, :through => :taggings, :include => :tagging do
- def add_joins_and_select
- find :all, :select => 'tags.*, authors.id as author_id', :include => false,
- :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
- end
- end
- end
-
-* Fixed that schema changes while the database was open would break any connections to an SQLite database (now we reconnect if that error is throw) [David Heinemeier Hansson]
-
-* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [Jonathan Viney]
-
-* Quit ignoring default :include options in has_many :through calls [Mark James]
-
-* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [Jonathan Viney]
-
-* Eager Loading support added for has_many :through => :has_many associations (see below). [Rick Olson]
-
-* Allow has_many :through to work on has_many associations (closes #3864) [sco@scottraymond.net] Example:
-
- class Firm < ActiveRecord::Base
- has_many :clients
- has_many :invoices, :through => :clients
- end
-
- class Client < ActiveRecord::Base
- belongs_to :firm
- has_many :invoices
- end
-
- class Invoice < ActiveRecord::Base
- belongs_to :client
- end
-
-* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [Josh Susser]
-
-* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [Jonathan Viney]
-
-* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick Olson]
-
-* SQL Server adapter gets some love #4298 [Ryan Tomayko]
-
-* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell@cdmplus.com]
-
-* Rework table aliasing to account for truncated table aliases. Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
-
- Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
-
-* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick Olson]
-
-* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Michael Koziarski]
-
-* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Michael Koziarski]
-
-* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom Ward]
-
-* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco@pobox.com]
-
-* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
-
-* Use association's :conditions when eager loading. [Jeremy Evans] #4144
-
-* Alias the has_and_belongs_to_many join table on eager includes. #4106 [Jeremy Evans]
-
- This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
-
- Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
-
-* Oracle adapter gets some love #4230 [Michael Schoen]
-
- * Changes :text to CLOB rather than BLOB [Moses Hohman]
- * Fixes an issue with nil numeric length/scales (several)
- * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
- * Tweaks a unit test to get it all green again
- * Adds support for #current_database
-
-* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 [Rick Olson]
-
- class CachedModel < ActiveRecord::Base
- self.abstract_class = true
- end
-
- class Post < CachedModel
- end
-
- CachedModel.abstract_class?
- => true
-
- Post.abstract_class?
- => false
-
- Post.base_class
- => Post
-
- Post.table_name
- => 'posts'
-
-* Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]
-
- class Foo < ActiveRecord::Base
- has_many :attachments, :as => :attachable, :dependent => :delete_all
- end
-
-* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay]
-
-* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
-
-* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model [Rick Olson]
-
-* Allow ordering of calculated results and/or grouped fields in calculations [solo@gatelys.com]
-
-* Make ActiveRecord::Base#save! return true instead of nil on success. #4173 [johan@johansorensen.com]
-
-* Dynamically set allow_concurrency. #4044 [Stefan Kaes]
-
-* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
-
- topic.to_xml
-
- ...returns:
-
- <?xml version="1.0" encoding="UTF-8"?>
- <topic>
- <title>The First Topic</title>
- <author-name>David</author-name>
- <id type="integer">1</id>
- <approved type="boolean">false</approved>
- <replies-count type="integer">0</replies-count>
- <bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>
- <written-on type="datetime">2003-07-16 09:28:00</written-on>
- <content>Have a nice day</content>
- <author-email-address>david@loudthinking.com</author-email-address>
- <parent-id></parent-id>
- <last-read type="date">2004-04-15</last-read>
- </topic>
-
- ...and you can configure with:
-
- topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
-
- ...that'll return:
-
- <topic>
- <title>The First Topic</title>
- <author-name>David</author-name>
- <approved type="boolean">false</approved>
- <content>Have a nice day</content>
- <author-email-address>david@loudthinking.com</author-email-address>
- <parent-id></parent-id>
- <last-read type="date">2004-04-15</last-read>
- </topic>
-
- You can even do load first-level associations as part of the document:
-
- firm.to_xml :include => [ :account, :clients ]
-
- ...that'll return something like:
-
- <?xml version="1.0" encoding="UTF-8"?>
- <firm>
- <id type="integer">1</id>
- <rating type="integer">1</rating>
- <name>37signals</name>
- <clients>
- <client>
- <rating type="integer">1</rating>
- <name>Summit</name>
- </client>
- <client>
- <rating type="integer">1</rating>
- <name>Microsoft</name>
- </client>
- </clients>
- <account>
- <id type="integer">1</id>
- <credit-limit type="integer">50</credit-limit>
- </account>
- </firm>
-
-* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
-
-* Documentation fixes for :dependent [robby@planetargon.com]
-
-* Stop the MySQL adapter crashing when views are present. #3782 [Jonathan Viney]
-
-* Don't classify the belongs_to class, it is already singular #4117 [keithm@infused.org]
-
-* Allow set_fixture_class to take Classes instead of strings for a class in a module. Raise FixtureClassNotFound if a fixture can't load. [Rick Olson]
-
-* Fix quoting of inheritance column for STI eager loading #4098 [Jonathan Viney <jonathan@bluewire.net.nz>]
-
-* Added smarter table aliasing for eager associations for multiple self joins #3580 [Rick Olson]
-
- * The first time a table is referenced in a join, no alias is used.
- * After that, the parent class name and the reflection name are used.
-
- Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
-
- * Any additional join references get a numerical suffix like '_2', '_3', etc.
-
-* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
-
-* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna@wota.jp]. Examples:
-
- # cascaded in two levels
- >> Author.find(:all, :include=>{:posts=>:comments})
- => authors
- +- posts
- +- comments
-
- # cascaded in two levels and normal association
- >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
- => authors
- +- posts
- +- comments
- +- categorizations
-
- # cascaded in two levels with two has_many associations
- >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
- => authors
- +- posts
- +- comments
- +- categorizations
-
- # cascaded in three levels
- >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
- => companies
- +- groups
- +- members
- +- favorites
-
-* Make counter cache work when replacing an association #3245 [eugenol@gmail.com]
-
-* Make migrations verbose [Jamis Buck]
-
-* Make counter_cache work with polymorphic belongs_to [Jamis Buck]
-
-* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 [anna@wota.jp]
-
-* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
-
- * Does not support DATE SQL column types; use DATETIME instead.
- * Date columns on HABTM join tables are returned as String, not Time.
- * Insertions are potentially broken for :polymorphic join tables
- * BLOB column access not yet fully supported
-
-* Clear stale, cached connections left behind by defunct threads. [Jeremy Kemper]
-
-* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. [Jeremy Kemper]
-
-* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [Michael Schoen]
-
-* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [David Heinemeier Hansson]
-
-* Speed up class -> connection caching and stale connection verification. #3979 [Stefan Kaes]
-
-* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. [Kevin Clark]
-
-* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack@wobblini.net]
-
-* Define attribute query methods to avoid method_missing calls. #3677 [Jonathan Viney]
-
-* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 [Simon Stapleton, Tom Ward]
-
-* Added support for nested scopes #3407 [anna@wota.jp]. Examples:
-
- Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
- Developer.find(:all) # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
-
- # inner rule is used. (all previous parameters are ignored)
- Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
- Developer.find(:all) # => SELECT * FROM developers WHERE (name = 'Jamis')
- end
-
- # parameters are merged
- Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
- Developer.find(:all) # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
- end
- end
-
-* Fixed db2 connection with empty user_name and auth options #3622 [phurley@gmail.com]
-
-* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
-
-* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [Lars Pind]
-
-* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
-
- Person.average :age
- Person.minimum :age
- Person.maximum :age
- Person.sum :salary, :group => :last_name
-
-* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [Luke Redpath]
-
-* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
-
-* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 [Aggregated by schoenm@earthlink.net]
-
-* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails@atyp.de]
-
-* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [Stefan Kaes]
-
-* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first [Bob Silva]
-
-* Allow :include to be used with has_many :through associations #3611 [Michael Schoen]
-
-* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table). #2920 [Blair Zajac]
-
-* SQLServer: more compatible limit/offset emulation. #3779 [Tom Ward]
-
-* Polymorphic join support for has_one associations (has_one :foo, :as => :bar) #3785 [Rick Olson]
-
-* PostgreSQL: correctly parse negative integer column defaults. #3776 [bellis@deepthought.org]
-
-* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
-
-* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Lütke]
-
-* Using AssociationCollection#build with arrays of hashes should call build, not create [David Heinemeier Hansson]
-
-* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
-
-* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
-
-* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [Shugo Maeda]
-
-* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra <m.stienstra@fngtps.com>]
-
-* If the OCI library is not available, raise an exception indicating as much. #3593 [Michael Schoen]
-
-* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
-
-* Make dynamic finders honor additional passed in :conditions. #3569 [Oleg Pudeyev <pudeyo@rpi.edu>, Marcel Molina Jr.]
-
-* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
-
-* Make .count work for has_many associations with multi line finder sql [Michael Schoen]
-
-* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
-
-* Allow configuration of the column used for optimistic locking [wilsonb@gmail.com]
-
-* Don't hardcode 'id' in acts as list. [ror@philippeapril.com]
-
-* Fix date errors for SQLServer in association tests. #3406 [Kevin Clark]
-
-* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna@wota.jp]
-
-* Disambiguate table names for columns in validates_uniqueness_of's WHERE clause. #3423 [alex.borovsky@gmail.com]
-
-* .with_scope imposed create parameters now bypass attr_protected [Tobias Lütke]
-
-* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
-
-* Multiple enhancements and adjustments to DB2 adaptor. #3377 [contact@maik-schmidt.de]
-
-* Sanitize scoped conditions. [Marcel Molina Jr.]
-
-* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [David Heinemeier Hansson]
-
-* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [Tobias Lütke]
-
-* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [Tobias Lütke]
-
-* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
-
-* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
-
-* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
-
-* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
-
-* made .find() work on :through relations. [Tobias Lütke]
-
-* Fix typo in association docs. #3296. [Blair Zajac]
-
-* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
-
-*1.13.2* (December 13th, 2005)
-
-* Become part of Rails 1.0
-
-* MySQL: allow encoding option for mysql.rb driver. [Jeremy Kemper]
-
-* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]. Example:
-
- class Post
- has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
- end
-
- post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
- post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
- post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
-
-* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]
-
-* MySQL: fixes for the bundled mysql.rb driver. #3160 [Justin Forder]
-
-* SQLServer: fix obscure optimistic locking bug. #3068 [kajism@yahoo.com]
-
-* SQLServer: support uniqueidentifier columns. #2930 [keithm@infused.org]
-
-* SQLServer: cope with tables names qualified by owner. #3067 [jeff@ministrycentered.com]
-
-* SQLServer: cope with columns with "desc" in the name. #1950 [Ron Lusk, Ryan Tomayko]
-
-* SQLServer: cope with primary keys with "select" in the name. #3057 [rdifrango@captechventures.com]
-
-* Oracle: active? performs a select instead of a commit. #3133 [Michael Schoen]
-
-* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
-
-* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausoleil]
-
-* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
-
-* PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
-
-* Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
-
-* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
-
-* Added preliminary support for polymorphic associations [David Heinemeier Hansson]
-
-* Added preliminary support for join models [David Heinemeier Hansson]
-
-* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. [jeremy@jthopple.com, Marcel Molina Jr.]
-
-* Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz <kennethkunz@gmail.com>]
-
-* Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz <kennethkunz@gmail.com>]
-
-* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
-
-* Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
-
-* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
-
-* Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
-
-* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
-
-* MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. #2978 [Shugo Maeda]
-
-* has_and_belongs_to_many: use JOIN instead of LEFT JOIN. [Jeremy Kemper]
-
-* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
-
-* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
-
-* Correct boolean handling in generated reader methods. #2945 [Don Park, Stefan Kaes]
-
-* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
-
-* Document :force option to create_table. #2921 [Blair Zajac <blair@orcaware.com>]
-
-* Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz <kennethkunz@gmail.com>]
-
-* SQLServer: active? and reconnect! methods for handling stale connections. #428 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
-
-* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array. #1345 [MarkusQ@reality.com]
-
-* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
-
-* Oracle: active? and reconnect! methods for handling stale connections. Optionally retry queries after reconnect. #428 [Michael Schoen <schoenm@earthlink.net>]
-
-* Correct documentation for Base.delete_all. #1568 [Newhydra]
-
-* Oracle: test case for column default parsing. #2788 [Michael Schoen <schoenm@earthlink.net>]
-
-* Update documentation for Migrations. #2861 [Tom Werner <tom@cube6media.com>]
-
-* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? #428 [Jeremy Kemper]
-
-* Oracle: Much faster column reflection. #2848 [Michael Schoen <schoenm@earthlink.net>]
-
-* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
-
-* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
-
-* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
-
-* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
-
-* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
-
-* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]
-
-* Don't cast nil or empty strings to a dummy date. #2789 [Rick Bradley <rick@rickbradley.com>]
-
-* acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 [rephorm@rephorm.com]
-
-* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
-
-* Fixed acts_as_list for definitions without an explicit :order #2803 [Jonathan Viney]
-
-* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper]
-
-* Correct handling of complex order clauses with SQL Server limit emulation. #2770 [Tom Ward <tom@popdog.net>, Matt B.]
-
-* Correct whitespace problem in Oracle default column value parsing. #2788 [rick@rickbradley.com]
-
-* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 [larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper]
-
-* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. [Jeremy Kemper]
-
-* More compatible Oracle column reflection. #2771 [Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>]
-
-
-*1.13.0* (November 7th, 2005)
-
-* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
-
-* Added :include as an option for association declarations [David Heinemeier Hansson]. Example:
-
- has_many :posts, :include => [ :author, :comments ]
-
-* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints. Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes. [Jeremy Kemper] Example:
-
- Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
- # Find where name = ? and active=true
- Comment.find :all, :conditions => ['name = ?', name]
- # Create comment associated with :post_id
- Comment.create :body => "Hello world"
- end
-
-* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
-
-* Added constrain scoping for creates using a hash of attributes bound to the :creation key [David Heinemeier Hansson]. Example:
-
- Comment.constrain(:creation => { :post_id => 5 }) do
- # Associated with :post_id
- Comment.create :body => "Hello world"
- end
-
- This is rarely used directly, but allows for find_or_create on associations. So you can do:
-
- # If the tag doesn't exist, a new one is created that's associated with the person
- person.tags.find_or_create_by_name("Summer")
-
-* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [David Heinemeier Hansson]. Example:
-
- # No 'Summer' tag exists
- Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
-
- # Now the 'Summer' tag does exist
- Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
-
-* Added extension capabilities to has_many and has_and_belongs_to_many proxies [David Heinemeier Hansson]. Example:
-
- class Account < ActiveRecord::Base
- has_many :people do
- def find_or_create_by_name(name)
- first_name, *last_name = name.split
- last_name = last_name.join " "
-
- find_or_create_by_first_name_and_last_name(first_name, last_name)
- end
- end
- end
-
- person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
- person.first_name # => "David"
- person.last_name # => "Heinemeier Hansson"
-
- Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
-
-* Omit internal dtproperties table from SQLServer table list. #2729 [Ryan Tomayko]
-
-* Quote column names in generated SQL. #2728 [Ryan Tomayko]
-
-* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 [Jeremy Kemper]
-
-* Add Model.create! to match existing model.save! method. When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors). [Jeremy Kemper]
-
-* Correct fixture behavior when table name pluralization is off. #2719 [Rick Bradley <rick@rickbradley.com>]
-
-* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 [Dan Peterson]
-
-* Added migration support for Oracle #2647 [Michael Schoen]
-
-* Worked around that connection can't be reset if allow_concurrency is off. #2648 [Michael Schoen <schoenm@earthlink.net>]
-
-* Fixed SQL Server adapter to pass even more tests and do even better #2634 [Ryan Tomayko]
-
-* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
-
-* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 [Tom Ward]
-
-* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility. #2618 [dansketcher@gmail.com]
-
-* Constraints are cloned so they can't be inadvertently modified while they're
-in effect. Added :readonly finder constraint. Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true. [Jeremy Kemper <rails@bitsweat.net>]
-
-* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson@gmail.com]
-
-* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [Michael Schoen]
-
-* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
-
-
-*1.12.2* (October 26th, 2005)
-
-* Allow symbols to rename columns when using SQLite adapter. #2531 [Kevin Clark]
-
-* Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell <robby@planetargon.com>]
-
-* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [Stefan Kaes]
-
-* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz <patrick@lenz.sh>]
-
-* HABTM finders shouldn't return readonly records. #2525 [Patrick Lenz <patrick@lenz.sh>]
-
-* Make all tests runnable on their own. #2521. [Blair Zajac <blair@orcaware.com>]
-
-
-*1.12.1* (October 19th, 2005)
-
-* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
-
-* Correct PostgreSQL primary key sequence detection. #2507 [tmornini@infomania.com]
-
-* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
-
-
-*1.12.0* (October 16th, 2005)
-
-* Update/clean up documentation (rdoc)
-
-* PostgreSQL sequence support. Use set_sequence_name in your model class to specify its primary key sequence. #2292 [Rick Olson <technoweenie@gmail.com>, Robby Russell <robby@planetargon.com>]
-
-* Change default logging colors to work on both white and black backgrounds. [Sam Stephenson]
-
-* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net]
-
-* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby@planetargon.com>]
-
-* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
-
-* Avoid memleak in dev mode when using fcgi
-
-* Simplified .clear on active record associations by using the existing delete_records method. #1906 [Caleb <me@cpb.ca>]
-
-* Delegate access to a customized primary key to the conventional id method. #2444. [Blair Zajac <blair@orcaware.com>]
-
-* Fix errors caused by assigning a has-one or belongs-to property to itself
-
-* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped [Sam Stephenson]
-
-* Update DB2 adapter. #2206. [contact@maik-schmidt.de]
-
-* Corrections to SQLServer native data types. #2267. [rails.20.clarry@spamgourmet.com]
-
-* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
-
-* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. [Blair Zajac <blair@orcaware.com>]
-
-* Misc doc fixes (typos/grammar/etc.). #2430. [coffee2code]
-
-* Add test coverage for content_columns. #2432. [coffee2code]
-
-* Speed up for unthreaded environments. #2431. [Stefan Kaes]
-
-* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [Stefan Kaes]
-
-* Speed up the setting of table_name. #2428. [Stefan Kaes]
-
-* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig <michael@schuerig.de>]
-
-* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig <michael@schuerig.de>]
-
-* Add geometric type for postgresql adapter. #2233 [Andrew Kaspick]
-
-* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 [Chad Fowler <chad@chadfowler.com>, Nicholas Seckar]
-
-* Added :force option to create_table that'll try to drop the table if it already exists before creating
-
-* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. [Nicholas Seckar]
-
-* Use foreign_key inflection uniformly. #2156 [Blair Zajac <blair@orcaware.com>]
-
-* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys. #2221 [joergd@pobox.com, ObieFernandez <obiefernandez@gmail.com>]
-
-* Returning false from before_destroy should cancel the action. #1829 [Jeremy Huffman]
-
-* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type. #2256 [mat <mat@absolight.fr>]
-
-* Extensive documentation for the abstract database adapter. #2250 [François Beausoleil <fbeausoleil@ftml.net>]
-
-* Clean up Fixtures.reset_sequences for PostgreSQL. Handle tables with no rows and models with custom primary keys. #2174, #2183 [jay@jay.fm, Blair Zajac <blair@orcaware.com>]
-
-* Improve error message when nil is assigned to an attr which validates_size_of within a range. #2022 [Manuel Holtgrewe <purestorm@ggnore.net>]
-
-* Make update_attribute use the same writer method that update_attributes uses.
- #2237 [trevor@protocool.com]
-
-* Make migrations honor table name prefixes and suffixes. #2298 [Jakob Skjerning, Marcel Molina Jr.]
-
-* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 [dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org]
-
-* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id. #1749 [chris@chrisbrinker.com]
-
-* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
-
-* Added new symbol-driven approach to activating observers with Base#observers= [David Heinemeier Hansson]. Example:
-
- ActiveRecord::Base.observers = :cacher, :garbage_collector
-
-* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 [solo@gatelys.com]
-
-* Wrap :conditions in parentheses to prevent problems with OR's #1871 [Jamis Buck]
-
-* Allow the postgresql adapter to work with the SchemaDumper. [Jamis Buck]
-
-* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. [Jamis Buck]
-
-* Fixed migrations for Windows when using more than 10 [David Naseby]
-
-* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
-
-* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Lütke]
-
-* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
-
-* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
-
-* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina Jr.]
-
-* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
-
-* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
-
-* Added better exception error when unknown column types are used with migrations #1814 [François Beausoleil]
-
-* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism@yahoo.com]
-
-* Fixed comparison of Active Record objects so two new objects are not equal #2099 [deberg]
-
-* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 [Tom Ward]
-
-* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 [coffee2code]
-
-* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 [sd@notso.net]
-
-* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
-
-* Make sure the schema_info table is created before querying the current version #1903
-
-* Fixtures ignore table name prefix and suffix #1987 [Jakob Skjerning]
-
-* Add documentation for index_type argument to add_index method for migrations #2005 [Blaine]
-
-* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
-
-* Make destroy return self #1913 [Sebastian Kanthak]
-
-* Fix typo in validations documentation #1938 [court3nay]
-
-* Make acts_as_list work for insert_at(1) #1966 [hensleyl@papermountain.org]
-
-* Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
-
-* Allow add_column and create_table to specify NOT NULL #1712 [emptysands@gmail.com]
-
-* Fix create_table so that id column is implicitly added [Rick Olson]
-
-* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
-
-* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
-
-* Fixed the handling of camelCase columns names in Oracle #1798
-
-* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
-
-* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
-
-* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Lütke]
-
- class Comment < AR:B
- def self.search(q)
- find(:all, :conditions => ["body = ?", q])
- end
- end
-
- class Post < AR:B
- has_many :comments
- end
-
- Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
-
- NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
- by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
- details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
- noticed :)
-
-* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 [Sam Stephenson]
-
-* Remove extra definition of supports_migrations? from abstract_adaptor.rb [Nicholas Seckar]
-
-* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
-
-* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 [Maik Schmidt]
-
-* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 [Stefan Kaes]
-
-
-*1.11.1* (11 July, 2005)
-
-* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olson]
-
-* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler@kianta.com]
-
-* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
-
-* Correct reflected table name for singular associations. #1688 [court3nay]
-
-* Fixed optimistic locking with SQL Server #1660 [tom@popdog.net]
-
-* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
-
-* Added better error message for "packets out of order" #1630 [court3nay]
-
-* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
-
-
-*1.11.0* (6 July, 2005)
-
-* Fixed that Yaml error message in fixtures hid the real error #1623 [Nicholas Seckar]
-
-* Changed logging of SQL statements to use the DEBUG level instead of INFO
-
-* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Lütke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
-
-* Added callback hooks to association collections #1549 [Florian Weber]. Example:
-
- class Project
- has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
-
- def evaluate_velocity(developer)
- ...
- end
- end
-
- ..raising an exception will cause the object not to be added (or removed, with before_remove).
-
-
-* Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry]
-
-* Fixed Base#write_attribute to work with both symbols and strings #1190 [Paul Legato]
-
-* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 [Florian Weber]
-
-* Speed up ActiveRecord#method_missing for the common case (read_attribute).
-
-* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [Stefan Kaes]
-
-* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
-
-* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel@onemail.at]
-
-* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [Michael Schuerig]
-
-* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
-
-* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry]
-
-* Fixed that multiparameter posts ignored attr_protected #1532 [alec+rails@veryclever.net]
-
-* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 [flash@vanklinkenbergsoftware.nl]
-
-* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be:
-
- Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
-
- ...should instead be:
-
- Developer.find(
- :all,
- :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
- :conditions => 'project_id=1'
- )
-
-* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.]
-
-* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
-
-* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
-
-* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 [Alisdair McDiarmid]
-
-* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
-
-* Allow any Enumerable, not just Array, to work as bind variables #1344 [Jeremy Kemper]
-
-* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
- Example:
-
- david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
- david.save
-
- If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
- project is saved when david.save is called.
-
- Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
-
- david.project_ids = [1, 5, 7]
-
-* Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com]
-
-* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Lütke]
-
-* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 [dave@cherryville.org]
-
-* Corrected @@configurations typo #1410 [david@ruppconsulting.com]
-
-* Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com]
-
-* Allow before/after update hooks to work on models using optimistic locking
-
-* Eager loading of dependent has_one associations won't delete the association #1212
-
-* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
-
-* Using transactional fixtures now causes the data to be loaded only once.
-
-* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
-
- fixtures :web_sites
-
- def test_something
- assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
- end
-
-* Added DoubleRenderError exception that'll be raised if render* is called twice #518 [Nicholas Seckar]
-
-* Fixed exceptions occuring after render has been called #1096 [Nicholas Seckar]
-
-* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make " " fail the validation where it didn't before #1309
-
-* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
-
-* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
-
- Conditional validations such as the following are made possible:
- validates_numericality_of :income, :if => :employed?
-
- Conditional validations can also solve the salted login generator problem:
- validates_confirmation_of :password, :if => :new_password?
-
- Using blocks:
- validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
-
-* Fixed use of construct_finder_sql when using :join #1288 [dwlt@dwlt.net]
-
-* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 [Rick Olson]
-
-* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 [Jeremy Kemper]
-
-* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
-
-* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
-
-* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
-
- # SELECT * FROM topics WHERE title IN ('First', 'Second')
- Topic.find_all_by_title(["First", "Second"])
-
-* Added compatibility with camelCase column names for dynamic finders #533 [Dee Zsombor]
-
-* Fixed extraneous comma in count() function that made it not work with joins #1156 [Jarkko Laine/Dee Zsombor]
-
-* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
-
-* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny@mac.com]
-
-* Added encoding and min_messages options for PostgreSQL #1205 [Shugo Maeda]. Configuration example:
-
- development:
- adapter: postgresql
- database: rails_development
- host: localhost
- username: postgres
- password:
- encoding: UTF8
- min_messages: ERROR
-
-* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 [Jamis Buck]
-
-* Added validates_exclusion_of as a negative of validates_inclusion_of
-
-* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
-
-
-*1.10.1* (20th April, 2005)
-
-* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
-
-* Fixed order of loading in eager associations
-
-* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
-
-
-*1.10.0* (19th April, 2005)
-
-* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
-
- for post in Post.find(:all, :limit => 100)
- puts "Post: " + post.title
- puts "Written by: " + post.author.name
- puts "Last comment on: " + post.comments.first.created_on
- end
-
- This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
-
- for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
-
- ...and the number of database queries needed is now 1.
-
-* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
-
- Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
- Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
- Person.find(:first, :order => "created_on DESC", :offset => 5)
- Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
- Person.find(:all, :offset => 10, :limit => 10)
-
-* Added acts_as_nested_set #1000 [wschenk]. Introduction:
-
- This acts provides Nested Set functionality. Nested Set is similiar to Tree, but with
- the added feature that you can select the children and all of it's descendants with
- a single query. A good use case for this is a threaded post system, where you want
- to display every reply to a comment without multiple selects.
-
-* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [Dave Thomas]
-
-* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1@gmail.com]
-
-* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 [andrew.john.peters@gmail.com]
-
-* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 [stephenh@chase3000.com]
-
-* Fixed page caching for non-vhost applications living underneath the root #1004 [Ben Schumacher]
-
-* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 [adelle]
-
-* Added the option to specify the acceptance string in validates_acceptance_of #1106 [caleb@aei-tech.com]
-
-* Added insert_at(position) to acts_as_list #1083 [DeLynnB]
-
-* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
-
-* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 [yon@milliped.com]
-
-* Fixed boolean saving on Oracle #1093 [mparrish@pearware.org]
-
-* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
-
-* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 [Leon Bredt]
-
-* Added quoting of column names for fixtures #997 [jcfischer@gmail.com]
-
-* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
-
-* Fixed that benchmarking times for rendering included db runtimes #987 [Stefan Kaes]
-
-* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave@cherryville.org]
-
-* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 [Jeremy Kemper]
-
-* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave@cherryville.org]
-
-* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [Stefan Kaes]
-
-* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [Stefan Kaes]
-
-* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
-
-* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
-
-
-*1.9.1* (27th March, 2005)
-
-* Fixed that Active Record objects with float attribute could not be cloned #808
-
-* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 [Nicholas Seckar]
-
-* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
-
-* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 [Scott Barron]
-
-* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 [delynnb]
-
-
-*1.9.0* (22th March, 2005)
-
-* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
-
- Developer.find_all nil, 'id ASC', 5 # return the first five developers
- Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
-
- This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
-
-* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
-
-* Improved the performance of the OCI8 adapter for Oracle #723 [pilx/gjenkins]
-
-* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 [dave@cherryville.org]
-
-* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 [mindel]
-
-* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 [Alisdair McDiarmid]
-
-* Added the possibility of specifying fixtures in multiple calls #816 [kim@tinker.com]
-
-* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 [stian@grytoyr.net]
-
-* Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
-
-* Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
-
-* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
-
-* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
-
-* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
-
-
-*1.8.0* (7th March, 2005)
-
-* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
-
-* Added support for timestamp with time zone in PostgreSQL #560 [Scott Barron]
-
-* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
-
- * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
- +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
- objects that should be inspected to determine which attributes triggered the errors.
- * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
- You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
-
-* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 [rodrigo k]
-
-* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 [Jeremy Kemper]
-
-* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
-
- class Account < ActiveRecord::Base
- has_one :credit_card, :dependent => true
- end
- class CreditCard < ActiveRecord::Base
- belongs_to :account
- end
-
- account.credit_card # => returns existing credit card, lets say id = 12
- account.credit_card = CreditCard.create("number" => "123")
- account.save # => CC with id = 12 is destroyed
-
-
-* Added validates_numericality_of #716 [Sebastian Kanthak/Chris McGrath]. Docuemntation:
-
- Validates whether the value of the specified attribute is numeric by trying to convert it to
- a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
- <tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
-
- class Person < ActiveRecord::Base
- validates_numericality_of :value, :on => :create
- end
-
- Configuration options:
- * <tt>message</tt> - A custom error message (default is: "is not a number")
- * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
- * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
-
-
-* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron]
-
-* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
-
-* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 [james@slashetc.com]
-
-
-*1.7.0* (24th February, 2005)
-
-* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 [Jamis Buck]
-
-* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
-
- 1. Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
- and safe way of passing table-specific sequence information to the adapter.)
- 2. Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
- resort to some hacks to get data converted to Date or Time in Ruby.
- If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
- hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
- This is nasty - but if you use Duck Typing you'll probably not care very much.
- In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
- valid - too many databases use DATE for both.
- Timezones and sub-second precision on timestamps are not supported.
- 3. Default values that are functions (such as "SYSDATE") are not supported. This is a
- restriction of the way active record supports default values.
- 4. Referential integrity constraints are not fully supported. Under at least
- some circumstances, active record appears to delete parent and child records out of
- sequence and out of transaction scope. (Or this may just be a problem of test setup.)
-
- The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
-
-* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 [YuriSchimke]
-
-* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 [yerejm]
-
-* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 [tonka]
-
-* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 [Nicholas Seckar]
-
-* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 [daniel@nightrunner.com]
-
-* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 [adelle@bullet.net.au]
-
-* Fixed that find_by_* would fail when column names had numbers #670 [demetrius]
-
-* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
-
- 1. Created a new columns method that is much cleaner.
- 2. Corrected a problem with the select and select_all methods
- that didn't account for the LIMIT clause being passed into raw SQL statements.
- 3. Implemented the string_to_time method in order to create proper instances of the time class.
- 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
- 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
-
-* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow]
-
-* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
-
- class Person < ActiveRecord::Base
- validates_each :first_name, :last_name do |record, attr|
- record.errors.add attr, 'starts with z.' if attr[0] == ?z
- end
- end
-
-* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 [Jeremy Kemper]
-
-* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
-
- class Person < ActiveRecord::Base
- validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
- end
-
-* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 [Tim Bates]
-
-* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
-
- class Project < ActiveRecord::Base
- primary_key "sysid"
- table_name "XYZ_PROJECT"
- inheritance_column { original_inheritance_column + "_id" }
- end
-
-* Fixed Base#clone for use with PostgreSQL #565 [hanson@surgery.wisc.edu]
-
-
-*1.6.0* (January 25th, 2005)
-
-* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
-
-* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
-
-* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
-
-* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
-
- people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
- Person.update(people.keys, people.values)
-
-* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 [Scott Baron]
-
-* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 [notahat]
-
-* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 [Demetrius]
-
-* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 [Jeremy Kemper]
-
-* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 [Eric Anderson]
-
-* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 [Eric Anderson]
-
-* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
-
-* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
-
-* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 [Demetrius]
-
-* Fixed that find_all would produce invalid sql when called sequentialy #490 [Scott Baron]
-
-
-*1.5.1* (January 18th, 2005)
-
-* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]
-
-* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 [Pelle]
-
-
-*1.5.0* (January 17th, 2005)
-
-* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 [Eric Hodel]
-
-* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
-
- class Book < ActiveRecord::Base
- has_many :pages
- belongs_to :library
-
- validates_associated :pages, :library
- end
-
-* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
-
- == Unsaved objects and associations
-
- You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
- aware of, mostly involving the saving of associated objects.
-
- === One-to-one associations
-
- * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
- order to update their primary keys - except if the parent object is unsaved (new_record? == true).
- * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
- is cancelled.
- * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
- * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
- not save the parent either.
-
- === Collections
-
- * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
- (the owner of the collection) is not yet stored in the database.
- * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
- * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
- * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
-
-* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 [Tim Bates]
-
-* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 [Tim Bates]
-
-* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 [Tim Bates]
-
-* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 [Tim Bates]
-
-* Fixed binary support for PostgreSQL #444 [alex@byzantine.no]
-
-* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size 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 less SELECT query if you use length.
-
-* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
-
-* Fixed that foreign keys named the same as the association would cause stack overflow #437 [Eric Anderson]
-
-* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 [Alexey]
-
-* Added Base#reload that reloads the attributes of an object from the database #422 [Andreas Schwarz]
-
-* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
-
-* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
-
-* Added that Observers can use the observes class method instead of overwriting self.observed_class().
-
- Before:
- class ListSweeper < ActiveRecord::Base
- def self.observed_class() [ List, Item ]
- end
-
- After:
- class ListSweeper < ActiveRecord::Base
- observes List, Item
- end
-
-* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
-
-* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
-
-* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
-
- Before: topic.update_attribute(:approved, !approved?)
- After : topic.toggle!(:approved)
-
-* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
-
- page.views # => 1
- page.increment!(:views) # executes an UPDATE statement
- page.views # => 2
-
- page.increment(:views).increment!(:views)
- page.views # => 4
-
-* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
-
-
-
-
-*1.14.2* (April 9th, 2005)
-
-* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
-
-
-*1.14.1* (April 6th, 2006)
-
-* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
-
-* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
-
-* Enable Limit/Offset in Calculations (closes #4558) [lmarlow]
-
-* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick Olson]
-
-* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue@free.fr]
-
-* Allow AR::Base#respond_to? to behave when @attributes is nil [Ryan Davis]
-
-* Support eager includes when going through a polymorphic has_many association. [Rick Olson]
-
-* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick Olson]
-
- class Post < ActiveRecord::Base
- has_one :tagging, :as => :taggable
- end
-
- Post.find :all, :include => :tagging
-
-* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick Olson]
-
-* Added support for going through a polymorphic has_many association: (closes #4401) [Rick Olson]
-
- class PhotoCollection < ActiveRecord::Base
- has_many :photos, :as => :photographic
- belongs_to :firm
- end
-
- class Firm < ActiveRecord::Base
- has_many :photo_collections
- has_many :photos, :through => :photo_collections
- end
-
-* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. [ruben.nine@gmail.com]
-
-* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. [Florian Weber]
-
-* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) [Stefan]
-
-* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick Olson]
-
-* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [David Heinemeier Hansson]
-
-* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [David Heinemeier Hansson]
-
-* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
-
-* Fixed broken OCIAdapter #4457 [Michael Schoen]
-
-
-*1.14.0* (March 27th, 2006)
-
-* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
-
-* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick Olson]
-
-* Add support for :include to with_scope [andrew@redlinesoftware.com]
-
-* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [Michael Schoen]
-
-* Change periods (.) in table aliases to _'s. Closes #4251 [jeff@ministrycentered.com]
-
-* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick Olson]
-
-* Fixed issue that kept :select options from being scoped [Rick Olson]
-
-* Fixed db_schema_import when binary types are present #3101 [David Heinemeier Hansson]
-
-* Fixed that MySQL enums should always be returned as strings #3501 [David Heinemeier Hansson]
-
-* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. [Rick Olson]
-
- class Connection < ActiveRecord::Base
- belongs_to :user
- belongs_to :channel
- end
-
- class Channel < ActiveRecord::Base
- has_many :connections
- has_many :contacts, :through => :connections, :class_name => 'User' # OLD
- has_many :contacts, :through => :connections, :source => :user # NEW
- end
-
-* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 [contact@maik-schmidt.de]
-
-* Allow overriding of find parameters in scoped has_many :through calls [Rick Olson]
-
- In this example, :include => false disables the default eager association from loading. :select changes the standard
- select clause. :joins specifies a join that is added to the end of the has_many :through query.
-
- class Post < ActiveRecord::Base
- has_many :tags, :through => :taggings, :include => :tagging do
- def add_joins_and_select
- find :all, :select => 'tags.*, authors.id as author_id', :include => false,
- :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
- end
- end
- end
-
-* Fixed that schema changes while the database was open would break any connections to an SQLite database (now we reconnect if that error is throw) [David Heinemeier Hansson]
-
-* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [Jonathan Viney]
-
-* Quit ignoring default :include options in has_many :through calls [Mark James]
-
-* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [Jonathan Viney]
-
-* Eager Loading support added for has_many :through => :has_many associations (see below). [Rick Olson]
-
-* Allow has_many :through to work on has_many associations (closes #3864) [sco@scottraymond.net] Example:
-
- class Firm < ActiveRecord::Base
- has_many :clients
- has_many :invoices, :through => :clients
- end
-
- class Client < ActiveRecord::Base
- belongs_to :firm
- has_many :invoices
- end
-
- class Invoice < ActiveRecord::Base
- belongs_to :client
- end
-
-* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [Josh Susser]
-
-* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [Jonathan Viney]
-
-* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick Olson]
-
-* SQL Server adapter gets some love #4298 [Ryan Tomayko]
-
-* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell@cdmplus.com]
-
-* Rework table aliasing to account for truncated table aliases. Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
-
- Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
-
-* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick Olson]
-
-* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Michael Koziarski]
-
-* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Michael Koziarski]
-
-* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom Ward]
-
-* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco@pobox.com]
-
-* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
-
-* Use association's :conditions when eager loading. [Jeremy Evans] #4144
-
-* Alias the has_and_belongs_to_many join table on eager includes. #4106 [Jeremy Evans]
-
- This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
-
- Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
-
-* Oracle adapter gets some love #4230 [Michael Schoen]
-
- * Changes :text to CLOB rather than BLOB [Moses Hohman]
- * Fixes an issue with nil numeric length/scales (several)
- * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
- * Tweaks a unit test to get it all green again
- * Adds support for #current_database
-
-* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 [Rick Olson]
-
- class CachedModel < ActiveRecord::Base
- self.abstract_class = true
- end
-
- class Post < CachedModel
- end
-
- CachedModel.abstract_class?
- => true
-
- Post.abstract_class?
- => false
-
- Post.base_class
- => Post
-
- Post.table_name
- => 'posts'
-
-* Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]
-
- class Foo < ActiveRecord::Base
- has_many :attachments, :as => :attachable, :dependent => :delete_all
- end
-
-* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay]
-
-* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
-
-* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model [Rick Olson]
-
-* Allow ordering of calculated results and/or grouped fields in calculations [solo@gatelys.com]
-
-* Make ActiveRecord::Base#save! return true instead of nil on success. #4173 [johan@johansorensen.com]
-
-* Dynamically set allow_concurrency. #4044 [Stefan Kaes]
-
-* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
-
- topic.to_xml
-
- ...returns:
-
- <?xml version="1.0" encoding="UTF-8"?>
- <topic>
- <title>The First Topic</title>
- <author-name>David</author-name>
- <id type="integer">1</id>
- <approved type="boolean">false</approved>
- <replies-count type="integer">0</replies-count>
- <bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>
- <written-on type="datetime">2003-07-16 09:28:00</written-on>
- <content>Have a nice day</content>
- <author-email-address>david@loudthinking.com</author-email-address>
- <parent-id></parent-id>
- <last-read type="date">2004-04-15</last-read>
- </topic>
-
- ...and you can configure with:
-
- topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
-
- ...that'll return:
-
- <topic>
- <title>The First Topic</title>
- <author-name>David</author-name>
- <approved type="boolean">false</approved>
- <content>Have a nice day</content>
- <author-email-address>david@loudthinking.com</author-email-address>
- <parent-id></parent-id>
- <last-read type="date">2004-04-15</last-read>
- </topic>
-
- You can even do load first-level associations as part of the document:
-
- firm.to_xml :include => [ :account, :clients ]
-
- ...that'll return something like:
-
- <?xml version="1.0" encoding="UTF-8"?>
- <firm>
- <id type="integer">1</id>
- <rating type="integer">1</rating>
- <name>37signals</name>
- <clients>
- <client>
- <rating type="integer">1</rating>
- <name>Summit</name>
- </client>
- <client>
- <rating type="integer">1</rating>
- <name>Microsoft</name>
- </client>
- </clients>
- <account>
- <id type="integer">1</id>
- <credit-limit type="integer">50</credit-limit>
- </account>
- </firm>
-
-* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
-
-* Documentation fixes for :dependent [robby@planetargon.com]
-
-* Stop the MySQL adapter crashing when views are present. #3782 [Jonathan Viney]
-
-* Don't classify the belongs_to class, it is already singular #4117 [keithm@infused.org]
-
-* Allow set_fixture_class to take Classes instead of strings for a class in a module. Raise FixtureClassNotFound if a fixture can't load. [Rick Olson]
-
-* Fix quoting of inheritance column for STI eager loading #4098 [Jonathan Viney <jonathan@bluewire.net.nz>]
-
-* Added smarter table aliasing for eager associations for multiple self joins #3580 [Rick Olson]
-
- * The first time a table is referenced in a join, no alias is used.
- * After that, the parent class name and the reflection name are used.
-
- Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
-
- * Any additional join references get a numerical suffix like '_2', '_3', etc.
-
-* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
-
-* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna@wota.jp]. Examples:
-
- # cascaded in two levels
- >> Author.find(:all, :include=>{:posts=>:comments})
- => authors
- +- posts
- +- comments
-
- # cascaded in two levels and normal association
- >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
- => authors
- +- posts
- +- comments
- +- categorizations
-
- # cascaded in two levels with two has_many associations
- >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
- => authors
- +- posts
- +- comments
- +- categorizations
-
- # cascaded in three levels
- >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
- => companies
- +- groups
- +- members
- +- favorites
-
-* Make counter cache work when replacing an association #3245 [eugenol@gmail.com]
-
-* Make migrations verbose [Jamis Buck]
-
-* Make counter_cache work with polymorphic belongs_to [Jamis Buck]
-
-* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 [anna@wota.jp]
-
-* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
-
- * Does not support DATE SQL column types; use DATETIME instead.
- * Date columns on HABTM join tables are returned as String, not Time.
- * Insertions are potentially broken for :polymorphic join tables
- * BLOB column access not yet fully supported
-
-* Clear stale, cached connections left behind by defunct threads. [Jeremy Kemper]
-
-* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. [Jeremy Kemper]
-
-* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [Michael Schoen]
-
-* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [David Heinemeier Hansson]
-
-* Speed up class -> connection caching and stale connection verification. #3979 [Stefan Kaes]
-
-* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. [Kevin Clark]
-
-* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack@wobblini.net]
-
-* Define attribute query methods to avoid method_missing calls. #3677 [Jonathan Viney]
-
-* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 [Simon Stapleton, Tom Ward]
-
-* Added support for nested scopes #3407 [anna@wota.jp]. Examples:
-
- Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
- Developer.find(:all) # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
-
- # inner rule is used. (all previous parameters are ignored)
- Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
- Developer.find(:all) # => SELECT * FROM developers WHERE (name = 'Jamis')
- end
-
- # parameters are merged
- Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
- Developer.find(:all) # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
- end
- end
-
-* Fixed db2 connection with empty user_name and auth options #3622 [phurley@gmail.com]
-
-* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
-
-* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [Lars Pind]
-
-* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
-
- Person.average :age
- Person.minimum :age
- Person.maximum :age
- Person.sum :salary, :group => :last_name
-
-* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [Luke Redpath]
-
-* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
-
-* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 [Aggregated by schoenm@earthlink.net]
-
-* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails@atyp.de]
-
-* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [Stefan Kaes]
-
-* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first [Bob Silva]
-
-* Allow :include to be used with has_many :through associations #3611 [Michael Schoen]
-
-* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table). #2920 [Blair Zajac]
-
-* SQLServer: more compatible limit/offset emulation. #3779 [Tom Ward]
-
-* Polymorphic join support for has_one associations (has_one :foo, :as => :bar) #3785 [Rick Olson]
-
-* PostgreSQL: correctly parse negative integer column defaults. #3776 [bellis@deepthought.org]
-
-* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
-
-* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Lütke]
-
-* Using AssociationCollection#build with arrays of hashes should call build, not create [David Heinemeier Hansson]
-
-* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
-
-* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
-
-* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [Shugo Maeda]
-
-* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra <m.stienstra@fngtps.com>]
-
-* If the OCI library is not available, raise an exception indicating as much. #3593 [Michael Schoen]
-
-* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
-
-* Make dynamic finders honor additional passed in :conditions. #3569 [Oleg Pudeyev <pudeyo@rpi.edu>, Marcel Molina Jr.]
-
-* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
-
-* Make .count work for has_many associations with multi line finder sql [Michael Schoen]
-
-* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
-
-* Allow configuration of the column used for optimistic locking [wilsonb@gmail.com]
-
-* Don't hardcode 'id' in acts as list. [ror@philippeapril.com]
-
-* Fix date errors for SQLServer in association tests. #3406 [Kevin Clark]
-
-* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna@wota.jp]
-
-* Disambiguate table names for columns in validates_uniqueness_of's WHERE clause. #3423 [alex.borovsky@gmail.com]
-
-* .with_scope imposed create parameters now bypass attr_protected [Tobias Lütke]
-
-* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
-
-* Multiple enhancements and adjustments to DB2 adaptor. #3377 [contact@maik-schmidt.de]
-
-* Sanitize scoped conditions. [Marcel Molina Jr.]
-
-* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [David Heinemeier Hansson]
-
-* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [Tobias Lütke]
-
-* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [Tobias Lütke]
-
-* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
-
-* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
-
-* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
-
-* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
-
-* made .find() work on :through relations. [Tobias Lütke]
-
-* Fix typo in association docs. #3296. [Blair Zajac]
-
-* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
-
-*1.13.2* (December 13th, 2005)
-
-* Become part of Rails 1.0
-
-* MySQL: allow encoding option for mysql.rb driver. [Jeremy Kemper]
-
-* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]. Example:
-
- class Post
- has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
- end
-
- post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
- post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
- post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
-
-* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]
-
-* MySQL: fixes for the bundled mysql.rb driver. #3160 [Justin Forder]
-
-* SQLServer: fix obscure optimistic locking bug. #3068 [kajism@yahoo.com]
-
-* SQLServer: support uniqueidentifier columns. #2930 [keithm@infused.org]
-
-* SQLServer: cope with tables names qualified by owner. #3067 [jeff@ministrycentered.com]
-
-* SQLServer: cope with columns with "desc" in the name. #1950 [Ron Lusk, Ryan Tomayko]
-
-* SQLServer: cope with primary keys with "select" in the name. #3057 [rdifrango@captechventures.com]
-
-* Oracle: active? performs a select instead of a commit. #3133 [Michael Schoen]
-
-* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
-
-* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausoleil]
-
-* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
-
-* PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
-
-* Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
-
-* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
-
-* Added preliminary support for polymorphic associations [David Heinemeier Hansson]
-
-* Added preliminary support for join models [David Heinemeier Hansson]
-
-* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. [jeremy@jthopple.com, Marcel Molina Jr.]
-
-* Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz <kennethkunz@gmail.com>]
-
-* Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz <kennethkunz@gmail.com>]
-
-* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
-
-* Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
-
-* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
-
-* Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
-
-* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
-
-* MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. #2978 [Shugo Maeda]
-
-* has_and_belongs_to_many: use JOIN instead of LEFT JOIN. [Jeremy Kemper]
-
-* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
-
-* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
-
-* Correct boolean handling in generated reader methods. #2945 [Don Park, Stefan Kaes]
-
-* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
-
-* Document :force option to create_table. #2921 [Blair Zajac <blair@orcaware.com>]
-
-* Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz <kennethkunz@gmail.com>]
-
-* SQLServer: active? and reconnect! methods for handling stale connections. #428 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
-
-* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array. #1345 [MarkusQ@reality.com]
-
-* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
-
-* Oracle: active? and reconnect! methods for handling stale connections. Optionally retry queries after reconnect. #428 [Michael Schoen <schoenm@earthlink.net>]
-
-* Correct documentation for Base.delete_all. #1568 [Newhydra]
-
-* Oracle: test case for column default parsing. #2788 [Michael Schoen <schoenm@earthlink.net>]
-
-* Update documentation for Migrations. #2861 [Tom Werner <tom@cube6media.com>]
-
-* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? #428 [Jeremy Kemper]
-
-* Oracle: Much faster column reflection. #2848 [Michael Schoen <schoenm@earthlink.net>]
-
-* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
-
-* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
-
-* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
-
-* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
-
-* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
-
-* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]
-
-* Don't cast nil or empty strings to a dummy date. #2789 [Rick Bradley <rick@rickbradley.com>]
-
-* acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 [rephorm@rephorm.com]
-
-* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
-
-* Fixed acts_as_list for definitions without an explicit :order #2803 [Jonathan Viney]
-
-* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper]
-
-* Correct handling of complex order clauses with SQL Server limit emulation. #2770 [Tom Ward <tom@popdog.net>, Matt B.]
-
-* Correct whitespace problem in Oracle default column value parsing. #2788 [rick@rickbradley.com]
-
-* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 [larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper]
-
-* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. [Jeremy Kemper]
-
-* More compatible Oracle column reflection. #2771 [Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>]
-
-
-*1.13.0* (November 7th, 2005)
-
-* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
-
-* Added :include as an option for association declarations [David Heinemeier Hansson]. Example:
-
- has_many :posts, :include => [ :author, :comments ]
-
-* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints. Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes. [Jeremy Kemper] Example:
-
- Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
- # Find where name = ? and active=true
- Comment.find :all, :conditions => ['name = ?', name]
- # Create comment associated with :post_id
- Comment.create :body => "Hello world"
- end
-
-* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
-
-* Added constrain scoping for creates using a hash of attributes bound to the :creation key [David Heinemeier Hansson]. Example:
-
- Comment.constrain(:creation => { :post_id => 5 }) do
- # Associated with :post_id
- Comment.create :body => "Hello world"
- end
-
- This is rarely used directly, but allows for find_or_create on associations. So you can do:
-
- # If the tag doesn't exist, a new one is created that's associated with the person
- person.tags.find_or_create_by_name("Summer")
-
-* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [David Heinemeier Hansson]. Example:
-
- # No 'Summer' tag exists
- Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
-
- # Now the 'Summer' tag does exist
- Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
-
-* Added extension capabilities to has_many and has_and_belongs_to_many proxies [David Heinemeier Hansson]. Example:
-
- class Account < ActiveRecord::Base
- has_many :people do
- def find_or_create_by_name(name)
- first_name, *last_name = name.split
- last_name = last_name.join " "
-
- find_or_create_by_first_name_and_last_name(first_name, last_name)
- end
- end
- end
-
- person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
- person.first_name # => "David"
- person.last_name # => "Heinemeier Hansson"
-
- Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
-
-* Omit internal dtproperties table from SQLServer table list. #2729 [Ryan Tomayko]
-
-* Quote column names in generated SQL. #2728 [Ryan Tomayko]
-
-* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 [Jeremy Kemper]
-
-* Add Model.create! to match existing model.save! method. When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors). [Jeremy Kemper]
-
-* Correct fixture behavior when table name pluralization is off. #2719 [Rick Bradley <rick@rickbradley.com>]
-
-* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 [Dan Peterson]
-
-* Added migration support for Oracle #2647 [Michael Schoen]
-
-* Worked around that connection can't be reset if allow_concurrency is off. #2648 [Michael Schoen <schoenm@earthlink.net>]
-
-* Fixed SQL Server adapter to pass even more tests and do even better #2634 [Ryan Tomayko]
-
-* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
-
-* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 [Tom Ward]
-
-* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility. #2618 [dansketcher@gmail.com]
-
-* Constraints are cloned so they can't be inadvertently modified while they're
-in effect. Added :readonly finder constraint. Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true. [Jeremy Kemper <rails@bitsweat.net>]
-
-* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson@gmail.com]
-
-* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [Michael Schoen]
-
-* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
-
-
-*1.12.2* (October 26th, 2005)
-
-* Allow symbols to rename columns when using SQLite adapter. #2531 [Kevin Clark]
-
-* Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell <robby@planetargon.com>]
-
-* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [Stefan Kaes]
-
-* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz <patrick@lenz.sh>]
-
-* HABTM finders shouldn't return readonly records. #2525 [Patrick Lenz <patrick@lenz.sh>]
-
-* Make all tests runnable on their own. #2521. [Blair Zajac <blair@orcaware.com>]
-
-
-*1.12.1* (October 19th, 2005)
-
-* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
-
-* Correct PostgreSQL primary key sequence detection. #2507 [tmornini@infomania.com]
-
-* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
-
-
-*1.12.0* (October 16th, 2005)
-
-* Update/clean up documentation (rdoc)
-
-* PostgreSQL sequence support. Use set_sequence_name in your model class to specify its primary key sequence. #2292 [Rick Olson <technoweenie@gmail.com>, Robby Russell <robby@planetargon.com>]
-
-* Change default logging colors to work on both white and black backgrounds. [Sam Stephenson]
-
-* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net]
-
-* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell <robby@planetargon.com>]
-
-* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
-
-* Avoid memleak in dev mode when using fcgi
-
-* Simplified .clear on active record associations by using the existing delete_records method. #1906 [Caleb <me@cpb.ca>]
-
-* Delegate access to a customized primary key to the conventional id method. #2444. [Blair Zajac <blair@orcaware.com>]
-
-* Fix errors caused by assigning a has-one or belongs-to property to itself
-
-* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped [Sam Stephenson]
-
-* Update DB2 adapter. #2206. [contact@maik-schmidt.de]
-
-* Corrections to SQLServer native data types. #2267. [rails.20.clarry@spamgourmet.com]
-
-* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
-
-* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. [Blair Zajac <blair@orcaware.com>]
-
-* Misc doc fixes (typos/grammar/etc.). #2430. [coffee2code]
-
-* Add test coverage for content_columns. #2432. [coffee2code]
-
-* Speed up for unthreaded environments. #2431. [Stefan Kaes]
-
-* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [Stefan Kaes]
-
-* Speed up the setting of table_name. #2428. [Stefan Kaes]
-
-* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig <michael@schuerig.de>]
-
-* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig <michael@schuerig.de>]
-
-* Add geometric type for postgresql adapter. #2233 [Andrew Kaspick]
-
-* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [Stefan Kaes]
-
-* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 [Chad Fowler <chad@chadfowler.com>, Nicholas Seckar]
-
-* Added :force option to create_table that'll try to drop the table if it already exists before creating
-
-* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. [Nicholas Seckar]
-
-* Use foreign_key inflection uniformly. #2156 [Blair Zajac <blair@orcaware.com>]
-
-* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys. #2221 [joergd@pobox.com, ObieFernandez <obiefernandez@gmail.com>]
-
-* Returning false from before_destroy should cancel the action. #1829 [Jeremy Huffman]
-
-* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type. #2256 [mat <mat@absolight.fr>]
-
-* Extensive documentation for the abstract database adapter. #2250 [François Beausoleil <fbeausoleil@ftml.net>]
-
-* Clean up Fixtures.reset_sequences for PostgreSQL. Handle tables with no rows and models with custom primary keys. #2174, #2183 [jay@jay.fm, Blair Zajac <blair@orcaware.com>]
-
-* Improve error message when nil is assigned to an attr which validates_size_of within a range. #2022 [Manuel Holtgrewe <purestorm@ggnore.net>]
-
-* Make update_attribute use the same writer method that update_attributes uses.
- #2237 [trevor@protocool.com]
-
-* Make migrations honor table name prefixes and suffixes. #2298 [Jakob Skjerning, Marcel Molina Jr.]
-
-* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 [dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org]
-
-* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id. #1749 [chris@chrisbrinker.com]
-
-* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
-
-* Added new symbol-driven approach to activating observers with Base#observers= [David Heinemeier Hansson]. Example:
-
- ActiveRecord::Base.observers = :cacher, :garbage_collector
-
-* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 [solo@gatelys.com]
-
-* Wrap :conditions in parentheses to prevent problems with OR's #1871 [Jamis Buck]
-
-* Allow the postgresql adapter to work with the SchemaDumper. [Jamis Buck]
-
-* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. [Jamis Buck]
-
-* Fixed migrations for Windows when using more than 10 [David Naseby]
-
-* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
-
-* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Lütke]
-
-* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
-
-* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
-
-* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina Jr.]
-
-* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
-
-* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
-
-* Added better exception error when unknown column types are used with migrations #1814 [François Beausoleil]
-
-* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism@yahoo.com]
-
-* Fixed comparison of Active Record objects so two new objects are not equal #2099 [deberg]
-
-* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 [Tom Ward]
-
-* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 [coffee2code]
-
-* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 [sd@notso.net]
-
-* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
-
-* Make sure the schema_info table is created before querying the current version #1903
-
-* Fixtures ignore table name prefix and suffix #1987 [Jakob Skjerning]
-
-* Add documentation for index_type argument to add_index method for migrations #2005 [Blaine]
-
-* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
-
-* Make destroy return self #1913 [Sebastian Kanthak]
-
-* Fix typo in validations documentation #1938 [court3nay]
-
-* Make acts_as_list work for insert_at(1) #1966 [hensleyl@papermountain.org]
-
-* Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
-
-* Allow add_column and create_table to specify NOT NULL #1712 [emptysands@gmail.com]
-
-* Fix create_table so that id column is implicitly added [Rick Olson]
-
-* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
-
-* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
-
-* Fixed the handling of camelCase columns names in Oracle #1798
-
-* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
-
-* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
-
-* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Lütke]
-
- class Comment < AR:B
- def self.search(q)
- find(:all, :conditions => ["body = ?", q])
- end
- end
-
- class Post < AR:B
- has_many :comments
- end
-
- Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
-
- NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
- by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
- details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
- noticed :)
-
-* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 [Sam Stephenson]
-
-* Remove extra definition of supports_migrations? from abstract_adaptor.rb [Nicholas Seckar]
-
-* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
-
-* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 [Maik Schmidt]
-
-* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 [Stefan Kaes]
-
-
-*1.11.1* (11 July, 2005)
-
-* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olson]
-
-* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler@kianta.com]
-
-* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
-
-* Correct reflected table name for singular associations. #1688 [court3nay]
-
-* Fixed optimistic locking with SQL Server #1660 [tom@popdog.net]
-
-* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
-
-* Added better error message for "packets out of order" #1630 [court3nay]
-
-* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
-
-
-*1.11.0* (6 July, 2005)
-
-* Fixed that Yaml error message in fixtures hid the real error #1623 [Nicholas Seckar]
-
-* Changed logging of SQL statements to use the DEBUG level instead of INFO
-
-* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Lütke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
-
-* Added callback hooks to association collections #1549 [Florian Weber]. Example:
-
- class Project
- has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
-
- def evaluate_velocity(developer)
- ...
- end
- end
-
- ..raising an exception will cause the object not to be added (or removed, with before_remove).
-
-
-* Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry]
-
-* Fixed Base#write_attribute to work with both symbols and strings #1190 [Paul Legato]
-
-* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 [Florian Weber]
-
-* Speed up ActiveRecord#method_missing for the common case (read_attribute).
-
-* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [Stefan Kaes]
-
-* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
-
-* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel@onemail.at]
-
-* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [Michael Schuerig]
-
-* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
-
-* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry]
-
-* Fixed that multiparameter posts ignored attr_protected #1532 [alec+rails@veryclever.net]
-
-* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 [flash@vanklinkenbergsoftware.nl]
-
-* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be:
-
- Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
-
- ...should instead be:
-
- Developer.find(
- :all,
- :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
- :conditions => 'project_id=1'
- )
-
-* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.]
-
-* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
-
-* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
-
-* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 [Alisdair McDiarmid]
-
-* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
-
-* Allow any Enumerable, not just Array, to work as bind variables #1344 [Jeremy Kemper]
-
-* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
- Example:
-
- david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
- david.save
-
- If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
- project is saved when david.save is called.
-
- Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
-
- david.project_ids = [1, 5, 7]
-
-* Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com]
-
-* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Lütke]
-
-* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 [dave@cherryville.org]
-
-* Corrected @@configurations typo #1410 [david@ruppconsulting.com]
-
-* Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com]
-
-* Allow before/after update hooks to work on models using optimistic locking
-
-* Eager loading of dependent has_one associations won't delete the association #1212
-
-* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
-
-* Using transactional fixtures now causes the data to be loaded only once.
-
-* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
-
- fixtures :web_sites
-
- def test_something
- assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
- end
-
-* Added DoubleRenderError exception that'll be raised if render* is called twice #518 [Nicholas Seckar]
-
-* Fixed exceptions occuring after render has been called #1096 [Nicholas Seckar]
-
-* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make " " fail the validation where it didn't before #1309
-
-* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
-
-* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
-
- Conditional validations such as the following are made possible:
- validates_numericality_of :income, :if => :employed?
-
- Conditional validations can also solve the salted login generator problem:
- validates_confirmation_of :password, :if => :new_password?
-
- Using blocks:
- validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
-
-* Fixed use of construct_finder_sql when using :join #1288 [dwlt@dwlt.net]
-
-* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 [Rick Olson]
-
-* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 [Jeremy Kemper]
-
-* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
-
-* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
-
-* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
-
- # SELECT * FROM topics WHERE title IN ('First', 'Second')
- Topic.find_all_by_title(["First", "Second"])
-
-* Added compatibility with camelCase column names for dynamic finders #533 [Dee Zsombor]
-
-* Fixed extraneous comma in count() function that made it not work with joins #1156 [Jarkko Laine/Dee Zsombor]
-
-* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
-
-* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny@mac.com]
-
-* Added encoding and min_messages options for PostgreSQL #1205 [Shugo Maeda]. Configuration example:
-
- development:
- adapter: postgresql
- database: rails_development
- host: localhost
- username: postgres
- password:
- encoding: UTF8
- min_messages: ERROR
-
-* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 [Jamis Buck]
-
-* Added validates_exclusion_of as a negative of validates_inclusion_of
-
-* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
-
-
-*1.10.1* (20th April, 2005)
-
-* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
-
-* Fixed order of loading in eager associations
-
-* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
-
-
-*1.10.0* (19th April, 2005)
-
-* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
-
- for post in Post.find(:all, :limit => 100)
- puts "Post: " + post.title
- puts "Written by: " + post.author.name
- puts "Last comment on: " + post.comments.first.created_on
- end
-
- This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
-
- for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
-
- ...and the number of database queries needed is now 1.
-
-* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
-
- Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
- Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
- Person.find(:first, :order => "created_on DESC", :offset => 5)
- Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
- Person.find(:all, :offset => 10, :limit => 10)
-
-* Added acts_as_nested_set #1000 [wschenk]. Introduction:
-
- This acts provides Nested Set functionality. Nested Set is similiar to Tree, but with
- the added feature that you can select the children and all of it's descendants with
- a single query. A good use case for this is a threaded post system, where you want
- to display every reply to a comment without multiple selects.
-
-* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [Dave Thomas]
-
-* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1@gmail.com]
-
-* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 [andrew.john.peters@gmail.com]
-
-* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 [stephenh@chase3000.com]
-
-* Fixed page caching for non-vhost applications living underneath the root #1004 [Ben Schumacher]
-
-* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 [adelle]
-
-* Added the option to specify the acceptance string in validates_acceptance_of #1106 [caleb@aei-tech.com]
-
-* Added insert_at(position) to acts_as_list #1083 [DeLynnB]
-
-* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
-
-* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 [yon@milliped.com]
-
-* Fixed boolean saving on Oracle #1093 [mparrish@pearware.org]
-
-* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
-
-* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 [Leon Bredt]
-
-* Added quoting of column names for fixtures #997 [jcfischer@gmail.com]
-
-* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
-
-* Fixed that benchmarking times for rendering included db runtimes #987 [Stefan Kaes]
-
-* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave@cherryville.org]
-
-* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 [Jeremy Kemper]
-
-* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave@cherryville.org]
-
-* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [Stefan Kaes]
-
-* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [Stefan Kaes]
-
-* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
-
-* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
-
-
-*1.9.1* (27th March, 2005)
-
-* Fixed that Active Record objects with float attribute could not be cloned #808
-
-* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 [Nicholas Seckar]
-
-* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
-
-* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 [Scott Barron]
-
-* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 [delynnb]
-
-
-*1.9.0* (22th March, 2005)
-
-* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
-
- Developer.find_all nil, 'id ASC', 5 # return the first five developers
- Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
-
- This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
-
-* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
-
-* Improved the performance of the OCI8 adapter for Oracle #723 [pilx/gjenkins]
-
-* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 [dave@cherryville.org]
-
-* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 [mindel]
-
-* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 [Alisdair McDiarmid]
-
-* Added the possibility of specifying fixtures in multiple calls #816 [kim@tinker.com]
-
-* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 [stian@grytoyr.net]
-
-* Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
-
-* Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
-
-* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
-
-* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
-
-* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
-
-
-*1.8.0* (7th March, 2005)
-
-* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
-
-* Added support for timestamp with time zone in PostgreSQL #560 [Scott Barron]
-
-* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
-
- * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
- +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
- objects that should be inspected to determine which attributes triggered the errors.
- * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
- You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
-
-* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 [rodrigo k]
-
-* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 [Jeremy Kemper]
-
-* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
-
- class Account < ActiveRecord::Base
- has_one :credit_card, :dependent => true
- end
- class CreditCard < ActiveRecord::Base
- belongs_to :account
- end
-
- account.credit_card # => returns existing credit card, lets say id = 12
- account.credit_card = CreditCard.create("number" => "123")
- account.save # => CC with id = 12 is destroyed
-
-
-* Added validates_numericality_of #716 [Sebastian Kanthak/Chris McGrath]. Docuemntation:
-
- Validates whether the value of the specified attribute is numeric by trying to convert it to
- a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
- <tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
-
- class Person < ActiveRecord::Base
- validates_numericality_of :value, :on => :create
- end
-
- Configuration options:
- * <tt>message</tt> - A custom error message (default is: "is not a number")
- * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
- * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
-
-
-* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron]
-
-* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
-
-* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 [james@slashetc.com]
-
-
-*1.7.0* (24th February, 2005)
-
-* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 [Jamis Buck]
-
-* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
-
- 1. Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
- and safe way of passing table-specific sequence information to the adapter.)
- 2. Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
- resort to some hacks to get data converted to Date or Time in Ruby.
- If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
- hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
- This is nasty - but if you use Duck Typing you'll probably not care very much.
- In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
- valid - too many databases use DATE for both.
- Timezones and sub-second precision on timestamps are not supported.
- 3. Default values that are functions (such as "SYSDATE") are not supported. This is a
- restriction of the way active record supports default values.
- 4. Referential integrity constraints are not fully supported. Under at least
- some circumstances, active record appears to delete parent and child records out of
- sequence and out of transaction scope. (Or this may just be a problem of test setup.)
-
- The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
-
-* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 [YuriSchimke]
-
-* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 [yerejm]
-
-* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 [tonka]
-
-* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 [Nicholas Seckar]
-
-* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 [daniel@nightrunner.com]
-
-* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 [adelle@bullet.net.au]
-
-* Fixed that find_by_* would fail when column names had numbers #670 [demetrius]
-
-* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
-
- 1. Created a new columns method that is much cleaner.
- 2. Corrected a problem with the select and select_all methods
- that didn't account for the LIMIT clause being passed into raw SQL statements.
- 3. Implemented the string_to_time method in order to create proper instances of the time class.
- 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
- 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
-
-* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow]
-
-* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
-
- class Person < ActiveRecord::Base
- validates_each :first_name, :last_name do |record, attr|
- record.errors.add attr, 'starts with z.' if attr[0] == ?z
- end
- end
-
-* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 [Jeremy Kemper]
-
-* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
-
- class Person < ActiveRecord::Base
- validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
- end
-
-* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 [Tim Bates]
-
-* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
-
- class Project < ActiveRecord::Base
- primary_key "sysid"
- table_name "XYZ_PROJECT"
- inheritance_column { original_inheritance_column + "_id" }
- end
-
-* Fixed Base#clone for use with PostgreSQL #565 [hanson@surgery.wisc.edu]
-
-
-*1.6.0* (January 25th, 2005)
-
-* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
-
-* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
-
-* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
-
-* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
-
- people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
- Person.update(people.keys, people.values)
-
-* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 [Scott Baron]
-
-* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 [notahat]
-
-* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 [Demetrius]
-
-* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 [Jeremy Kemper]
-
-* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 [Eric Anderson]
-
-* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 [Eric Anderson]
-
-* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
-
-* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
-
-* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 [Demetrius]
-
-* Fixed that find_all would produce invalid sql when called sequentialy #490 [Scott Baron]
-
-
-*1.5.1* (January 18th, 2005)
-
-* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]
-
-* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 [Pelle]
-
-
-*1.5.0* (January 17th, 2005)
-
-* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 [Eric Hodel]
-
-* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
-
- class Book < ActiveRecord::Base
- has_many :pages
- belongs_to :library
-
- validates_associated :pages, :library
- end
-
-* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
-
- == Unsaved objects and associations
-
- You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
- aware of, mostly involving the saving of associated objects.
-
- === One-to-one associations
-
- * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
- order to update their primary keys - except if the parent object is unsaved (new_record? == true).
- * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
- is cancelled.
- * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
- * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
- not save the parent either.
-
- === Collections
-
- * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
- (the owner of the collection) is not yet stored in the database.
- * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
- * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
- * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
-
-* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 [Tim Bates]
-
-* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 [Tim Bates]
-
-* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 [Tim Bates]
-
-* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 [Tim Bates]
-
-* Fixed binary support for PostgreSQL #444 [alex@byzantine.no]
-
-* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size 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 less SELECT query if you use length.
-
-* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
-
-* Fixed that foreign keys named the same as the association would cause stack overflow #437 [Eric Anderson]
-
-* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 [Alexey]
-
-* Added Base#reload that reloads the attributes of an object from the database #422 [Andreas Schwarz]
-
-* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
-
-* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
-
-* Added that Observers can use the observes class method instead of overwriting self.observed_class().
-
- Before:
- class ListSweeper < ActiveRecord::Base
- def self.observed_class() [ List, Item ]
- end
-
- After:
- class ListSweeper < ActiveRecord::Base
- observes List, Item
- end
-
-* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
-
-* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
-
-* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
-
- Before: topic.update_attribute(:approved, !approved?)
- After : topic.toggle!(:approved)
-
-* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
-
- page.views # => 1
- page.increment!(:views) # executes an UPDATE statement
- page.views # => 2
-
- page.increment(:views).increment!(:views)
- page.views # => 4
-
-* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
-
-
-*1.4.0* (January 4th, 2005)
-
-* Added automated optimistic locking if the field <tt>lock_version</tt> is present. Each update to the
- record increments the lock_version column and the locking facilities ensure that records instantiated twice
- will let the last one saved raise a StaleObjectError if the first was also updated. Example:
-
- p1 = Person.find(1)
- p2 = Person.find(1)
-
- p1.first_name = "Michael"
- p1.save
-
- p2.first_name = "should fail"
- p2.save # Raises a ActiveRecord::StaleObjectError
-
- You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
- or otherwise apply the business logic needed to resolve the conflict.
-
- #384 [Michael Koziarski]
-
-* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL.
- They work by appending the name of an attribute to <tt>find_by_</tt>, so you get finders like <tt>Person.find_by_user_name,
- Payment.find_by_transaction_id</tt>. So instead of writing <tt>Person.find_first(["user_name = ?", user_name])</tt>, you just do
- <tt>Person.find_by_user_name(user_name)</tt>.
-
- It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
- <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
- <tt>Person.find_first(["user_name = ? AND password = ?", user_name, password])</tt>, you just do
- <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
-
- While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like
- <tt>Payment.find_all_by_amount(50)</tt> that is turned into <tt>Payment.find_all(["amount = ?", 50])</tt>. This is something not as equally useful,
- though, as it's not possible to specify the order in which the objects are returned.
-
-* Added block-style for callbacks #332 [Jeremy Kemper].
-
- Before:
- before_destroy(Proc.new{ |record| Person.destroy_all "firm_id = #{record.id}" })
-
- After:
- before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
-
-* Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 [Josh Peek]
-
-* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates
- and times from the database. This is set to :local by default.
-
-* Added the possibility for adapters to overwrite add_limit! to implement a different limiting scheme than "LIMIT X" used by MySQL, PostgreSQL, and SQLite.
-
-* Added the possibility of having objects with acts_as_list created before their scope is available or...
-
-* Added a db2 adapter that only depends on the Ruby/DB2 bindings (http://raa.ruby-lang.org/project/ruby-db2/) #386 [Maik Schmidt]
-
-* Added the final touches to the Microsoft SQL Server adapter by Joey Gibson that makes it suitable for actual use #394 [DeLynn Barry]
-
-* Added that Base#find takes an optional options hash, including :conditions. Base#find_on_conditions deprecated in favor of #find with :conditions #407 [Jeremy Kemper]
-
-* Added HasManyAssociation#count that works like Base#count #413 [intinig]
-
-* Fixed handling of binary content in blobs and similar fields for Ruby/MySQL and SQLite #409 [xal]
-
-* Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 [Tobias Lütke]
-
-* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it.
- If require_association did not set the constant then const_get will call const_missing, resulting in an infinite loop #380 [Jeremy Kemper]
-
-* Fixed broken transactions that were actually only running object-level and not db level transactions [andreas]
-
-* Fixed that validates_uniqueness_of used 'id' instead of defined primary key #406
-
-* Fixed that the overwritten respond_to? method didn't take two parameters like the original #391
-
-* Fixed quoting in validates_format_of that would allow some rules to pass regardless of input #390 [Dmitry V. Sabanin]
-
-
-*1.3.0* (December 23, 2004)
-
-* Added a require_association hook on const_missing that makes it possible to use any model class without requiring it first. This makes STI look like:
-
- before:
- require_association 'person'
- class Employee < Person
- end
-
- after:
- class Employee < Person
- end
-
- This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes.
-
-* Added that Base.update_all and Base.delete_all return an integer of the number of affected rows #341
-
-* Added scope option to validation_uniqueness #349 [Kent Sibilev]
-
-* Added respondence to *_before_type_cast for all attributes to return their string-state before they were type casted by the column type.
- This is helpful for getting "100,000" back on a integer-based validation where the value would normally be "100".
-
-* Added allow_nil options to validates_inclusion_of so that validation is only triggered if the attribute is not nil [what-a-day]
-
-* Added work-around for PostgreSQL and the problem of getting fixtures to be created from id 1 on each test case.
- This only works for auto-incrementing primary keys called "id" for now #359 [Scott Baron]
-
-* Added Base#clear_association_cache to empty all the cached associations #347 [Tobias Lütke]
-
-* Added more informative exceptions in establish_connection #356 [Jeremy Kemper]
-
-* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise).
-
- Before:
- person.attributes = @params["person"]
- person.save
-
- Now:
- person.update_attributes(@params["person"])
-
-* Added Base.destroy and Base.delete to remove records without holding a reference to them first.
-
-* Added that query benchmarking will only happen if its going to be logged anyway #344
-
-* Added higher_item and lower_item as public methods for acts_as_list #342 [Tobias Lütke]
-
-* Fixed that options[:counter_sql] was overwritten with interpolated sql rather than original sql #355 [Jeremy Kemper]
-
-* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used
- the attributes[] hash instead of checking for @base.respond_to?(attr.to_s). [Marten]
-
-* Fixed that Base.table_name would expect a parameter when used in has_and_belongs_to_many joins [Anna Lissa Cruz]
-
-* Fixed that nested transactions now work by letting the outer most transaction have the responsibilty of starting and rolling back the transaction.
- If any of the inner transactions swallow the exception raised, though, the transaction will not be rolled back. So always let the transaction
- bubble up even when you've dealt with local issues. Closes #231 and #340.
-
-* Fixed validates_{confirmation,acceptance}_of to only happen when the virtual attributes are not nil #348 [dpiddy@gmail.com]
-
-* Changed the interface on AbstractAdapter to require that adapters return the number of affected rows on delete and update operations.
-
-* Fixed the automated timestamping feature when running under Rails' development environment that resets the inheritable attributes on each request.
-
-
-
-*1.2.0*
-
-* Added Base.validates_inclusion_of that validates whether the value of the specified attribute is available in a particular enumerable
- object. [what-a-day]
-
- class Person < ActiveRecord::Base
- validates_inclusion_of :gender, :in=>%w( m f ), :message=>"woah! what are you then!??!!"
- validates_inclusion_of :age, :in=>0..99
- end
-
-* Added acts_as_list that can decorates an existing class with methods like move_higher/lower, move_to_top/bottom. [Tobias Lütke] Example:
-
- class TodoItem < ActiveRecord::Base
- acts_as_list :scope => :todo_list_id
- belongs_to :todo_list
- end
-
-* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in
- categories and the likes. [Tobias Lütke]
-
-* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names
- created_at/created_on or updated_at/updated_on are present. [Tobias Lütke]
-
-* Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place [Tobias Lütke]
-
-* Added automatic transaction block around AssociationCollection.<<, AssociationCollection.delete, and AssociationCollection.destroy_all
-
-* Fixed that Base#find will return an array if given an array -- regardless of the number of elements #270 [Marten]
-
-* Fixed that has_and_belongs_to_many would generate bad sql when naming conventions differed from using vanilla "id" everywhere [RedTerror]
-
-* Added a better exception for when a type column is used in a table without the intention of triggering single-table inheritance. Example:
-
- ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'bad_class!'.
- This error is raised because the column 'type' is reserved for storing the class in case of inheritance.
- Please rename this column if you didn't intend it to be used for storing the inheritance class or
- overwrite Company.inheritance_column to use another column for that information.
-
-* Added that single-table inheritance will only kick in if the inheritance_column (by default "type") is present. Otherwise, inheritance won't
- have any magic side effects.
-
-* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 [mjobin]
-
-* Fixed Base.errors to be indifferent as to whether strings or symbols are used. Examples:
-
- Before:
- errors.add(:name, "must be shorter") if name.size > 10
- errors.on(:name) # => "must be shorter"
- errors.on("name") # => nil
-
- After:
- errors.add(:name, "must be shorter") if name.size > 10
- errors.on(:name) # => "must be shorter"
- errors.on("name") # => "must be shorter"
-
-* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching
- it against the regular expression provided. [Marcel Molina Jr.]
-
- class Person < ActiveRecord::Base
- validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :on => :create
- end
-
-* Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Lütke]. Example:
-
- Validates that the specified attribute matches the length restrictions supplied in either:
-
- - configuration[:minimum]
- - configuration[:maximum]
- - configuration[:is]
- - configuration[:within] (aka. configuration[:in])
-
- Only one option can be used at a time.
-
- class Person < ActiveRecord::Base
- validates_length_of :first_name, :maximum=>30
- validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
- validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
- validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
- validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
- end
-
-* Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself.
-
-* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system.
- Useful for making sure that only one user can be named "davidhh".
-
- class Person < ActiveRecord::Base
- validates_uniqueness_of :user_name
- end
-
- When the record is created, a check is performed to make sure that no record exist in the database with the given value for the specified
- attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
-
-
-* Added Base.validates_confirmation_of that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
-
- Model:
- class Person < ActiveRecord::Base
- validates_confirmation_of :password
- end
-
- View:
- <%= password_field "person", "password" %>
- <%= password_field "person", "password_confirmation" %>
-
- The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
- It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
-
-
-* Added Base.validates_acceptance_of that encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
-
- class Person < ActiveRecord::Base
- validates_acceptance_of :terms_of_service
- end
-
- The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
-
- NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
-
-
-* Added validation macros to make the stackable just like the life cycle callbacks. Examples:
-
- class Person < ActiveRecord::Base
- validate { |record| record.errors.add("name", "too short") unless name.size > 10 }
- validate { |record| record.errors.add("name", "too long") unless name.size < 20 }
- validate_on_create :validate_password
-
- private
- def validate_password
- errors.add("password", "too short") unless password.size > 6
- end
- end
-
-* Added the option for sanitizing find_by_sql and the offset parts in regular finds [Sam Stephenson]. Examples:
-
- Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
- Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
-
-* Fixed value quoting in all generated SQL statements, so that integers are not surrounded in quotes and that all sanitation are happening
- through the database's own quoting routine. This should hopefully make it lots easier for new adapters that doesn't accept '1' for integer
- columns.
-
-* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient
- [Florian Weber]
-
-* Added counter_sql option for has_many associations [Jeremy Kemper]. Documentation:
-
- <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
- specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
-
-* Fixed that methods wrapped in callbacks still return their original result #260 [Jeremy Kemper]
-
-* Fixed the Inflector to handle the movie/movies pair correctly #261 [Scott Baron]
-
-* Added named bind-style variable interpolation #281 [Michael Koziarski]. Example:
-
- Person.find(["id = :id and first_name = :first_name", { :id => 5, :first_name = "bob' or 1=1" }])
-
-* Added bind-style variable interpolation for the condition arrays that uses the adapter's quote method [Michael Koziarski]
-
- Before:
- find_first([ "user_name = '%s' AND password = '%s'", user_name, password ])]
- find_first([ "firm_id = %s", firm_id ])] # unsafe!
-
- After:
- find_first([ "user_name = ? AND password = ?", user_name, password ])]
- find_first([ "firm_id = ?", firm_id ])]
-
-* Added CSV format for fixtures #272 [what-a-day]. (See the new and expanded documentation on fixtures for more information)
-
-* Fixed fixtures using primary key fields called something else than "id" [dave]
-
-* Added proper handling of time fields that are turned into Time objects with the dummy date of 2000/1/1 [HariSeldon]
-
-* Added reverse order of deleting fixtures, so referential keys can be maintained #247 [Tim Bates]
-
-* Added relative path search for sqlite dbfiles in database.yml (if RAILS_ROOT is defined) #233 [Jeremy Kemper]
-
-* Added option to establish_connection where you'll be able to leave out the parameter to have it use the RAILS_ENV environment variable
-
-* Fixed problems with primary keys and postgresql sequences (#230) [Tim Bates]
-
-* Added reloading for associations under cached environments like FastCGI and mod_ruby. This makes it possible to use those environments for development.
- This is turned on by default, but can be turned off with ActiveRecord::Base.reload_dependencies = false in production environments.
-
- NOTE: This will only have an effect if you let the associations manage the requiring of model classes. All libraries loaded through
- require will be "forever" cached. You can, however, use ActiveRecord::Base.load_or_require("library") to get this behavior outside of the
- auto-loading associations.
-
-* Added ERB capabilities to the fixture files for dynamic fixture generation. You don't need to do anything, just include ERB blocks like:
-
- david:
- id: 1
- name: David
-
- jamis:
- id: 2
- name: Jamis
-
- <% for digit in 3..10 %>
- dev_<%= digit %>:
- id: <%= digit %>
- name: fixture_<%= digit %>
- <% end %>
-
-* Changed the yaml fixture searcher to look in the root of the fixtures directory, so when you before could have something like:
-
- fixtures/developers/fixtures.yaml
- fixtures/accounts/fixtures.yaml
-
- ...you now need to do:
-
- fixtures/developers.yaml
- fixtures/accounts.yaml
-
-* Changed the fixture format from:
-
- name: david
- data:
- id: 1
- name: David Heinemeier Hansson
- birthday: 1979-10-15
- profession: Systems development
- ---
- name: steve
- data:
- id: 2
- name: Steve Ross Kellock
- birthday: 1974-09-27
- profession: guy with keyboard
-
- ...to:
-
- david:
- id: 1
- name: David Heinemeier Hansson
- birthday: 1979-10-15
- profession: Systems development
-
- steve:
- id: 2
- name: Steve Ross Kellock
- birthday: 1974-09-27
- profession: guy with keyboard
-
- The change is NOT backwards compatible. Fixtures written in the old YAML style needs to be rewritten!
-
-* All associations will now attempt to require the classes that they associate to. Relieving the need for most explicit 'require' statements.
-
-
-*1.1.0* (34)
-
-* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically
- instantiated in instance variables relating to their names using the following style:
-
- class FixturesTest < Test::Unit::TestCase
- fixtures :developers # you can add more with comma separation
-
- def test_developers
- assert_equal 3, @developers.size # the container for all the fixtures is automatically set
- assert_kind_of Developer, @david # works like @developers["david"].find
- assert_equal "David Heinemeier Hansson", @david.name
- end
- end
-
-* Added HasAndBelongsToManyAssociation#push_with_attributes(object, join_attributes) that can create associations in the join table with additional
- attributes. This is really useful when you have information that's only relevant to the join itself, such as a "added_on" column for an association
- between post and category. The added attributes will automatically be injected into objects retrieved through the association similar to the piggy-back
- approach:
-
- post.categories.push_with_attributes(category, :added_on => Date.today)
- post.categories.first.added_on # => Date.today
-
- NOTE: The categories table doesn't have a added_on column, it's the categories_post join table that does!
-
-* Fixed that :exclusively_dependent and :dependent can't be activated at the same time on has_many associations [Jeremy Kemper]
-
-* Fixed that database passwords couldn't be all numeric [Jeremy Kemper]
-
-* Fixed that calling id would create the instance variable for new_records preventing them from being saved correctly [Jeremy Kemper]
-
-* Added sanitization feature to HasManyAssociation#find_all so it works just like Base.find_all [Sam Stephenson/Jeremy Kemper]
-
-* Added that you can pass overlapping ids to find without getting duplicated records back [Jeremy Kemper]
-
-* Added that Base.benchmark returns the result of the block [Jeremy Kemper]
-
-* Fixed problem with unit tests on Windows with SQLite [paterno]
-
-* Fixed that quotes would break regular non-yaml fixtures [Dmitry Sabanin/daft]
-
-* Fixed fixtures on windows with line endings cause problems under unix / mac [Tobias Lütke]
-
-* Added HasAndBelongsToManyAssociation#find(id) that'll search inside the collection and find the object or record with that id
-
-* Added :conditions option to has_and_belongs_to_many that works just like the one on all the other associations
-
-* Added AssociationCollection#clear to remove all associations from has_many and has_and_belongs_to_many associations without destroying the records [geech]
-
-* Added type-checking and remove in 1-instead-of-N sql statements to AssociationCollection#delete [geech]
-
-* Added a return of self to AssociationCollection#<< so appending can be chained, like project << Milestone.create << Milestone.create [geech]
-
-* Added Base#hash and Base#eql? which means that all of the equality using features of array and other containers now works:
-
- [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
-
-* Added :uniq as an option to has_and_belongs_to_many which will automatically ensure that AssociateCollection#uniq is called
- before pulling records out of the association. This is especially useful for three-way (and above) has_and_belongs_to_many associations.
-
-* Added AssociateCollection#uniq which is especially useful for has_and_belongs_to_many associations that can include duplicates,
- which is common on associations that also use metadata. Usage: post.categories.uniq
-
-* Fixed respond_to? to use a subclass specific hash instead of an Active Record-wide one
-
-* Fixed has_and_belongs_to_many to treat associations between classes in modules properly [Florian Weber]
-
-* Added a NoMethod exception to be raised when query and writer methods are called for attributes that doesn't exist [geech]
-
-* Added a more robust version of Fixtures that throws meaningful errors when on formatting issues [geech]
-
-* Added Base#transaction as a compliment to Base.transaction for prettier use in instance methods [geech]
-
-* Improved the speed of respond_to? by placing the dynamic methods lookup table in a hash [geech]
-
-* Added that any additional fields added to the join table in a has_and_belongs_to_many association
- will be placed as attributes when pulling records out through has_and_belongs_to_many associations.
- This is helpful when have information about the association itself that you want available on retrival.
-
-* Added better loading exception catching and RubyGems retries to the database adapters [alexeyv]
-
-* Fixed bug with per-model transactions [daniel]
-
-* Fixed Base#transaction so that it returns the result of the last expression in the transaction block [alexeyv]
-
-* Added Fixture#find to find the record corresponding to the fixture id. The record
- class name is guessed by using Inflector#classify (also new) on the fixture directory name.
-
- Before: Document.find(@documents["first"]["id"])
- After : @documents["first"].find
-
-* Fixed that the table name part of column names ("TABLE.COLUMN") wasn't removed properly [Andreas Schwarz]
-
-* Fixed a bug with Base#size when a finder_sql was used that didn't capitalize SELECT and FROM [geech]
-
-* Fixed quoting problems on SQLite by adding quote_string to the AbstractAdapter that can be overwritten by the concrete
- adapters for a call to the dbm. [Andreas Schwarz]
-
-* Removed RubyGems backup strategy for requiring SQLite-adapter -- if people want to use gems, they're already doing it with AR.
-
-
-*1.0.0 (35)*
-
-* Added OO-style associations methods [Florian Weber]. Examples:
-
- Project#milestones_count => Project#milestones.size
- Project#build_to_milestones => Project#milestones.build
- Project#create_for_milestones => Project#milestones.create
- Project#find_in_milestones => Project#milestones.find
- Project#find_all_in_milestones => Project#milestones.find_all
-
-* Added serialize as a new class method to control when text attributes should be YAMLized or not. This means that automated
- serialization of hashes, arrays, and so on WILL NO LONGER HAPPEN (#10). You need to do something like this:
-
- class User < ActiveRecord::Base
- serialize :settings
- end
-
- This will assume that settings is a text column and will now YAMLize any object put in that attribute. You can also specify
- an optional :class_name option that'll raise an exception if a serialized object is retrieved as a descendant of a class not in
- the hierarchy. Example:
-
- class User < ActiveRecord::Base
- serialize :settings, :class_name => "Hash"
- end
-
- user = User.create("settings" => %w( one two three ))
- User.find(user.id).settings # => raises SerializationTypeMismatch
-
-* Added the option to connect to a different database for one model at a time. Just call establish_connection on the class
- you want to have connected to another database than Base. This will automatically also connect decendents of that class
- to the different database [Renald Buter].
-
-* Added transactional protection for Base#save. Validations can now check for values knowing that it happens in a transaction and callbacks
- can raise exceptions knowing that the save will be rolled back. [Suggested by Alexey Verkhovsky]
-
-* Added column name quoting so reserved words, such as "references", can be used as column names [Ryan Platte]
-
-* Added the possibility to chain the return of what happened inside a logged block [geech]:
-
- This now works:
- log { ... }.map { ... }
-
- Instead of doing:
- result = []
- log { result = ... }
- result.map { ... }
-
-* Added "socket" option for the MySQL adapter, so you can change it to something else than "/tmp/mysql.sock" [Anna Lissa Cruz]
-
-* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema,
- person.respond_to? "name" will return true.
-
-* Added Base.benchmark which can be used to aggregate logging and benchmark, so you can measure and represent multiple statements in a single block.
- Usage (hides all the SQL calls for the individual actions and calculates total runtime for them all):
-
- Project.benchmark("Creating project") do
- project = Project.create("name" => "stuff")
- project.create_manager("name" => "David")
- project.milestones << Milestone.find_all
- end
-
-* Added logging of invalid SQL statements [Daniel Von Fange]
-
-* Added alias Errors#[] for Errors#on, so you can now say person.errors["name"] to retrieve the errors for name [Andreas Schwarz]
-
-* Added RubyGems require attempt if sqlite-ruby is not available through regular methods.
-
-* Added compatibility with 2.x series of sqlite-ruby drivers. [Jamis Buck]
-
-* Added type safety for association assignments, so a ActiveRecord::AssociationTypeMismatch will be raised if you attempt to
- assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on
- mistaken association assignments. [Reported by Andreas Schwarz]
-
-* Added the option to keep many fixtures in one single YAML document [what-a-day]
-
-* Added the class method "inheritance_column" that can be overwritten to return the name of an alternative column than "type" for storing
- the type for inheritance hierarchies. [Dave Steinberg]
-
-* Added [] and []= as an alternative way to access attributes when the regular methods have been overwritten [Dave Steinberg]
-
-* Added the option to observer more than one class at the time by specifying observed_class as an array
-
-* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them
- on PostgreSQL. [Dave Steinberg]
-
-* Changed that integer and floats set to "" through attributes= remain as NULL. This was especially a problem for scaffolding and postgresql. (#49)
-
-* Changed the MySQL Adapter to rely on MySQL for its defaults for socket, host, and port [Andreas Schwarz]
-
-* Changed ActionControllerError to decent from StandardError instead of Exception. It can now be caught by a generic rescue.
-
-* Changed class inheritable attributes to not use eval [Caio Chassot]
-
-* Changed Errors#add to now use "invalid" as the default message instead of true, which means full_messages work with those [Marcel Molina Jr.]
-
-* Fixed spelling on Base#add_on_boundry_breaking to Base#add_on_boundary_breaking (old naming still works) [Marcel Molina Jr.]
-
-* Fixed that entries in the has_and_belongs_to_many join table didn't get removed when an associated object was destroyed.
-
-* Fixed unnecessary calls to SET AUTOCOMMIT=0/1 for MySQL adapter [Andreas Schwarz]
-
-* Fixed PostgreSQL defaults are now handled gracefully [Dave Steinberg]
-
-* Fixed increment/decrement_counter are now atomic updates [Andreas Schwarz]
-
-* Fixed the problems the Inflector had turning Attachment into attuchments and Cases into Casis [radsaq/Florian Gross]
-
-* Fixed that cloned records would point attribute references on the parent object [Andreas Schwarz]
-
-* Fixed SQL for type call on inheritance hierarchies [Caio Chassot]
-
-* Fixed bug with typed inheritance [Florian Weber]
-
-* Fixed a bug where has_many collection_count wouldn't use the conditions specified for that association
-
-
-*0.9.5*
-
-* Expanded the table_name guessing rules immensely [Florian Green]. Documentation:
-
- Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
- directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
- to guess the table name from even when called on Reply. The guessing rules are as follows:
- * Class name ends in "x", "ch" or "ss": "es" is appended, so a Search class becomes a searches table.
- * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies",
- so a Category class becomes a categories table.
- * Class name ends in "fe": The "fe" is replaced with "ves", so a Wife class becomes a wives table.
- * Class name ends in "lf" or "rf": The "f" is replaced with "ves", so a Half class becomes a halves table.
- * Class name ends in "person": The "person" is replaced with "people", so a Salesperson class becomes a salespeople table.
- * Class name ends in "man": The "man" is replaced with "men", so a Spokesman class becomes a spokesmen table.
- * Class name ends in "sis": The "i" is replaced with an "e", so a Basis class becomes a bases table.
- * Class name ends in "tum" or "ium": The "um" is replaced with an "a", so a Datum class becomes a data table.
- * Class name ends in "child": The "child" is replaced with "children", so a NodeChild class becomes a node_children table.
- * Class name ends in an "s": No additional characters are added or removed.
- * Class name doesn't end in "s": An "s" is appended, so a Comment class becomes a comments table.
- * Class name with word compositions: Compositions are underscored, so CreditCard class becomes a credit_cards table.
- Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended.
- So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts".
-
- You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a
- "mice" table. Example:
-
- class Mouse < ActiveRecord::Base
- def self.table_name() "mice" end
- end
-
- This conversion is now done through an external class called Inflector residing in lib/active_record/support/inflector.rb.
-
-* Added find_all_in_collection to has_many defined collections. Works like this:
-
- class Firm < ActiveRecord::Base
- has_many :clients
- end
-
- firm.id # => 1
- firm.find_all_in_clients "revenue > 1000" # SELECT * FROM clients WHERE firm_id = 1 AND revenue > 1000
-
- [Requested by Dave Thomas]
-
-* Fixed finders for inheritance hierarchies deeper than one level [Florian Weber]
-
-* Added add_on_boundry_breaking to errors to accompany add_on_empty as a default validation method. It's used like this:
-
- class Person < ActiveRecord::Base
- protected
- def validation
- errors.add_on_boundry_breaking "password", 3..20
- end
- end
-
- This will add an error to the tune of "is too short (minimum is 3 characters)" or "is too long (minimum is 20 characters)" if
- the password is outside the boundry. The messages can be changed by passing a third and forth parameter as message strings.
-
-* Implemented a clone method that works properly with AR. It returns a clone of the record that
- hasn't been assigned an id yet and is treated as a new record.
-
-* Allow for domain sockets in PostgreSQL by not assuming localhost when no host is specified [Scott Barron]
-
-* Fixed that bignums are saved properly instead of attempted to be YAMLized [Andreas Schwartz]
-
-* Fixed a bug in the GEM where the rdoc options weren't being passed according to spec [Chad Fowler]
-
-* Fixed a bug with the exclusively_dependent option for has_many
-
-
-*0.9.4*
-
-* Correctly guesses the primary key when the class is inside a module [Dave Steinberg].
-
-* Added [] and []= as alternatives to read_attribute and write_attribute [Dave Steinberg]
-
-* has_and_belongs_to_many now accepts an :order key to determine in which order the collection is returned [radsaq].
-
-* The ids passed to find and find_on_conditions are now automatically sanitized.
-
-* Added escaping of plings in YAML content.
-
-* Multi-parameter assigns where all the parameters are empty will now be set to nil instead of a new instance of their class.
-
-* Proper type within an inheritance hierarchy is now ensured already at object initialization (instead of first at create)
-
-
-*0.9.3*
-
-* Fixed bug with using a different primary key name together with has_and_belongs_to_many [Investigation by Scott]
-
-* Added :exclusively_dependent option to the has_many association macro. The doc reads:
-
- If set to true all the associated object are deleted in one SQL statement without having their
- before_destroy callback run. This should only be used on associations that depend solely on
- this class and don't need to do any clean-up in before_destroy. The upside is that it's much
- faster, especially if there's a counter_cache involved.
-
-* Added :port key to connection options, so the PostgreSQL and MySQL adapters can connect to a database server
- running on another port than the default.
-
-* Converted the new natural singleton methods that prevented AR objects from being saved by PStore
- (and hence be placed in a Rails session) to a module. [Florian Weber]
-
-* Fixed the use of floats (was broken since 0.9.0+)
-
-* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with
- Action Pack scaffolding.
-
-* Fixed booleans support for PostgreSQL (use real true/false on boolean fields instead of 0/1 on tinyints) [radsaq]
-
-
-*0.9.2*
-
-* Added static method for instantly updating a record
-
-* Treat decimal and numeric as Ruby floats [Andreas Schwartz]
-
-* Treat chars as Ruby strings (fixes problem for Action Pack form helpers too)
-
-* Removed debugging output accidently left in (which would screw web applications)
-
-
-*0.9.1*
-
-* Added MIT license
-
-* Added natural object-style assignment for has_and_belongs_to_many associations. Consider the following model:
-
- class Event < ActiveRecord::Base
- has_one_and_belongs_to_many :sponsors
- end
-
- class Sponsor < ActiveRecord::Base
- has_one_and_belongs_to_many :sponsors
- end
-
- Earlier, you'd have to use synthetic methods for creating associations between two objects of the above class:
-
- roskilde_festival.add_to_sponsors(carlsberg)
- roskilde_festival.remove_from_sponsors(carlsberg)
-
- nike.add_to_events(world_cup)
- nike.remove_from_events(world_cup)
-
- Now you can use regular array-styled methods:
-
- roskilde_festival.sponsors << carlsberg
- roskilde_festival.sponsors.delete(carlsberg)
-
- nike.events << world_cup
- nike.events.delete(world_cup)
-
-* Added delete method for has_many associations. Using this will nullify an association between the has_many and the belonging
- object by setting the foreign key to null. Consider this model:
-
- class Post < ActiveRecord::Base
- has_many :comments
- end
-
- class Comment < ActiveRecord::Base
- belongs_to :post
- end
-
- You could do something like:
-
- funny_comment.has_post? # => true
- announcement.comments.delete(funny_comment)
- funny_comment.has_post? # => false
-
-
-*0.9.0*
-
-* Active Record is now thread safe! (So you can use it with Cerise and WEBrick applications)
- [Implementation idea by Michael Neumann, debugging assistance by Jamis Buck]
-
-* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute.
- This brings the tax for using Active Record instead of "riding on the metal" (using MySQL-ruby C-driver directly) down to ~50%.
- Done by doing lazy type conversions and caching column information on the class-level.
-
-* Added callback objects and procs as options for implementing the target for callback macros.
-
-* Added "counter_cache" option to belongs_to that automates the usage of increment_counter and decrement_counter. Consider:
-
- class Post < ActiveRecord::Base
- has_many :comments
- end
-
- class Comment < ActiveRecord::Base
- belongs_to :post
- end
-
- Iterating over 100 posts like this:
-
- <% for post in @posts %>
- <%= post.title %> has <%= post.comments_count %> comments
- <% end %>
-
- Will generate 100 SQL count queries -- one for each call to post.comments_count. If you instead add a "comments_count" int column
- to the posts table and rewrite the comments association macro with:
-
- class Comment < ActiveRecord::Base
- belongs_to :post, :counter_cache => true
- end
-
- Those 100 SQL count queries will be reduced to zero. Beware that counter caching is only appropriate for objects that begin life
- with the object it's specified to belong with and is destroyed like that as well. Typically objects where you would also specify
- :dependent => true. If your objects switch from one belonging to another (like a post that can be move from one category to another),
- you'll have to manage the counter yourself.
-
-* Added natural object-style assignment for has_one and belongs_to associations. Consider the following model:
-
- class Project < ActiveRecord::Base
- has_one :manager
- end
-
- class Manager < ActiveRecord::Base
- belongs_to :project
- end
-
- Earlier, assignments would work like following regardless of which way the assignment told the best story:
-
- active_record.manager_id = david.id
-
- Now you can do it either from the belonging side:
-
- david.project = active_record
-
- ...or from the having side:
-
- active_record.manager = david
-
- If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the
- project_id attribute on david would be set to the id of active_record, then david would be saved.
-
-* Added natural object-style assignment for has_many associations [Florian Weber]. Consider the following model:
-
- class Project < ActiveRecord::Base
- has_many :milestones
- end
-
- class Milestone < ActiveRecord::Base
- belongs_to :project
- end
-
- Earlier, assignments would work like following regardless of which way the assignment told the best story:
-
- deadline.project_id = active_record.id
-
- Now you can do it either from the belonging side:
-
- deadline.project = active_record
-
- ...or from the having side:
-
- active_record.milestones << deadline
-
- The milestone is automatically saved with the new foreign key.
-
-* API CHANGE: Attributes for text (or blob or similar) columns will now have unknown classes stored using YAML instead of using
- to_s. (Known classes that won't be yamelized are: String, NilClass, TrueClass, FalseClass, Fixnum, Date, and Time).
- Likewise, data pulled out of text-based attributes will be attempted converged using Yaml if they have the "--- " header.
- This was primarily done to be enable the storage of hashes and arrays without wrapping them in aggregations, so now you can do:
-
- user = User.find(1)
- user.preferences = { "background" => "black", "display" => large }
- user.save
-
- User.find(1).preferences # => { "background" => "black", "display" => large }
-
- Please note that this method should only be used when you don't care about representing the object in proper columns in
- the database. A money object consisting of an amount and a currency is still a much better fit for a value object done through
- aggregations than this new option.
-
-* POSSIBLE CODE BREAKAGE: As a consequence of the lazy type conversions, it's a bad idea to reference the @attributes hash
- directly (it always was, but now it's paramount that you don't). If you do, you won't get the type conversion. So to implement
- new accessors for existing attributes, use read_attribute(attr_name) and write_attribute(attr_name, value) instead. Like this:
-
- class Song < ActiveRecord::Base
- # Uses an integer of seconds to hold the length of the song
-
- def length=(minutes)
- write_attribute("length", minutes * 60)
- end
-
- def length
- read_attribute("length") / 60
- end
- end
-
- The clever kid will notice that this opens a door to sidestep the automated type conversion by using @attributes directly.
- This is not recommended as read/write_attribute may be granted additional responsibilities in the future, but if you think
- you know what you're doing and aren't afraid of future consequences, this is an option.
-
-* Applied a few minor bug fixes reported by Daniel Von Fange.
-
-
-*0.8.4*
-
-_Reflection_
-
-* Added ActiveRecord::Reflection with a bunch of methods and classes for reflecting in aggregations and associations.
-
-* Added Base.columns and Base.content_columns which returns arrays of column description (type, default, etc) objects.
-
-* Added Base#attribute_names which returns an array of names for the attributes available on the object.
-
-* Added Base#column_for_attribute(name) which returns the column description object for the named attribute.
-
-
-_Misc_
-
-* Added multi-parameter assignment:
-
- # Instantiate objects for all attribute classes that needs more than one constructor parameter. This is done
- # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
- # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
- # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
- # parenteses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
- # s for String, and a for Array.
-
- This is incredibly useful for assigning dates from HTML drop-downs of month, year, and day.
-
-* Fixed bug with custom primary key column name and Base.find on multiple parameters.
-
-* Fixed bug with dependent option on has_one associations if there was no associated object.
-
-
-*0.8.3*
-
-_Transactions_
-
-* Added transactional protection for destroy (important for the new :dependent option) [Suggested by Carl Youngblood]
-
-* Fixed so transactions are ignored on MyISAM tables for MySQL (use InnoDB to get transactions)
-
-* Changed transactions so only exceptions will cause a rollback, not returned false.
-
-
-_Mapping_
-
-* Added support for non-integer primary keys [Aredridel/earlier work by Michael Neumann]
-
- User.find "jdoe"
- Product.find "PDKEY-INT-12"
-
-* Added option to specify naming method for primary key column. ActiveRecord::Base.primary_key_prefix_type can either
- be set to nil, :table_name, or :table_name_with_underscore. :table_name will assume that Product class has a primary key
- of "productid" and :table_name_with_underscore will assume "product_id". The default nil will just give "id".
-
-* Added an overwriteable primary_key method that'll instruct AR to the name of the
- id column [Aredridele/earlier work by Guan Yang]
-
- class Project < ActiveRecord::Base
- def self.primary_key() "project_id" end
- end
-
-* Fixed that Active Records can safely associate inside and out of modules.
-
- class MyApplication::Account < ActiveRecord::Base
- has_many :clients # will look for MyApplication::Client
- has_many :interests, :class_name => "Business::Interest" # will look for Business::Interest
- end
-
-* Fixed that Active Records can safely live inside modules [Aredridel]
-
- class MyApplication::Account < ActiveRecord::Base
- end
-
-
-_Misc_
-
-* Added freeze call to value object assignments to ensure they remain immutable [Spotted by Gavin Sinclair]
-
-* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is
- observed_class() class method. This is more consistant with things like self.table_name(). Works like this:
-
- class AuditObserver < ActiveRecord::Observer
- def self.observed_class() Account end
- def after_update(account)
- AuditTrail.new(account, "UPDATED")
- end
- end
-
- [Suggested by Gavin Sinclair]
-
-* Create new Active Record objects by setting the attributes through a block. Like this:
-
- person = Person.new do |p|
- p.name = 'Freddy'
- p.age = 19
- end
-
- [Suggested by Gavin Sinclair]
-
-
-*0.8.2*
-
-* Added inheritable callback queues that can ensure that certain callback methods or inline fragments are
- run throughout the entire inheritance hierarchy. Regardless of whether a descendant overwrites the callback
- method:
-
- class Topic < ActiveRecord::Base
- before_destroy :destroy_author, 'puts "I'm an inline fragment"'
- end
-
- Learn more in link:classes/ActiveRecord/Callbacks.html
-
-* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when
- the holder is destroyed:
-
- class Album < ActiveRecord::Base
- has_many :tracks, :dependent => true
- end
-
- All the associated tracks are destroyed when the album is.
-
-* Added Base.create as a factory that'll create, save, and return a new object in one step.
-
-* Automatically convert strings in config hashes to symbols for the _connection methods. This allows you
- to pass the argument hashes directly from yaml. (Luke)
-
-* Fixed the install.rb to include simple.rb [Spotted by Kevin Bullock]
-
-* Modified block syntax to better follow our code standards outlined in
- http://www.rubyonrails.org/CodingStandards
-
-
-*0.8.1*
-
-* Added object-level transactions [Austin Ziegler]
-
-* Changed adapter-specific connection methods to use centralized ActiveRecord::Base.establish_connection,
- which is parametized through a config hash with symbol keys instead of a regular parameter list.
- This will allow for database connections to be opened in a more generic fashion. (Luke)
-
- NOTE: This requires all *_connections to be updated! Read more in:
- http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000081
-
-* Fixed SQLite adapter so objects fetched from has_and_belongs_to_many have proper attributes
- (t.name is now name). [Spotted by Garrett Rooney]
-
-* Fixed SQLite adapter so dates are returned as Date objects, not Time objects [Spotted by Gavin Sinclair]
-
-* Fixed requirement of date class, so date conversions are succesful regardless of whether you
- manually require date or not.
-
-
-*0.8.0*
-
-* Added transactions
-
-* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7])
- as parameter and then return an array of objects instead of just an object
-
-* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a
- collection, not an association
-
-* Fixed SQLite adapter so empty or nil values in columns of datetime, date, or time type
- aren't treated as current time [Spotted by Gavin Sinclair]
-
-
-*0.7.6*
-
-* Fixed the install.rb to create the lib/active_record/support directory [Spotted by Gavin Sinclair]
-* Fixed that has_association? would always return true [Daniel Von Fange]
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
new file mode 100644
index 0000000000..3de5af22c5
--- /dev/null
+++ b/activerecord/CHANGELOG.md
@@ -0,0 +1,6874 @@
+## Rails 4.0.0 (unreleased) ##
+
+* Added support for partial indices to PostgreSQL adapter
+
+ The `add_index` method now supports a `where` option that receives a
+ string with the partial index criteria.
+
+ add_index(:accounts, :code, :where => "active")
+
+ Generates
+
+ CREATE INDEX index_accounts_on_code ON accounts(code) WHERE active
+
+ *Marcelo Silveira*
+
+* Implemented ActiveRecord::Relation#none method
+
+ The `none` method returns a chainable relation with zero records
+ (an instance of the NullRelation class).
+
+ Any subsequent condition chained to the returned relation will continue
+ generating an empty relation and will not fire any query to the database.
+
+ *Juanjo Bazán*
+
+* Added the `ActiveRecord::NullRelation` class implementing the null
+ object pattern for the Relation class. *Juanjo Bazán*
+
+* Added deprecation for the `:dependent => :restrict` association option.
+
+ Please note:
+
+ * Up until now `has_many` and `has_one`, `:dependent => :restrict`
+ option raised a `DeleteRestrictionError` at the time of destroying
+ the object. Instead, it will add an error on the model.
+
+ * To fix this warning, make sure your code isn't relying on a
+ `DeleteRestrictionError` and then add
+ `config.active_record.dependent_restrict_raises = false` to your
+ application config.
+
+ * New rails application would be generated with the
+ `config.active_record.dependent_restrict_raises = false` in the
+ application config.
+
+ *Manoj Kumar*
+
+* Added `create_join_table` migration helper to create HABTM join tables
+
+ create_join_table :products, :categories
+ # =>
+ # create_table :categories_products, :id => false do |td|
+ # td.integer :product_id, :null => false
+ # td.integer :category_id, :null => false
+ # end
+
+ *Rafael Mendonça França*
+
+* The primary key is always initialized in the @attributes hash to nil (unless
+ another value has been specified).
+
+* In previous releases, the following would generate a single query with
+ an `OUTER JOIN comments`, rather than two separate queries:
+
+ Post.includes(:comments)
+ .where("comments.name = 'foo'")
+
+ This behaviour relies on matching SQL string, which is an inherently
+ flawed idea unless we write an SQL parser, which we do not wish to
+ do.
+
+ Therefore, it is now deprecated.
+
+ To avoid deprecation warnings and for future compatibility, you must
+ explicitly state which tables you reference, when using SQL snippets:
+
+ Post.includes(:comments)
+ .where("comments.name = 'foo'")
+ .references(:comments)
+
+ Note that you do not need to explicitly specify references in the
+ following cases, as they can be automatically inferred:
+
+ Post.where(comments: { name: 'foo' })
+ Post.where('comments.name' => 'foo')
+ Post.order('comments.name')
+
+ You also do not need to worry about this unless you are doing eager
+ loading. Basically, don't worry unless you see a deprecation warning
+ or (in future releases) an SQL error due to a missing JOIN.
+
+ [Jon Leighton]
+
+* Support for the `schema_info` table has been dropped. Please
+ switch to `schema_migrations`.
+
+* Connections *must* be closed at the end of a thread. If not, your
+ connection pool can fill and an exception will be raised.
+
+* Added the `ActiveRecord::Model` module which can be included in a
+ class as an alternative to inheriting from `ActiveRecord::Base`:
+
+ class Post
+ include ActiveRecord::Model
+ end
+
+ Please note:
+
+ * Up until now it has been safe to assume that all AR models are
+ descendants of `ActiveRecord::Base`. This is no longer a safe
+ assumption, but it may transpire that there are areas of the
+ code which still make this assumption. So there may be
+ 'teething difficulties' with this feature. (But please do try it
+ and report bugs.)
+
+ * Plugins & libraries etc that add methods to `ActiveRecord::Base`
+ will not be compatible with `ActiveRecord::Model`. Those libraries
+ should add to `ActiveRecord::Model` instead (which is included in
+ `Base`), or better still, avoid monkey-patching AR and instead
+ provide a module that users can include where they need it.
+
+ * To minimise the risk of conflicts with other code, it is
+ advisable to include `ActiveRecord::Model` early in your class
+ definition.
+
+ *Jon Leighton*
+
+* PostgreSQL hstore records can be created.
+
+* PostgreSQL hstore types are automatically deserialized from the database.
+
+
+## Rails 3.2.1 (unreleased) ##
+
+* The threshold for auto EXPLAIN is ignored if there's no logger. *fxn*
+
+* Fix possible race condition when two threads try to define attribute
+ methods for the same class.
+
+
+## Rails 3.2.0 (January 20, 2012) ##
+
+* Added a `with_lock` method to ActiveRecord objects, which starts
+ a transaction, locks the object (pessimistically) and yields to the block.
+ The method takes one (optional) parameter and passes it to `lock!`.
+
+ Before:
+
+ class Order < ActiveRecord::Base
+ def cancel!
+ transaction do
+ lock!
+ # ... cancelling logic
+ end
+ end
+ end
+
+ After:
+
+ class Order < ActiveRecord::Base
+ def cancel!
+ with_lock do
+ # ... cancelling logic
+ end
+ end
+ end
+
+ *Olek Janiszewski*
+
+* 'on' and 'ON' boolean columns values are type casted to true
+ *Santiago Pastorino*
+
+* Added ability to run migrations only for given scope, which allows
+ to run migrations only from one engine (for example to revert changes
+ from engine that you want to remove).
+
+ Example:
+ rake db:migrate SCOPE=blog
+
+ *Piotr Sarnacki*
+
+* Migrations copied from engines are now scoped with engine's name,
+ for example 01_create_posts.blog.rb. *Piotr Sarnacki*
+
+* Implements `AR::Base.silence_auto_explain`. This method allows the user to
+ selectively disable automatic EXPLAINs within a block. *fxn*
+
+* Implements automatic EXPLAIN logging for slow queries.
+
+ A new configuration parameter `config.active_record.auto_explain_threshold_in_seconds`
+ determines what's to be considered a slow query. Setting that to `nil` disables
+ this feature. Defaults are 0.5 in development mode, and `nil` in test and production
+ modes.
+
+ As of this writing there's support for SQLite, MySQL (mysql2 adapter), and
+ PostgreSQL.
+
+ *fxn*
+
+* Implemented ActiveRecord::Relation#pluck method
+
+ Method returns Array of column value from table under ActiveRecord model
+
+ Client.pluck(:id)
+
+ *Bogdan Gusiev*
+
+* Automatic closure of connections in threads is deprecated. For example
+ the following code is deprecated:
+
+ Thread.new { Post.find(1) }.join
+
+ It should be changed to close the database connection at the end of
+ the thread:
+
+ Thread.new {
+ Post.find(1)
+ Post.connection.close
+ }.join
+
+ Only people who spawn threads in their application code need to worry
+ about this change.
+
+* Deprecated:
+
+ * `set_table_name`
+ * `set_inheritance_column`
+ * `set_sequence_name`
+ * `set_primary_key`
+ * `set_locking_column`
+
+ Use an assignment method instead. For example, instead of `set_table_name`, use `self.table_name=`:
+
+ class Project < ActiveRecord::Base
+ self.table_name = "project"
+ end
+
+ Or define your own `self.table_name` method:
+
+ class Post < ActiveRecord::Base
+ def self.table_name
+ "special_" + super
+ end
+ end
+ Post.table_name # => "special_posts"
+
+ *Jon Leighton*
+
+* Generated association methods are created within a separate module to allow overriding and
+ composition using `super`. For a class named `MyModel`, the module is named
+ `MyModel::GeneratedFeatureMethods`. It is included into the model class immediately after
+ the `generated_attributes_methods` module defined in ActiveModel, so association methods
+ override attribute methods of the same name. *Josh Susser*
+
+* Implemented ActiveRecord::Relation#explain. *fxn*
+
+* Add ActiveRecord::Relation#uniq for generating unique queries.
+
+ Before:
+
+ Client.select('DISTINCT name')
+
+ After:
+
+ Client.select(:name).uniq
+
+ This also allows you to revert the uniqueness in a relation:
+
+ Client.select(:name).uniq.uniq(false)
+
+ *Jon Leighton*
+
+* Support index sort order in sqlite, mysql and postgres adapters. *Vlad Jebelev*
+
+* Allow the :class_name option for associations to take a symbol (:Client) in addition to
+ a string ('Client').
+
+ This is to avoid confusing newbies, and to be consistent with the fact that other options
+ like :foreign_key already allow a symbol or a string.
+
+ *Jon Leighton*
+
+* In development mode the db:drop task also drops the test database. For symmetry with
+ the db:create task. *Dmitriy Kiriyenko*
+
+* Added ActiveRecord::Base.store for declaring simple single-column key/value stores *DHH*
+
+ class User < ActiveRecord::Base
+ store :settings, accessors: [ :color, :homepage ]
+ end
+
+ u = User.new(color: 'black', homepage: '37signals.com')
+ u.color # Accessor stored attribute
+ u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
+
+
+* MySQL: case-insensitive uniqueness validation avoids calling LOWER when
+ the column already uses a case-insensitive collation. Fixes #561.
+
+ *Joseph Palermo*
+
+* Transactional fixtures enlist all active database connections. You can test
+ models on different connections without disabling transactional fixtures.
+
+ *Jeremy Kemper*
+
+* Add first_or_create, first_or_create!, first_or_initialize methods to Active Record. This is a
+ better approach over the old find_or_create_by dynamic methods because it's clearer which
+ arguments are used to find the record and which are used to create it:
+
+ User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson")
+
+ *Andrés Mejía*
+
+* Fix nested attributes bug where _destroy parameter is taken into account
+ during :reject_if => :all_blank (fixes #2937)
+
+ *Aaron Christy*
+
+## Rails 3.1.3 (November 20, 2011) ##
+
+* Perf fix: If we're deleting all records in an association, don't add a IN(..) clause
+ to the query. *GH 3672*
+
+ *Jon Leighton*
+
+* Fix bug with referencing other mysql databases in set_table_name. *GH 3690*
+
+* Fix performance bug with mysql databases on a server with lots of other databses. *GH 3678*
+
+ *Christos Zisopoulos and Kenny J*
+
+## Rails 3.1.2 (November 18, 2011) ##
+
+* Fix bug with PostgreSQLAdapter#indexes. When the search path has multiple schemas, spaces
+ were not being stripped from the schema names after the first.
+
+ *Sean Kirby*
+
+* Preserve SELECT columns on the COUNT for finder_sql when possible. *GH 3503*
+
+ *Justin Mazzi*
+
+* Reset prepared statement cache when schema changes impact statement results. *GH 3335*
+
+ *Aaron Patterson*
+
+* Postgres: Do not attempt to deallocate a statement if the connection is no longer active.
+
+ *Ian Leitch*
+
+* Prevent QueryCache leaking database connections. *GH 3243*
+
+ *Mark J. Titorenko*
+
+* Fix bug where building the conditions of a nested through association could potentially
+ modify the conditions of the through and/or source association. If you have experienced
+ bugs with conditions appearing in the wrong queries when using nested through associations,
+ this probably solves your problems. *GH #3271*
+
+ *Jon Leighton*
+
+* If a record is removed from a has_many :through, all of the join records relating to that
+ record should also be removed from the through association's target.
+
+ *Jon Leighton*
+
+* Fix adding multiple instances of the same record to a has_many :through. *GH #3425*
+
+ *Jon Leighton*
+
+* Fix creating records in a through association with a polymorphic source type. *GH #3247*
+
+ *Jon Leighton*
+
+* MySQL: use the information_schema than the describe command when we look for a primary key. *GH #3440*
+
+ *Kenny J*
+
+## Rails 3.1.1 (October 7, 2011) ##
+
+* Add deprecation for the preload_associations method. Fixes #3022.
+
+ *Jon Leighton*
+
+* Don't require a DB connection when loading a model that uses set_primary_key. GH #2807.
+
+ *Jon Leighton*
+
+* Fix using select() with a habtm association, e.g. Person.friends.select(:name). GH #3030 and
+ \#2923.
+
+ *Hendy Tanata*
+
+* Fix belongs_to polymorphic with custom primary key on target. GH #3104.
+
+ *Jon Leighton*
+
+* CollectionProxy#replace should change the DB records rather than just mutating the array.
+ Fixes #3020.
+
+ *Jon Leighton*
+
+* LRU cache in mysql and sqlite are now per-process caches.
+
+ * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache keys are per process id.
+ * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto
+ *Aaron Patterson*
+
+* Support bulk change_table in mysql2 adapter, as well as the mysql one. *Jon Leighton*
+
+* If multiple parameters are sent representing a date, and some are blank, the
+ resulting object is nil. In previous releases those values defaulted to 1. This only affects existing but blank parameters, missing ones still raise an error. [Akira Matsuda]
+* ActiveRecord::Base.establish_connection now takes a string that contains
+ a URI that specifies the connection configuration. For example:
+ ActiveRecord::Base.establish_connection 'postgres://localhost/foo'
+
+* Active Record's dynamic finder will now raise the error if you passing in less number of arguments than what you call in method signature.
+
+ So if you were doing this and expecting the second argument to be nil:
+
+ User.find_by_username_and_group("sikachu")
+
+ You'll now get `ArgumentError: wrong number of arguments (1 for 2).` You'll then have to do this:
+
+ User.find_by_username_and_group("sikachu", nil)
+
+ *Prem Sichanugrist*
+
+
+## Rails 3.1.0 (August 30, 2011) ##
+
+* Add a proxy_association method to association proxies, which can be called by association
+ extensions to access information about the association. This replaces proxy_owner etc with
+ proxy_association.owner.
+
+ *Jon Leighton*
+
+* ActiveRecord::MacroReflection::AssociationReflection#build_record has a new method signature.
+
+ Before: def build_association(*options)
+ After: def build_association(*options, &block)
+
+ Users who are redefining this method to extend functionality should ensure that the block is
+ passed through to ActiveRecord::Base#new.
+
+ This change is necessary to fix https://github.com/rails/rails/issues/1842.
+
+ *Jon Leighton*
+
+* AR#pluralize_table_names can be used to singularize/pluralize table name of an individual model:
+
+ class User < ActiveRecord::Base
+ self.pluralize_table_names = false
+ end
+
+ Previously this could only be set globally for all models through ActiveRecord::Base.pluralize_table_names. *Guillermo Iguaran*
+
+* Add block setting of attributes to singular associations:
+
+ class User < ActiveRecord::Base
+ has_one :account
+ end
+
+ user.build_account{ |a| a.credit_limit => 100.0 }
+
+ The block is called after the instance has been initialized. *Andrew White*
+
+* Add ActiveRecord::Base.attribute_names to return a list of attribute names. This will return an empty array if the model is abstract or table does not exists. *Prem Sichanugrist*
+
+* CSV Fixtures are deprecated and support will be removed in Rails 3.2.0
+
+* AR#new, AR#create, AR#create!, AR#update_attributes and AR#update_attributes! all accept a second hash as option that allows you
+ to specify which role to consider when assigning attributes. This is built on top of ActiveModel's
+ new mass assignment capabilities:
+
+ class Post < ActiveRecord::Base
+ attr_accessible :title
+ attr_accessible :title, :published_at, :as => :admin
+ end
+
+ Post.new(params[:post], :as => :admin)
+
+ assign_attributes() with similar API was also added and attributes=(params, guard) was deprecated.
+
+ Please note that this changes the method signatures for AR#new, AR#create, AR#create!, AR#update_attributes and AR#update_attributes!. If you have overwritten these methods you should update them accordingly.
+
+ *Josh Kalderimis*
+
+* default_scope can take a block, lambda, or any other object which responds to `call` for lazy
+ evaluation:
+
+ default_scope { ... }
+ default_scope lambda { ... }
+ default_scope method(:foo)
+
+ This feature was originally implemented by Tim Morgan, but was then removed in favour of
+ defining a 'default_scope' class method, but has now been added back in by Jon Leighton.
+ The relevant lighthouse ticket is #1812.
+
+* Default scopes are now evaluated at the latest possible moment, to avoid problems where
+ scopes would be created which would implicitly contain the default scope, which would then
+ be impossible to get rid of via Model.unscoped.
+
+ Note that this means that if you are inspecting the internal structure of an
+ ActiveRecord::Relation, it will *not* contain the default scope, though the resulting
+ query will do. You can get a relation containing the default scope by calling
+ ActiveRecord#with_default_scope, though this is not part of the public API.
+
+ *Jon Leighton*
+
+* If you wish to merge default scopes in special ways, it is recommended to define your default
+ scope as a class method and use the standard techniques for sharing code (inheritance, mixins,
+ etc.):
+
+ class Post < ActiveRecord::Base
+ def self.default_scope
+ where(:published => true).where(:hidden => false)
+ end
+ end
+
+ *Jon Leighton*
+
+* PostgreSQL adapter only supports PostgreSQL version 8.2 and higher.
+
+* ConnectionManagement middleware is changed to clean up the connection pool
+ after the rack body has been flushed.
+
+* Added an update_column method on ActiveRecord. This new method updates a given attribute on an object, skipping validations and callbacks.
+ It is recommended to use #update_attribute unless you are sure you do not want to execute any callback, including the modification of
+ the updated_at column. It should not be called on new records.
+ Example:
+
+ User.first.update_column(:name, "sebastian") # => true
+
+ *Sebastian Martinez*
+
+* Associations with a :through option can now use *any* association as the
+ through or source association, including other associations which have a
+ :through option and has_and_belongs_to_many associations
+
+ *Jon Leighton*
+
+* The configuration for the current database connection is now accessible via
+ ActiveRecord::Base.connection_config. *fxn*
+
+* limits and offsets are removed from COUNT queries unless both are supplied.
+ For example:
+
+ People.limit(1).count # => 'SELECT COUNT(*) FROM people'
+ People.offset(1).count # => 'SELECT COUNT(*) FROM people'
+ People.limit(1).offset(1).count # => 'SELECT COUNT(*) FROM people LIMIT 1 OFFSET 1'
+
+ *lighthouse #6262*
+
+* ActiveRecord::Associations::AssociationProxy has been split. There is now an Association class
+ (and subclasses) which are responsible for operating on associations, and then a separate,
+ thin wrapper called CollectionProxy, which proxies collection associations.
+
+ This prevents namespace pollution, separates concerns, and will allow further refactorings.
+
+ Singular associations (has_one, belongs_to) no longer have a proxy at all. They simply return
+ the associated record or nil. This means that you should not use undocumented methods such
+ as bob.mother.create - use bob.create_mother instead.
+
+ *Jon Leighton*
+
+* Make has_many :through associations work correctly when you build a record and then save it. This
+ requires you to set the :inverse_of option on the source reflection on the join model, like so:
+
+ class Post < ActiveRecord::Base
+ has_many :taggings
+ has_many :tags, :through => :taggings
+ end
+
+ class Tagging < ActiveRecord::Base
+ belongs_to :post
+ belongs_to :tag, :inverse_of => :tagging # :inverse_of must be set!
+ end
+
+ class Tag < ActiveRecord::Base
+ has_many :taggings
+ has_many :posts, :through => :taggings
+ end
+
+ post = Post.first
+ tag = post.tags.build :name => "ruby"
+ tag.save # will save a Taggable linking to the post
+
+ *Jon Leighton*
+
+* Support the :dependent option on has_many :through associations. For historical and practical
+ reasons, :delete_all is the default deletion strategy employed by association.delete(*records),
+ despite the fact that the default strategy is :nullify for regular has_many. Also, this only
+ works at all if the source reflection is a belongs_to. For other situations, you should directly
+ modify the through association.
+
+ *Jon Leighton*
+
+* Changed the behaviour of association.destroy for has_and_belongs_to_many and has_many :through.
+ From now on, 'destroy' or 'delete' on an association will be taken to mean 'get rid of the link',
+ not (necessarily) 'get rid of the associated records'.
+
+ Previously, has_and_belongs_to_many.destroy(*records) would destroy the records themselves. It
+ would not delete any records in the join table. Now, it deletes the records in the join table.
+
+ Previously, has_many_through.destroy(*records) would destroy the records themselves, and the
+ records in the join table. [Note: This has not always been the case; previous version of Rails
+ only deleted the records themselves.] Now, it destroys only the records in the join table.
+
+ Note that this change is backwards-incompatible to an extent, but there is unfortunately no
+ way to 'deprecate' it before changing it. The change is being made in order to have
+ consistency as to the meaning of 'destroy' or 'delete' across the different types of associations.
+
+ If you wish to destroy the records themselves, you can do records.association.each(&:destroy)
+
+ *Jon Leighton*
+
+* Add :bulk => true option to change_table to make all the schema changes defined in change_table block using a single ALTER statement. *Pratik Naik*
+
+ Example:
+
+ change_table(:users, :bulk => true) do |t|
+ t.string :company_name
+ t.change :birthdate, :datetime
+ end
+
+ This will now result in:
+
+ ALTER TABLE `users` ADD COLUMN `company_name` varchar(255), CHANGE `updated_at` `updated_at` datetime DEFAULT NULL
+
+* Removed support for accessing attributes on a has_and_belongs_to_many join table. This has been
+ documented as deprecated behaviour since April 2006. Please use has_many :through instead.
+ *Jon Leighton*
+
+* Added a create_association! method for has_one and belongs_to associations. *Jon Leighton*
+
+* Migration files generated from model and constructive migration generators
+ (for example, add_name_to_users) use the reversible migration's `change`
+ method instead of the ordinary `up` and `down` methods. *Prem Sichanugrist*
+
+* Removed support for interpolating string SQL conditions on associations. Instead, you should
+ use a proc, like so:
+
+ Before:
+
+ has_many :things, :conditions => 'foo = #{bar}'
+
+ After:
+
+ has_many :things, :conditions => proc { "foo = #{bar}" }
+
+ Inside the proc, 'self' is the object which is the owner of the association, unless you are
+ eager loading the association, in which case 'self' is the class which the association is within.
+
+ You can have any "normal" conditions inside the proc, so the following will work too:
+
+ has_many :things, :conditions => proc { ["foo = ?", bar] }
+
+ Previously :insert_sql and :delete_sql on has_and_belongs_to_many association allowed you to call
+ 'record' to get the record being inserted or deleted. This is now passed as an argument to
+ the proc.
+
+* Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH]. Example:
+
+ # Schema: User(name:string, password_digest:string, password_salt:string)
+ class User < ActiveRecord::Base
+ has_secure_password
+ end
+
+ user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch")
+ user.save # => false, password required
+ user.password = "mUc3m00RsqyRe"
+ user.save # => false, confirmation doesn't match
+ user.password_confirmation = "mUc3m00RsqyRe"
+ user.save # => true
+ user.authenticate("notright") # => false
+ user.authenticate("mUc3m00RsqyRe") # => user
+ User.find_by_name("david").try(:authenticate, "notright") # => nil
+ User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
+
+
+* When a model is generated add_index is added by default for belongs_to or references columns
+
+ rails g model post user:belongs_to will generate the following:
+
+ class CreatePosts < ActiveRecord::Migration
+ def up
+ create_table :posts do |t|
+ t.belongs_to :user
+ t.timestamps
+ end
+
+ add_index :posts, :user_id
+ end
+
+ def down
+ drop_table :posts
+ end
+ end
+
+ *Santiago Pastorino*
+
+* Setting the id of a belongs_to object will update the reference to the
+ object. [#2989 state:resolved]
+* ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed
+ to closer match normal Ruby dup and clone semantics.
+* Calling ActiveRecord::Base#clone will result in a shallow copy of the record,
+ including copying the frozen state. No callbacks will be called.
+* Calling ActiveRecord::Base#dup will duplicate the record, including calling
+ after initialize hooks. Frozen state will not be copied, and all associations will be cleared. A duped record will return true for new_record?, have a nil id field, and is saveable.
+* Migrations can be defined as reversible, meaning that the migration system
+ will figure out how to reverse your migration. To use reversible migrations, just define the "change" method. For example:
+ class MyMigration < ActiveRecord::Migration
+ def change
+ create_table(:horses) do
+ t.column :content, :text
+ t.column :remind_at, :datetime
+ end
+ end
+ end
+
+ Some things cannot be automatically reversed for you. If you know how to reverse those things, you should define 'up' and 'down' in your migration. If you define something in `change` that cannot be reversed, an IrreversibleMigration exception will be raised when going down.
+* Migrations should use instance methods rather than class methods:
+ class FooMigration < ActiveRecord::Migration
+ def up
+ ...
+ end
+ end
+
+ *Aaron Patterson*
+
+* has_one maintains the association with separate after_create/after_update instead
+ of a single after_save. *fxn*
+
+* The following code:
+
+ Model.limit(10).scoping { Model.count }
+
+ now generates the following SQL:
+
+ SELECT COUNT(*) FROM models LIMIT 10
+
+ This may not return what you want. Instead, you may with to do something
+ like this:
+
+ Model.limit(10).scoping { Model.all.size }
+
+ *Aaron Patterson*
+
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* Destroying records via nested attributes works independent of reject_if LH #6006 *Durran Jordan*
+
+* Delegate any? and many? to Model.scoped for consistency *Andrew White*
+
+* Quote the ORDER BY clause in batched finds - fixes #6620 *Andrew White*
+
+* Change exists? so records are not instantiated - fixes #6127. This prevents after_find
+ and after_initialize callbacks being triggered when checking for record existence.
+ *Andrew White*
+
+* Fix performance bug with attribute accessors which only occurred on Ruby 1.8.7, and ensure we
+ cache type-casted values when the column returned from the db contains non-standard chars.
+ *Jon Leighton*
+
+* Fix a performance regression introduced here 86acbf1cc050c8fa8c74a10c735e467fb6fd7df8
+ related to read_attribute method *Stian Grytøyr*
+
+
+## Rails 3.0.6 (April 5, 2011) ##
+
+* Un-deprecate reorder method *Sebastian Martinez*
+
+* Extensions are applied when calling +except+ or +only+ on relations.
+ Thanks to Iain Hecker.
+
+* Schemas set in set_table_name are respected by the mysql adapter. LH #5322
+
+* Fixed a bug when empty? was called on a grouped Relation that wasn't loaded.
+ LH #5829
+
+* Reapply extensions when using except and only. Thanks Iain Hecker.
+
+* Binary data is escaped when being inserted to SQLite3 Databases. Thanks
+ Naruse!
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* Model.where(:column => 1).where(:column => 2) will always produce an AND
+ query.
+ *Aaron Patterson*
+
+* Deprecated support for interpolated association conditions in the form of :conditions => 'foo = #{bar}'.
+
+ Instead, you should use a proc, like so:
+
+ Before:
+
+ has_many :things, :conditions => 'foo = #{bar}'
+
+ After:
+
+ has_many :things, :conditions => proc { "foo = #{bar}" }
+
+ Inside the proc, 'self' is the object which is the owner of the association, unless you are
+ eager loading the association, in which case 'self' is the class which the association is within.
+
+ You can have any "normal" conditions inside the proc, so the following will work too:
+
+ has_many :things, :conditions => proc { ["foo = ?", bar] }
+
+ Previously :insert_sql and :delete_sql on has_and_belongs_to_many association allowed you to call
+ 'record' to get the record being inserted or deleted. This is now passed as an argument to
+ the proc.
+
+ *Jon Leighton*
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* Added deprecation warning for has_and_belongs_to_many associations where the join table has
+ additional attributes other than the keys. Access to these attributes is removed in 3.1.
+ Please use has_many :through instead. *Jon Leighton*
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* Support find by class like this: Post.where(:name => Post)
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* Dramatic speed increase (see: http://engineering.attinteractive.com/2010/10/arel-two-point-ohhhhh-yaaaaaa/) *Aaron Patterson*
+
+* reorder is deprecated in favor of except(:order).order(...) *Santiago Pastorino*
+
+* except is now AR public API
+
+ Model.order('name').except(:order).order('salary')
+
+ generates:
+
+ SELECT * FROM models ORDER BY salary
+
+ *Santiago Pastorino*
+
+* The following code:
+
+ Model.limit(10).scoping { Model.count }
+
+ now generates the following SQL:
+
+ SELECT COUNT(*) FROM models LIMIT 10
+
+ This may not return what you want. Instead, you may with to do something
+ like this:
+
+ Model.limit(10).scoping { Model.all.size }
+
+ *Aaron Patterson*
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* Introduce a fix for CVE-2010-3993
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* Changed update_attribute to not run callbacks and update the record directly in the database *Neeraj Singh*
+
+* Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope *José Valim*
+
+* New rake task, db:migrate:status, displays status of migrations #4947 *Kevin Skoglund*
+
+* select and order for ActiveRecord now always concatenate nested calls. Use reorder if you want the original order to be overwritten *Santiago Pastorino*
+
+* PostgreSQL: ensure the database time zone matches Ruby's time zone #4895 *Aaron Patterson*
+
+* Fixed that ActiveRecord::Base.compute_type would swallow NoMethodError #4751 *Andrew Bloomgarden, Andrew White*
+
+* Add index length support for MySQL. #1852 *Emili Parreno, Pratik Naik*
+
+ Example:
+
+ add_index(:accounts, :name, :name => 'by_name', :length => 10)
+ => CREATE INDEX by_name ON accounts(name(10))
+
+ add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
+ => CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
+
+* find_or_create_by_attr(value, ...) works when attr is protected. #4457 *Santiago Pastorino, Marc-André Lafortune*
+
+* New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 *Brian Durand*
+
+* Serialized attributes are not converted to YAML if they are any of the formats that can be serialized to XML (like Hash, Array and Strings). *José Valim*
+
+* Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 *Curtis Hawthorne*
+
+* PostgreSQL: drop support for old postgres driver. Use pg 0.9.0 or later. *Jeremy Kemper*
+
+* Observers can prevent records from saving by returning false, just like before_save and friends. #4087 *Mislav Marohnić*
+
+* Add Relation extensions. *Pratik Naik*
+
+ users = User.where(:admin => true).extending(User::AdminPowers)
+
+ latest_users = User.order('created_at DESC') do
+ def posts_count
+ Post.count(:user_id => to_a.map(&:id))
+ end
+ end
+
+* To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 *Andrew White*
+
+* Silenced "SHOW FIELDS" and "SET SQL_AUTO_IS_NULL=0" statements from the MySQL driver to improve log signal to noise ration in development *DHH*
+
+* PostgreSQLAdapter: set time_zone to UTC when Base.default_timezone == :utc so that Postgres doesn't incorrectly offset-adjust values inserted into TIMESTAMP WITH TIME ZONE columns. #3777 *Jack Christensen*
+
+* Allow relations to be used as scope.
+
+ class Item
+ scope :red, where(:colour => 'red')
+ end
+
+ Item.red.limit(10) # Ten red items
+
+* Rename named_scope to scope. *Pratik Naik*
+
+* Changed ActiveRecord::Base.store_full_sti_class to be true by default reflecting the previously announced Rails 3 default *DHH*
+
+* Add Relation#except. *Pratik Naik*
+
+ one_red_item = Item.where(:colour => 'red').limit(1)
+ all_items = one_red_item.except(:where, :limit)
+
+* Add Relation#delete_all. *Pratik Naik*
+
+ Item.where(:colour => 'red').delete_all
+
+* Add Model.having and Relation#having. *Pratik Naik*
+
+ Developer.group("salary").having("sum(salary) > 10000").select("salary")
+
+* Add Relation#count. *Pratik Naik*
+
+ legends = People.where("age > 100")
+ legends.count
+ legends.count(:age, :distinct => true)
+ legends.select('id').count
+
+* Add Model.readonly and association_collection#readonly finder method. *Pratik Naik*
+
+ Post.readonly.to_a # Load all posts in readonly mode
+ @user.items.readonly(false).to_a # Load all the user items in writable mode
+
+* Add .lock finder method *Pratik Naik*
+
+ User.lock.where(:name => 'lifo').to_a
+
+ old_items = Item.where("age > 100")
+ old_items.lock.each {|i| .. }
+
+* Add Model.from and association_collection#from finder methods *Pratik Naik*
+
+ user = User.scoped
+ user.select('*').from('users, items')
+
+* Add relation.destroy_all *Pratik Naik*
+
+ old_items = Item.where("age > 100")
+ old_items.destroy_all
+
+* Add relation.exists? *Pratik Naik*
+
+ red_items = Item.where(:colours => 'red')
+ red_items.exists?
+ red_items.exists?(1)
+
+* Add find(ids) to relations. *Pratik Naik*
+
+ old_users = User.order("age DESC")
+ old_users.find(1)
+ old_users.find(1, 2, 3)
+
+* Add new finder methods to association collection. *Pratik Naik*
+
+ class User < ActiveRecord::Base
+ has_many :items
+ end
+
+ user = User.first
+ user.items.where(:items => {:colour => 'red'})
+ user.items.select('items.id')
+
+* Add relation.reload to force reloading the records. *Pratik Naik*
+
+ topics = Topic.scoped
+ topics.to_a # force load
+ topics.first # returns a cached record
+ topics.reload
+ topics.first # Fetches a new record from the database
+
+* Rename Model.conditions and relation.conditions to .where. *Pratik Naik*
+
+ Before :
+ User.conditions(:name => 'lifo')
+ User.select('id').conditions(["age > ?", 21])
+
+ Now :
+ User.where(:name => 'lifo')
+ User.select('id').where(["age > ?", 21])
+
+* Add Model.select/group/order/limit/joins/conditions/preload/eager_load class methods returning a lazy relation. *Pratik Naik*
+
+ Examples :
+
+ posts = Post.select('id).order('name') # Returns a lazy relation
+ posts.each {|p| puts p.id } # Fires "select id from posts order by name"
+
+* Model.scoped now returns a relation if invoked without any arguments. *Pratik Naik*
+
+ Example :
+
+ posts = Post.scoped
+ posts.size # Fires "select count(*) from posts" and returns the count
+ posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
+
+* Association inverses for belongs_to, has_one, and has_many. Optimization to reduce database queries. #3533 *Murray Steele*
+
+ # post.comments sets each comment's post without needing to :include
+ class Post < ActiveRecord::Base
+ has_many :comments, :inverse_of => :post
+ end
+
+* MySQL: add_ and change_column support positioning. #3286 *Ben Marini*
+
+* Reset your Active Record counter caches with the reset_counter_cache class method. #1211 *Mike Breen, Gabe da Silveira*
+
+* Remove support for SQLite 2. Please upgrade to SQLite 3+ or install the plugin from git://github.com/rails/sqlite2_adapter.git *Pratik Naik*
+
+* PostgreSQL: XML datatype support. #1874 *Leonardo Borges*
+
+* quoted_date converts time-like objects to ActiveRecord::Base.default_timezone before serialization. This allows you to use Time.now in find conditions and have it correctly be serialized as the current time in UTC when default_timezone == :utc. #2946 *Geoff Buesing*
+
+* SQLite: drop support for 'dbfile' option in favor of 'database.' #2363 *Paul Hinze, Jeremy Kemper*
+
+* Added :primary_key option to belongs_to associations. #765 *Szymon Nowak, Philip Hallstrom, Noel Rocha*
+ # employees.company_name references companies.name
+ Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name'
+
+* 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*
+
+
+## 2.3.2 Final (March 15, 2009) ##
+
+* Added ActiveRecord::Base.find_each and ActiveRecord::Base.find_in_batches for batch processing *DHH/Jamis Buck*
+
+* Added that ActiveRecord::Base.exists? can be called with no arguments #1817 *Scott Taylor*
+
+* Add Support for updating deeply nested models from a single form. #1202 *Eloy Duran*
+
+ class Book < ActiveRecord::Base
+ has_one :author
+ has_many :pages
+
+ accepts_nested_attributes_for :author, :pages
+ end
+
+* Make after_save callbacks fire only if the record was successfully saved. #1735 *Michael Lovitt*
+
+ Previously the callbacks would fire if a before_save cancelled saving.
+
+* Support nested transactions using database savepoints. #383 *Jonathan Viney, Hongli Lai*
+
+* Added dynamic scopes ala dynamic finders #1648 *Yaroslav Markin*
+
+* Fixed that ActiveRecord::Base#new_record? should return false (not nil) for existing records #1219 *Yaroslav Markin*
+
+* I18n the word separator for error messages. Introduces the activerecord.errors.format.separator translation key. #1294 *Akira Matsuda*
+
+* Add :having as a key to find and the relevant associations. *Emilio Tagua*
+
+* Added default_scope to Base #1381 [Paweł Kondzior]. Example:
+
+ class Person < ActiveRecord::Base
+ default_scope :order => 'last_name, first_name'
+ end
+
+ class Company < ActiveRecord::Base
+ has_many :people
+ end
+
+ Person.all # => Person.find(:all, :order => 'last_name, first_name')
+ Company.find(1).people # => Person.find(:all, :order => 'last_name, first_name', :conditions => { :company_id => 1 })
+
+
+## 2.2.1 RC2 (November 14th, 2008) ##
+
+* Ensure indices don't flip order in schema.rb #1266 *Jordi Bunster*
+
+* Fixed that serialized strings should never be type-casted (i.e. turning "Yes" to a boolean) #857 *Andreas Korth*
+
+
+## 2.2.0 RC1 (October 24th, 2008) ##
+
+* Skip collection ids reader optimization if using :finder_sql *Jeremy Kemper*
+
+* Add Model#delete instance method, similar to Model.delete class method. #1086 *Hongli Lai (Phusion)*
+
+* MySQL: cope with quirky default values for not-null text columns. #1043 *Frederick Cheung*
+
+* Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] *Geoff Buesing*
+
+* Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] *Emilio Tagua*
+
+* Added find_last_by dynamic finder #762 *Emilio Tagua*
+
+* Internal API: configurable association options and build_association method for reflections so plugins may extend and override. #985 *Hongli Lai (Phusion)*
+
+* Changed benchmarks to be reported in milliseconds *David Heinemeier Hansson*
+
+* Connection pooling. #936 *Nick Sieger*
+
+* Merge scoped :joins together instead of overwriting them. May expose scoping bugs in your code! #501 *Andrew White*
+
+* before_save, before_validation and before_destroy callbacks that return false will now ROLLBACK the transaction. Previously this would have been committed before the processing was aborted. #891 *Xavier Noria*
+
+* Transactional migrations for databases which support them. #834 *divoxx, Adam Wiggins, Tarmo Tänav*
+
+* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. *Andrew Stone, Nik Wakelin*
+
+* change_column_default preserves the not-null constraint. #617 *Tarmo Tänav*
+
+* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) *#334*
+
+* Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David Lowenfels] Example :
+
+ \# Ensure essay contains at least 100 words.
+ validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
+
+* Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example:
+
+ User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } }
+ Item.first :conditions => { :items => { :color => 'red' } }
+
+* Always treat integer :limit as byte length. #420 *Tarmo Tänav*
+
+* Partial updates don't update lock_version if nothing changed. #426 *Daniel Morrison*
+
+* Fix column collision with named_scope and :joins. #46 *Duncan Beevers, Mark Catley*
+
+* db:migrate:down and :up update schema_migrations. #369 *Michael Raidel, RaceCondition*
+
+* PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a bind variable. *Tarmo Tänav*
+
+* MySQL: rename_column preserves column defaults. #466 *Diego Algorta*
+
+* Add :from option to calculations. #397 *Ben Munat*
+
+* Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. *Jan De Poorter*
+
+* PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later. *Jeremy Kemper*
+
+* Added SQL escaping for :limit and :offset in MySQL *Jonathan Wiess*
+
+
+## 2.1.0 (May 31st, 2008) ##
+
+* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. *Rick Olson*
+
+* Add first/last methods to associations/named_scope. Resolved #226. *Ryan Bates*
+
+* Added SQL escaping for :limit and :offset #288 *Aaron Bedra, Steven Bristol, Jonathan Wiess*
+
+* Added first/last methods to associations/named_scope. Resolved #226. *Ryan Bates*
+
+* Ensure hm:t preloading honours reflection options. Resolves #137. *Frederick Cheung*
+
+* Added protection against duplicate migration names (Aslak Hellesøy) *#112*
+
+* Base#instantiate_time_object: eliminate check for Time.zone, since we can assume this is set if time_zone_aware_attributes is set to true *Geoff Buesing*
+
+* Time zone aware attribute methods use Time.zone.parse instead of #to_time for String arguments, so that offset information in String is respected. Resolves #105. *Scott Fleckenstein, Geoff Buesing*
+
+* Added change_table for migrations (Jeff Dean) [#71]. Example:
+
+ change_table :videos do |t|
+ t.timestamps # adds created_at, updated_at
+ t.belongs_to :goat # adds goat_id integer
+ t.string :name, :email, :limit => 20 # adds name and email both with a 20 char limit
+ t.remove :name, :email # removes the name and email columns
+ end
+
+* Fixed has_many :through .create with no parameters caused a "can't dup NilClass" error (Steven Soroka) *#85*
+
+* Added block-setting of attributes for Base.create like Base.new already has (Adam Meehan) *#39*
+
+* Fixed that pessimistic locking you reference the quoted table name (Josh Susser) *#67*
+
+* Fixed that change_column should be able to use :null => true on a field that formerly had false [Nate Wiger] *#26*
+
+* Added that the MySQL adapter should map integer to either smallint, int, or bigint depending on the :limit just like PostgreSQL *David Heinemeier Hansson*
+
+* Change validates_uniqueness_of :case_sensitive option default back to true (from [9160]). Love your database columns, don't LOWER them. *Rick Olson*
+
+* Add support for interleaving migrations by storing which migrations have run in the new schema_migrations table. Closes #11493 *Jordi Bunster*
+
+* ActiveRecord::Base#sum defaults to 0 if no rows are returned. Closes #11550 *Kamal Fariz Mahyuddin*
+
+* Ensure that respond_to? considers dynamic finder methods. Closes #11538. *James Mead*
+
+* Ensure that save on parent object fails for invalid has_one association. Closes #10518. *Pratik Naik*
+
+* Remove duplicate code from associations. *Pratik Naik*
+
+* Refactor HasManyThroughAssociation to inherit from HasManyAssociation. Association callbacks and <association>_ids= now work with hm:t. #11516 *Ruy Asan*
+
+* Ensure HABTM#create and HABTM#build do not load entire association. *Pratik Naik*
+
+* Improve documentation. *Xavier Noria, Jack Danger Canty, leethal*
+
+* Tweak ActiveRecord::Base#to_json to include a root value in the returned hash: {"post": {"title": ...}} *Rick Olson*
+
+ Post.find(1).to_json # => {"title": ...}
+ config.active_record.include_root_in_json = true
+ Post.find(1).to_json # => {"post": {"title": ...}}
+
+* Add efficient #include? to AssociationCollection (for has_many/has_many :through/habtm). *stopdropandrew*
+
+* PostgreSQL: create_ and drop_database support. #9042 *ez, pedz, Nick Sieger*
+
+* Ensure that validates_uniqueness_of works with with_scope. Closes #9235. *Nik Wakelin, cavalle*
+
+* Partial updates include only unsaved attributes. Off by default; set YourClass.partial_updates = true to enable. *Jeremy Kemper*
+
+* Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled *Geoff Buesing*
+
+* Fixed that validates_size_of :within works in associations #11295, #10019 *cavalle*
+
+* Track changes to unsaved attributes. *Jeremy Kemper*
+
+* Switched to UTC-timebased version numbers for migrations and the schema. This will as good as eliminate the problem of multiple migrations getting the same version assigned in different branches. Also added rake db:migrate:up/down to apply individual migrations that may need to be run when you merge branches #11458 *John Barnette*
+
+* Fixed that has_many :through would ignore the hash conditions #11447 *Emilio Tagua*
+
+* Fix issue where the :uniq option of a has_many :through association is ignored when find(:all) is called. Closes #9407 *cavalle*
+
+* Fix duplicate table alias error when including an association with a has_many :through association on the same join table. Closes #7310 *cavalle*
+
+* More efficient association preloading code that compacts a through_records array in a central location. Closes #11427 *Jack Danger Canty*
+
+* Improve documentation. *Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert*
+
+* Fixed that ActiveRecord#Base.find_or_create/initialize would not honor attr_protected/accessible when used with a hash #11422 *Emilio Tagua*
+
+* Added ActiveRecord#Base.all/first/last as aliases for find(:all/:first/:last) #11413 *nkallen, Chris O'Sullivan*
+
+* Merge the has_finder gem, renamed as 'named_scope'. #11404 *nkallen*
+
+ class Article < ActiveRecord::Base
+ named_scope :published, :conditions => {:published => true}
+ named_scope :popular, :conditions => ...
+ end
+
+ Article.published.paginate(:page => 1)
+ Article.published.popular.count
+ Article.popular.find(:first)
+ Article.popular.find(:all, :conditions => {...})
+
+ See http://pivots.pivotallabs.com/users/nick/blog/articles/284-hasfinder-it-s-now-easier-than-ever-to-create-complex-re-usable-sql-queries
+
+* Add has_one :through support. #4756 *Chris O'Sullivan*
+
+* Migrations: create_table supports primary_key_prefix_type. #10314 *student, Chris O'Sullivan*
+
+* Added logging for dependency load errors with fixtures #11056 *stuthulhu*
+
+* Time zone aware attributes use Time#in_time_zone *Geoff Buesing*
+
+* Fixed that scoped joins would not always be respected #6821 *Theory/Jack Danger Canty*
+
+* Ensure that ActiveRecord::Calculations disambiguates field names with the table name. #11027 *cavalle*
+
+* Added add/remove_timestamps to the schema statements for adding the created_at/updated_at columns on existing tables #11129 *jramirez*
+
+* Added ActiveRecord::Base.find(:last) #11338 *Emilio Tagua*
+
+* test_native_types expects DateTime.local_offset instead of DateTime.now.offset; fixes test breakage due to dst transition *Geoff Buesing*
+
+* Add :readonly option to HasManyThrough associations. #11156 *Emilio Tagua*
+
+* Improve performance on :include/:conditions/:limit queries by selectively joining in the pre-query. #9560 *dasil003*
+
+* Perf fix: Avoid the use of named block arguments. Closes #11109 *adymo*
+
+* PostgreSQL: support server versions 7.4 through 8.0 and the ruby-pg driver. #11127 *jdavis*
+
+* Ensure association preloading doesn't break when an association returns nil. ##11145 *GMFlash*
+
+* Make dynamic finders respect the :include on HasManyThrough associations. #10998. *cpytel*
+
+* Base#instantiate_time_object only uses Time.zone when Base.time_zone_aware_attributes is true; leverages Time#time_with_datetime_fallback for readability *Geoff Buesing*
+
+* Refactor ConnectionAdapters::Column.new_time: leverage DateTime failover behavior of Time#time_with_datetime_fallback *Geoff Buesing*
+
+* Improve associations performance by using symbol callbacks instead of string callbacks. #11108 *adymo*
+
+* Optimise the BigDecimal conversion code. #11110 *adymo*
+
+* Introduce the :readonly option to all associations. Records from the association cannot be saved. #11084 *Emilio Tagua*
+
+* Multiparameter attributes for time columns fail over to DateTime when out of range of Time *Geoff Buesing*
+
+* Base#instantiate_time_object uses Time.zone.local() *Geoff Buesing*
+
+* Add timezone-aware attribute readers and writers. #10982 *Geoff Buesing*
+
+* Instantiating time objects in multiparameter attributes uses Time.zone if available. #10982 *Rick Olson*
+
+* Add note about how ActiveRecord::Observer classes are initialized in a Rails app. #10980 *Xavier Noria*
+
+* MySQL: omit text/blob defaults from the schema instead of using an empty string. #10963 *mdeiters*
+
+* belongs_to supports :dependent => :destroy and :delete. #10592 *Jonathan Viney*
+
+* Introduce preload query strategy for eager :includes. #9640 *Frederick Cheung, Aliaksey Kandratsenka, codafoo*
+
+* Support aggregations in finder conditions. #10572 *Ryan Kinderman*
+
+* Organize and clean up the Active Record test suite. #10742 *John Barnette*
+
+* Ensure that modifying has_and_belongs_to_many actions clear the query cache. Closes #10840 *john.andrews*
+
+* Fix issue where Table#references doesn't pass a :null option to a *_type attribute for polymorphic associations. Closes #10753 *railsjitsu*
+
+* Fixtures: removed support for the ancient pre-YAML file format. #10736 *John Barnette*
+
+* More thoroughly quote table names. #10698 *dimdenis, lotswholetime, Jeremy Kemper*
+
+* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement. #10686 *Brendan Ribera*
+
+* Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the new ActiveSupport::Cache::* libraries *David Heinemeier Hansson*
+
+* Make sure CSV fixtures are compatible with ruby 1.9's new csv implementation. *JEG2*
+
+* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 *Sam*
+
+* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 *jamesh, Frederick Cheung, protocool*
+
+* Don't unnecessarily load has_many associations in after_update callbacks. Closes #6822 *stopdropandrew, canadaduane*
+
+* Eager belongs_to :include infers the foreign key from the association name rather than the class name. #10517 *Jonathan Viney*
+
+* SQLite: fix rename_ and remove_column for columns with unique indexes. #10576 *Brandon Keepers*
+
+* Ruby 1.9 compatibility. #10655 *Jeremy Kemper, Dirkjan Bussink*
+
+
+## 2.0.2 (December 16th, 2007) ##
+
+* Ensure optimistic locking handles nil #lock_version values properly. Closes #10510 *Rick Olson*
+
+* Make the Fixtures Test::Unit enhancements more supporting for double-loaded test cases. Closes #10379 *brynary*
+
+* Fix that validates_acceptance_of still works for non-existent tables (useful for bootstrapping new databases). Closes #10474 *Josh Susser*
+
+* Ensure that the :uniq option for has_many :through associations retains the order. #10463 *remvee*
+
+* Base.exists? doesn't rescue exceptions to avoid hiding SQL errors. #10458 *Michael Klishin*
+
+* Documentation: Active Record exceptions, destroy_all and delete_all. #10444, #10447 *Michael Klishin*
+
+
+## 2.0.1 (December 7th, 2007) ##
+
+* Removed query cache rescue as it could cause code to be run twice (closes #10408) *David Heinemeier Hansson*
+
+
+## 2.0.0 (December 6th, 2007) ##
+
+* Anchor DateTimeTest to fixed DateTime instead of a variable value based on Time.now#advance#to_datetime, so that this test passes on 64-bit platforms running Ruby 1.8.6+ *Geoff Buesing*
+
+* Fixed that the Query Cache should just be ignored if the database is misconfigured (so that the "About your applications environment" works even before the database has been created) *David Heinemeier Hansson*
+
+* Fixed that the truncation of strings longer than 50 chars should use inspect
+ so newlines etc are escaped #10385 [Norbert Crombach]
+* Fixed that habtm associations should be able to set :select as part of their definition and have that honored *David Heinemeier Hansson*
+
+* Document how the :include option can be used in Calculations::calculate. Closes #7446 *adamwiggins, ultimoamore*
+
+* Fix typo in documentation for polymorphic associations w/STI. Closes #7461 *johnjosephbachir*
+
+* Reveal that the type option in migrations can be any supported column type for your database but also include caveat about agnosticism. Closes #7531 *adamwiggins, mikong*
+
+* More complete documentation for find_by_sql. Closes #7912 *fearoffish*
+
+* Added ActiveRecord::Base#becomes to turn a record into one of another class (mostly relevant for STIs) [David Heinemeier Hansson]. Example:
+
+ render :partial => @client.becomes(Company) # renders companies/company instead of clients/client
+
+* Fixed that to_xml should not automatically pass :procs to associations included with :include #10162 *Cheah Chu Yeow*
+
+* Fix documentation typo introduced in [8250]. Closes #10339 *Henrik N*
+
+* Foxy fixtures: support single-table inheritance. #10234 *tom*
+
+* Foxy fixtures: allow mixed usage to make migration easier and more attractive. #10004 *lotswholetime*
+
+* Make the record_timestamps class-inheritable so it can be set per model. #10004 *tmacedo*
+
+* Allow validates_acceptance_of to use a real attribute instead of only virtual (so you can record that the acceptance occured) #7457 *ambethia*
+
+* DateTimes use Ruby's default calendar reform setting. #10201 *Geoff Buesing*
+
+* Dynamic finders on association collections respect association :order and :limit. #10211, #10227 *Patrick Joyce, Rick Olson, Jack Danger Canty*
+
+* Add 'foxy' support for fixtures of polymorphic associations. #10183 *John Barnette, David Lowenfels*
+
+* validates_inclusion_of and validates_exclusion_of allow formatted :message strings. #8132 *devrieda, Mike Naberezny*
+
+* attr_readonly behaves well with optimistic locking. #10188 *Nick Bugajski*
+
+* Base#to_xml supports the nil="true" attribute like Hash#to_xml. #8268 *Jonathan del Strother*
+
+* Change plings to the more conventional quotes in the documentation. Closes #10104 *Jack Danger Canty*
+
+* Fix HasManyThrough Association so it uses :conditions on the HasMany Association. Closes #9729 *Jack Danger Canty*
+
+* Ensure that column names are quoted. Closes #10134 *wesley.moxam*
+
+* Smattering of grammatical fixes to documentation. Closes #10083 *Bob Silva*
+
+* Enhance explanation with more examples for attr_accessible macro. Closes #8095 *fearoffish, Marcel Molina Jr.*
+
+* Update association/method mapping table to refected latest collection methods for has_many :through. Closes #8772 *Pratik Naik*
+
+* Explain semantics of having several different AR instances in a transaction block. Closes #9036 *jacobat, Marcel Molina Jr.*
+
+* Update Schema documentation to use updated sexy migration notation. Closes #10086 *Sam Granieri*
+
+* Make fixtures work with the new test subclasses. *Tarmo Tänav, Michael Koziarski*
+
+* Introduce finder :joins with associations. Same :include syntax but with inner rather than outer joins. #10012 *RubyRedRick*
+ # Find users with an avatar
+ User.find(:all, :joins => :avatar)
+
+ # Find posts with a high-rated comment.
+ Post.find(:all, :joins => :comments, :conditions => 'comments.rating > 3')
+
+* Associations: speedup duplicate record check. #10011 *Pratik Naik*
+
+* Make sure that << works on has_many associations on unsaved records. Closes #9989 *Josh Susser*
+
+* Allow association redefinition in subclasses. #9346 *wildchild*
+
+* Fix has_many :through delete with custom foreign keys. #6466 *naffis*
+
+* Foxy fixtures, from rathole (http://svn.geeksomnia.com/rathole/trunk/README)
+ - stable, autogenerated IDs
+ - specify associations (belongs_to, has_one, has_many) by label, not ID
+ - specify HABTM associations as inline lists
+ - autofill timestamp columns
+ - support YAML defaults
+ - fixture label interpolation
+ Enabled for fixtures that correspond to a model class and don't specify a primary key value. #9981 *John Barnette*
+
+* Add docs explaining how to protect all attributes using attr_accessible with no arguments. Closes #9631 *boone, rmm5t*
+
+* Update add_index documentation to use new options api. Closes #9787 *Kamal Fariz Mahyuddin*
+
+* Allow find on a has_many association defined with :finder_sql to accept id arguments as strings like regular find does. Closes #9916 *krishna*
+
+* Use VALID_FIND_OPTIONS when resolving :find scoping rather than hard coding the list of valid find options. Closes #9443 *sur*
+
+* Limited eager loading no longer ignores scoped :order. Closes #9561 *Jack Danger Canty, Josh Peek*
+
+* Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation. #6322 *brandon, Chris Cruft*
+
+* Assigning nil to a composed_of aggregate also sets its immediate value to nil. #9843 *Chris Cruft*
+
+* Ensure that mysql quotes table names with database names correctly. Closes #9911 *crayz*
+
+ "foo.bar" => "`foo`.`bar`"
+
+* Complete the assimilation of Sexy Migrations from ErrFree *Chris Wanstrath, PJ Hyett*
+ http://errtheblog.com/post/2381
+
+* Qualified column names work in hash conditions, like :conditions => { 'comments.created_at' => ... }. #9733 *Jack Danger Canty*
+
+* Fix regression where the association would not construct new finder SQL on save causing bogus queries for "WHERE owner_id = NULL" even after owner was saved. #8713 *Bryan Helmkamp*
+
+* Refactor association create and build so before & after callbacks behave consistently. #8854 *Pratik Naik, mortent*
+
+* Quote table names. Defaults to column quoting. #4593 *Justin Lynn, gwcoffey, eadz, Dmitry V. Sabanin, Jeremy Kemper*
+
+* Alias association #build to #new so it behaves predictably. #8787 *Pratik Naik*
+
+* Add notes to documentation regarding attr_readonly behavior with counter caches and polymorphic associations. Closes #9835 *saimonmoore, Rick Olson*
+
+* Observers can observe model names as symbols properly now. Closes #9869 *queso*
+
+* find_and_(initialize|create)_by methods can now properly initialize protected attributes *Tobias Lütke*
+
+* belongs_to infers the foreign key from the association name instead of from the class name. *Jeremy Kemper*
+
+* PostgreSQL: support multiline default values. #7533 *Carl Lerche, aguynamedryan, Rein Henrichs, Tarmo Tänav*
+
+* MySQL: fix change_column on not-null columns that don't accept dfeault values of ''. #6663 *Jonathan Viney, Tarmo Tänav*
+
+* validates_uniqueness_of behaves well with abstract superclasses and
+ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Beausoleil, Josh Peek, Tarmo Tänav, pat]
+* Warn about protected attribute assigments in development and test environments when mass-assigning to an attr_protected attribute. #9802 *Henrik N*
+
+* Speedup database date/time parsing. *Jeremy Kemper, Tarmo Tänav*
+
+* Fix calling .clear on a has_many :dependent=>:delete_all association. *Tarmo Tänav*
+
+* Allow change_column to set NOT NULL in the PostgreSQL adapter *Tarmo Tänav*
+
+* Fix that ActiveRecord would create attribute methods and override custom attribute getters if the method is also defined in Kernel.methods. *Rick Olson*
+
+* Don't call attr_readonly on polymorphic belongs_to associations, in case it matches the name of some other non-ActiveRecord class/module. *Rick Olson*
+
+* Try loading activerecord-<adaptername>-adapter gem before trying a plain require so you can use custom gems for the bundled adapters. Also stops gems from requiring an adapter from an old Active Record gem. *Jeremy Kemper, Derrick Spell*
+
+
+## 2.0.0 Preview Release (September 29th, 2007) Includes duplicates of changes from 1.14.2 - 1.15.3 ##
+
+* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 *Dan Manges*
+
+ class Comment < ActiveRecord::Base
+ # Automatically sets Article#comments_count as readonly.
+ belongs_to :article, :counter_cache => :comments_count
+ end
+
+ class Article < ActiveRecord::Base
+ attr_readonly :approved_comments_count
+ end
+
+* Make size for has_many :through use counter cache if it exists. Closes #9734 *Xavier Shay*
+
+* Remove DB2 adapter since IBM chooses to maintain their own adapter instead. *Jeremy Kemper*
+
+* Extract Oracle, SQLServer, and Sybase adapters into gems. *Jeremy Kemper*
+
+* Added fixture caching that'll speed up a normal fixture-powered test suite between 50% and 100% #9682 *Frederick Cheung*
+
+* Correctly quote id list for limited eager loading. #7482 *tmacedo*
+
+* Fixed that using version-targetted migrates would fail on loggers other than the default one #7430 *valeksenko*
+
+* Fixed rename_column for SQLite when using symbols for the column names #8616 *drodriguez*
+
+* Added the possibility of using symbols in addition to concrete classes with ActiveRecord::Observer#observe. #3998 *Robby Russell, Tarmo Tänav*
+
+* Added ActiveRecord::Base#to_json/from_json *David Heinemeier Hansson, Cheah Chu Yeow*
+
+* Added ActiveRecord::Base#from_xml [David Heinemeier Hansson]. Example:
+
+ xml = "<person><name>David</name></person>"
+ Person.new.from_xml(xml).name # => "David"
+
+* Define dynamic finders as real methods after first usage. *bscofield*
+
+* Deprecation: remove deprecated threaded_connections methods. Use allow_concurrency instead. *Jeremy Kemper*
+
+* Associations macros accept extension blocks alongside modules. #9346 *Josh Peek*
+
+* Speed up and simplify query caching. *Jeremy Kemper*
+
+* connection.select_rows 'sql' returns an array (rows) of arrays (field values). #2329 *Michael Schuerig*
+
+* Eager loading respects explicit :joins. #9496 *dasil003*
+
+* Extract Firebird, FrontBase, and OpenBase adapters into gems. #9508, #9509, #9510 *Jeremy Kemper*
+
+* RubyGem database adapters: expects a gem named activerecord-<database>-adapter with active_record/connection_adapters/<database>_adapter.rb in its load path. *Jeremy Kemper*
+
+* Fixed that altering join tables in migrations would fail w/ sqlite3 #7453 *TimoMihaljov/brandon*
+
+* Fix association writer with :dependent => :nullify. #7314 *Jonathan Viney*
+
+* OpenBase: update for new lib and latest Rails. Support migrations. #8748 *dcsesq*
+
+* Moved acts_as_tree into a plugin of the same name on the official Rails svn. #9514 *Pratik Naik*
+
+* Moved acts_as_nested_set into a plugin of the same name on the official Rails svn. #9516 *Josh Peek*
+
+* Moved acts_as_list into a plugin of the same name on the official Rails svn. *Josh Peek*
+
+* Explicitly require active_record/query_cache before using it. *Jeremy Kemper*
+
+* Fix bug where unserializing an attribute attempts to modify a frozen @attributes hash for a deleted record. *Rick Olson, marclove*
+
+* Performance: absorb instantiate and initialize_with_callbacks into the Base methods. *Jeremy Kemper*
+
+* Fixed that eager loading queries and with_scope should respect the :group option *David Heinemeier Hansson*
+
+* Improve performance and functionality of the postgresql adapter. Closes #8049 *roderickvd*
+
+ For more information see: http://dev.rubyonrails.org/ticket/8049
+
+* Don't clobber includes passed to has_many.count *Jack Danger Canty*
+
+* Make sure has_many uses :include when counting *Jack Danger Canty*
+
+* Change the implementation of ActiveRecord's attribute reader and writer methods *Michael Koziarski*
+ - Generate Reader and Writer methods which cache attribute values in hashes. This is to avoid repeatedly parsing the same date or integer columns. - Change exception raised when users use find with :select then try to access a skipped column. Plugins could override missing_attribute() to lazily load the columns. - Move method definition to the class, instead of the instance - Always generate the readers, writers and predicate methods.
+* Perform a deep #dup on query cache results so that modifying activerecord attributes does not modify the cached attributes. *Rick Olson*
+
+ \# Ensure that has_many :through associations use a count query instead of loading the target when #size is called. Closes #8800 [Pratik Naik]
+* Added :unless clause to validations #8003 [monki]. Example:
+
+ def using_open_id?
+ !identity_url.blank?
+ end
+
+ validates_presence_of :identity_url, :if => using_open_id?
+ validates_presence_of :username, :unless => using_open_id?
+ validates_presence_of :password, :unless => using_open_id?
+
+* Fix #count on a has_many :through association so that it recognizes the :uniq option. Closes #8801 *Pratik Naik*
+
+* Fix and properly document/test count(column_name) usage. Closes #8999 *Pratik Naik*
+
+* Remove deprecated count(conditions=nil, joins=nil) usage. Closes #8993 *Pratik Naik*
+
+* Change belongs_to so that the foreign_key assumption is taken from the association name, not the class name. Closes #8992 *Josh Susser*
+
+ OLD
+ belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id
+
+ NEW
+ belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is visitor_id
+
+* Remove spurious tests from deprecated_associations_test, most of these aren't deprecated, and are duplicated in associations_test. Closes #8987 *Pratik Naik*
+
+* Make create! on a has_many :through association return the association object. Not the collection. Closes #8786 *Pratik Naik*
+
+* Move from select * to select tablename.* to avoid clobbering IDs. Closes #8889 *dasil003*
+
+* Don't call unsupported methods on associated objects when using :include, :method with to_xml #7307, *Manfred Stienstra, jwilger*
+
+* Define collection singular ids method for has_many :through associations. #8763 *Pratik Naik*
+
+* Array attribute conditions work with proxied association collections. #8318 *Kamal Fariz Mahyuddin, theamazingrando*
+
+* Fix polymorphic has_one associations declared in an abstract class. #8638 *Pratik Naik, Dax Huiberts*
+
+* Fixed validates_associated should not stop on the first error. #4276 *mrj, Manfred Stienstra, Josh Peek*
+
+* Rollback if commit raises an exception. #8642 *kik, Jeremy Kemper*
+
+* Update tests' use of fixtures for the new collections api. #8726 *Kamal Fariz Mahyuddin*
+
+* Save associated records only if the association is already loaded. #8713 *Blaine*
+
+* MySQL: fix show_variable. #8448 *matt, Jeremy Kemper*
+
+* Fixtures: correctly delete and insert fixtures in a single transaction. #8553 *Michael Schuerig*
+
+* Fixtures: people(:technomancy, :josh) returns both fixtures. #7880 *technomancy, Josh Peek*
+
+* Calculations support non-numeric foreign keys. #8154 *Kamal Fariz Mahyuddin*
+
+* with_scope is protected. #8524 *Josh Peek*
+
+* Quickref for association methods. #7723 *marclove, Mindsweeper*
+
+* Calculations: return nil average instead of 0 when there are no rows to average. #8298 *davidw*
+
+* acts_as_nested_set: direct_children is sorted correctly. #4761 *Josh Peek, rails@33lc0.net*
+
+* Raise an exception if both attr_protected and attr_accessible are declared. #8507 *stellsmi*
+
+* SQLite, MySQL, PostgreSQL, Oracle: quote column names in column migration SQL statements. #8466 *marclove, lorenjohnson*
+
+* Allow nil serialized attributes with a set class constraint. #7293 *sandofsky*
+
+* Oracle: support binary fixtures. #7987 *Michael Schoen*
+
+* Fixtures: pull fixture insertion into the database adapters. #7987 *Michael Schoen*
+
+* Announce migration versions as they're performed. *Jeremy Kemper*
+
+* find gracefully copes with blank :conditions. #7599 *Dan Manges, johnnyb*
+
+* validates_numericality_of takes :greater_than, :greater_than_or_equal_to, :equal_to, :less_than, :less_than_or_equal_to, :odd, and :even options. #3952 *Bob Silva, Dan Kubb, Josh Peek*
+
+* MySQL: create_database takes :charset and :collation options. Charset defaults to utf8. #8448 *matt*
+
+* Find with a list of ids supports limit/offset. #8437 *hrudududu*
+
+* Optimistic locking: revert the lock version when an update fails. #7840 *plang*
+
+* Migrations: add_column supports custom column types. #7742 *jsgarvin, Theory*
+
+* Load database adapters on demand. Eliminates config.connection_adapters and RAILS_CONNECTION_ADAPTERS. Add your lib directory to the $LOAD_PATH and put your custom adapter in lib/active_record/connection_adapters/adaptername_adapter.rb. This way you can provide custom adapters as plugins or gems without modifying Rails. *Jeremy Kemper*
+
+* Ensure that associations with :dependent => :delete_all respect :conditions option. Closes #8034 *Jack Danger Canty, Josh Peek, Rick Olson*
+
+* belongs_to assignment creates a new proxy rather than modifying its target in-place. #8412 *mmangino@elevatedrails.com*
+
+* Fix column type detection while loading fixtures. Closes #7987 *roderickvd*
+
+* Document deep eager includes. #6267 *Josh Susser, Dan Manges*
+
+* Document warning that associations names shouldn't be reserved words. #4378 *murphy@cYcnus.de, Josh Susser*
+
+* Sanitize Base#inspect. #8392, #8623 *Nik Wakelin, jnoon*
+
+* Replace the transaction {|transaction|..} semantics with a new Exception ActiveRecord::Rollback. *Michael Koziarski*
+
+* Oracle: extract column length for CHAR also. #7866 *ymendel*
+
+* Document :allow_nil option for validates_acceptance_of since it defaults to true. *tzaharia*
+
+* Update documentation for :dependent declaration so that it explicitly uses the non-deprecated API. *Jack Danger Canty*
+
+* Add documentation caveat about when to use count_by_sql. *fearoffish*
+
+* Enhance documentation for increment_counter and decrement_counter. *fearoffish*
+
+* Provide brief introduction to what optimistic locking is. *fearoffish*
+
+* Add documentation for :encoding option to mysql adapter. *marclove*
+
+* Added short-hand declaration style to migrations (inspiration from Sexy Migrations, http://errtheblog.com/post/2381) [David Heinemeier Hansson]. Example:
+
+ create_table "products" do |t|
+ t.column "shop_id", :integer
+ t.column "creator_id", :integer
+ t.column "name", :string, :default => "Untitled"
+ t.column "value", :string, :default => "Untitled"
+ t.column "created_at", :datetime
+ t.column "updated_at", :datetime
+ end
+
+ ...can now be written as:
+
+ create_table :products do |t|
+ t.integer :shop_id, :creator_id
+ t.string :name, :value, :default => "Untitled"
+ t.timestamps
+ end
+
+* Use association name for the wrapper element when using .to_xml. Previous behavior lead to non-deterministic situations with STI and polymorphic associations. *Michael Koziarski, jstrachan*
+
+* Improve performance of calling .create on has_many :through associations. *evan*
+
+* Improved cloning performance by relying less on exception raising #8159 *Blaine*
+
+* Added ActiveRecord::Base.inspect to return a column-view like #<Post id:integer, title:string, body:text> *David Heinemeier Hansson*
+
+* Added yielding of Builder instance for ActiveRecord::Base#to_xml calls *David Heinemeier Hansson*
+
+* Small additions and fixes for ActiveRecord documentation. Closes #7342 *Jeremy McAnally*
+
+* Add helpful debugging info to the ActiveRecord::StatementInvalid exception in ActiveRecord::ConnectionAdapters::SqliteAdapter#table_structure. Closes #7925. *court3nay*
+
+* SQLite: binary escaping works with $KCODE='u'. #7862 *tsuka*
+
+* Base#to_xml supports serialized attributes. #7502 *jonathan*
+
+* Base.update_all :order and :limit options. Useful for MySQL updates that must be ordered to avoid violating unique constraints. *Jeremy Kemper*
+
+* Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 *Michael Koziarski, Jeremy Kemper*
+
+* PostgreSQL: remove DateTime -> Time downcast. Warning: do not enable translate_results for the C bindings if you have timestamps outside Time's domain. *Jeremy Kemper*
+
+* find_or_create_by_* takes a hash so you can create with more attributes than are in the method name. For example, Person.find_or_create_by_name(:name => 'Henry', :comments => 'Hi new user!') is equivalent to Person.find_by_name('Henry') || Person.create(:name => 'Henry', :comments => 'Hi new user!'). #7368 *Josh Susser*
+
+* Make sure with_scope takes both :select and :joins into account when setting :readonly. Allows you to save records you retrieve using method_missing on a has_many :through associations. *Michael Koziarski*
+
+* Allow a polymorphic :source for has_many :through associations. Closes #7143 *protocool*
+
+* Consistent public/protected/private visibility for chained methods. #7813 *Dan Manges*
+
+* Oracle: fix quoted primary keys and datetime overflow. #7798 *Michael Schoen*
+
+* Consistently quote primary key column names. #7763 *toolmantim*
+
+* Fixtures: fix YAML ordered map support. #2665 *Manuel Holtgrewe, nfbuckley*
+
+* DateTimes assume the default timezone. #7764 *Geoff Buesing*
+
+* Sybase: hide timestamp columns since they're inherently read-only. #7716 *Mike Joyce*
+
+* Oracle: overflow Time to DateTime. #7718 *Michael Schoen*
+
+* PostgreSQL: don't use async_exec and async_query with postgres-pr. #7727, #7762 *flowdelic, toolmantim*
+
+* Fix has_many :through << with custom foreign keys. #6466, #7153 *naffis, Rich Collins*
+
+* Test DateTime native type in migrations, including an edge case with dates
+ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
+* SQLServer: correctly schema-dump tables with no indexes or descending indexes. #7333, #7703 *Jakob Skjerning, Tom Ward*
+
+* SQLServer: recognize real column type as Ruby float. #7057 *sethladd, Tom Ward*
+
+* Added fixtures :all as a way of loading all fixtures in the fixture directory at once #7214 *Manfred Stienstra*
+
+* Added database connection as a yield parameter to ActiveRecord::Base.transaction so you can manually rollback [David Heinemeier Hansson]. Example:
+
+ transaction do |transaction|
+ david.withdrawal(100)
+ mary.deposit(100)
+ transaction.rollback! # rolls back the transaction that was otherwise going to be successful
+ end
+
+* Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method *Jamis Buck*
+
+* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however *Tobias Lütke*
+ Task.cache { Task.find(1); Task.find(1) } # => 1 query
+
+* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. *Jamis Buck*
+
+* Oracle: fix lob and text default handling. #7344 *gfriedrich, Michael Schoen*
+
+* SQLServer: don't choke on strings containing 'null'. #7083 *Jakob Skjerning*
+
+* MySQL: blob and text columns may not have defaults in 5.x. Update fixtures schema for strict mode. #6695 *Dan Kubb*
+
+* update_all can take a Hash argument. sanitize_sql splits into two methods for conditions and assignment since NULL values and delimiters are handled differently. #6583, #7365 *sandofsky, Assaf*
+
+* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id. #6778 *Jonathan Viney, timc*
+
+* Use Date#to_s(:db) for quoted dates. #7411 *Michael Schoen*
+
+* Don't create instance writer methods for class attributes. Closes #7401 *Rick Olson*
+
+* Docs: validations examples. #7343 *zackchandler*
+
+* Add missing tests ensuring callbacks work with class inheritance. Closes #7339 *sandofsky*
+
+* Fixtures use the table name and connection from set_fixture_class. #7330 *Anthony Eden*
+
+* Remove useless code in #attribute_present? since 0 != blank?. Closes #7249 *Josh Susser*
+
+* Fix minor doc typos. Closes #7157 *Josh Susser*
+
+* Fix incorrect usage of #classify when creating the eager loading join statement. Closes #7044 *Josh Susser*
+
+* SQLServer: quote table name in indexes query. #2928 *keithm@infused.org*
+
+* Subclasses of an abstract class work with single-table inheritance. #5704, #7284 *BertG, nick+rails@ag.arizona.edu*
+
+* Make sure sqlite3 driver closes open connections on disconnect *Rob Rasmussen*
+
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 *Jeremy McAnally*
+
+* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 *Dan Manges, Jeremy Kemper*
+
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 *Jonathan Viney, Manfred Stienstra, altano@bigfoot.com*
+
+* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 *Michael Schoen*
+
+* MySQL: retain SSL settings on reconnect. #6976 *randyv2*
+
+* Apply scoping during initialize instead of create. Fixes setting of foreign key when using find_or_initialize_by with scoping. *Cody Fauser*
+
+* SQLServer: handle [quoted] table names. #6635 *rrich*
+
+* acts_as_nested_set works with single-table inheritance. #6030 *Josh Susser*
+
+* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order. #4668, #7021 *eventualbuddha, Michael Schoen*
+
+* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 *Dan Manges*
+ Student.find(:all, :conditions => { :grade => 9..12 })
+
+* Fix the Oracle adapter for serialized attributes stored in CLOBs. Closes #6825 *mschoen, tdfowler*
+
+* [DOCS] Apply more documentation for ActiveRecord Reflection. Closes #4055 *Robby Russell*
+
+* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 *Caio Chassot*
+
+* Bring the sybase adapter up to scratch for 1.2 release. *jsheets*
+
+* Rollback new_record? and id when an exception is raised in a save callback. #6910 *Ben Curren, outerim*
+
+* Pushing a record on an association collection doesn't unnecessarily load all the associated records. *Obie Fernandez, Jeremy Kemper*
+
+* Oracle: fix connection reset failure. #6846 *leonlleslie*
+
+* Subclass instantiation doesn't try to explicitly require the corresponding subclass. #6840 *leei, Jeremy Kemper*
+
+* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 *protocool*
+
+* Consolidated different create and create! versions to call through to the base class with scope. This fixes inconsistencies, especially related to protected attribtues. Closes #5847 *Alexander Dymo, Tobias Lütke*
+
+* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using. #6764 *vitaly, Jeremy Kemper*
+
+* Add AssociationCollection#create! to be consistent with AssociationCollection#create when dealing with a foreign key that is a protected attribute *Cody Fauser*
+
+* Added counter optimization for AssociationCollection#any? so person.friends.any? won't actually load the full association if we have the count in a cheaper form *David Heinemeier Hansson*
+
+* Change fixture_path to a class inheritable accessor allowing test cases to have their own custom set of fixtures. #6672 *Zach Dennis*
+
+* Quote ActiveSupport::Multibyte::Chars. #6653 *Julian Tarkhanov*
+
+* Simplify query_attribute by typecasting the attribute value and checking whether it's nil, false, zero or blank. #6659 *Jonathan Viney*
+
+* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string. #5716 *Andreas Schwarz*
+
+* Run validations in the order they were declared. #6657 *obrie*
+
+* MySQL: detect when a NOT NULL column without a default value is misreported as default ''. Can't detect for string, text, and binary columns since '' is a legitimate default. #6156 *simon@redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper*
+
+* Simplify association proxy implementation by factoring construct_scope out of method_missing. #6643 *martin*
+
+* Oracle: automatically detect the primary key. #6594 *vesaria, Michael Schoen*
+
+* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml. #6607 *philbogle@gmail.com, ray.fortna@jobster.com, Michael Schoen*
+
+* Don't inspect unloaded associations. #2905 *lmarlow*
+
+* SQLite: use AUTOINCREMENT primary key in >= 3.1.0. #6588, #6616 *careo, lukfugl*
+
+* Cache inheritance_column. #6592 *Stefan Kaes*
+
+* Firebird: decimal/numeric support. #6408 *macrnic*
+
+* make add_order a tad faster. #6567 *Stefan Kaes*
+
+* Find with :include respects scoped :order. #5850
+
+* Support nil and Array in :conditions => { attr => value } hashes. #6548 *Assaf, Jeremy Kemper*
+ find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
+
+* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column. #6495 *Si*
+
+* SQLite: fix calculations workaround, remove count(distinct) query rewrite, cleanup test connection scripts. *Jeremy Kemper*
+
+* SQLite: count(distinct) queries supported in >= 3.2.6. #6544 *Bob Silva*
+
+* Dynamically generate reader methods for serialized attributes. #6362 *Stefan Kaes*
+
+* Deprecation: object transactions warning. *Jeremy Kemper*
+
+* has_one :dependent => :nullify ignores nil associates. #4848, #6528 *bellis@deepthought.org, janovetz, Jeremy Kemper*
+
+* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters. #6515 *Michael Schoen*
+
+* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 *obrie*
+
+* Document other options available to migration's add_column. #6419 *grg*
+
+* MySQL: all_hashes compatibility with old MysqlRes class. #6429, #6601 *Jeremy Kemper*
+
+* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. *Jonathan Viney*
+
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters *Rick Olson*
+
+* Restore eager condition interpolation, document it's differences *Rick Olson*
+
+* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 *Jacob Fugal, Jeremy Kemper*
+
+* Add #delete support to has_many :through associations. Closes #6049 *Martin Landers*
+
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 *Rick Olson*
+
+* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 *turnip@turnipspatch.com*
+
+* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 *wreese@gmail.com*
+
+* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 *jonathan*
+
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 *Bob Silva*
+
+* The has_many create method works with polymorphic associations. #6361 *Dan Peterson*
+
+* MySQL: introduce Mysql::Result#all_hashes to support further optimization. #5581 *Stefan Kaes*
+
+* save! shouldn't validate twice. #6324 *maiha, Bob Silva*
+
+* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values. #1887, #5780 *Michael Schuerig*
+
+* Add an attribute reader method for ActiveRecord::Base.observers *Rick Olson*
+
+* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins. #6287 *Bob Silva*
+
+* has_one associations with a nil target may be safely marshaled. #6279 *norbauer, Jeremy Kemper*
+
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects *Michael Koziarski*
+
+* Add a :namespace option to AR::Base#to_xml *Michael Koziarski*
+
+* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. *Jeremy Kemper*
+
+* Mock Time.now for more accurate Touch mixin tests. #6213 *Dan Peterson*
+
+* Improve yaml fixtures error reporting. #6205 *Bruce Williams*
+
+* Rename AR::Base#quote so people can use that name in their models. #3628 *Michael Koziarski*
+
+* Add deprecation warning for inferred foreign key. #6029 *Josh Susser*
+
+* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 *jimw@mysql.com*
+
+* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true. #6024 *Josh Susser*
+
+* Document validates_presences_of behavior with booleans: you probably want validates_inclusion_of :attr, :in => [true, false]. #2253 *Bob Silva*
+
+* Optimistic locking: gracefully handle nil versions, treat as zero. #5908 *Tom Ward*
+
+* to_xml: the :methods option works on arrays of records. #5845 *Josh Starcher*
+
+* Deprecation: update docs. #5998 *Jakob Skjerning, Kevin Clark*
+
+* Add some XmlSerialization tests for ActiveRecord *Rick Olson*
+
+* has_many :through conditions are sanitized by the associating class. #5971 *martin.emde@gmail.com*
+
+* Tighten rescue clauses. #5985 *james@grayproductions.net*
+
+* Fix spurious newlines and spaces in AR::Base#to_xml output *Jamis Buck*
+
+* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database. #5927 *Chris Mear, Jonathan Viney*
+
+* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name. #5911 *Jonathan Viney*
+
+* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german. #5894 *Tom Ward, kruth@bfpi*
+
+* SQLServer: fix eager association test. #5901 *Tom Ward*
+
+* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance. #5838 *Kevin Clark*
+
+* Fixtures: correct escaping of \n and \r. #5859 *evgeny.zislis@gmail.com*
+
+* Migrations: gracefully handle missing migration files. #5857 *eli.gordon@gmail.com*
+
+* MySQL: update test schema for MySQL 5 strict mode. #5861 *Tom Ward*
+
+* to_xml: correct naming of included associations. #5831 *Josh Starcher*
+
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 *Josh Susser*
+
+* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. *Jeremy Kemper*
+
+ # Create a tagging to associate the post and tag.
+ post.tags << Tag.find_by_name('old')
+ post.tags.create! :name => 'general'
+
+ # Would have been:
+ post.taggings.create!(:tag => Tag.find_by_name('finally')
+ transaction do
+ post.taggings.create!(:tag => Tag.create!(:name => 'general'))
+ end
+
+* Cache nil results for :included has_one associations also. #5787 *Michael Schoen*
+
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. *Tobias Lütke*
+
+* Nested classes are given table names prefixed by the singular form of the parent's table name. *Jeremy Kemper*
+ Example: Invoice::Lineitem is given table name invoice_lineitems
+
+* Migrations: uniquely name multicolumn indexes so you don't have to. *Jeremy Kemper*
+ # people_active_last_name_index, people_active_deactivated_at_index
+ add_index :people, [:active, :last_name]
+ add_index :people, [:active, :deactivated_at]
+ remove_index :people, [:active, :last_name]
+ remove_index :people, [:active, :deactivated_at]
+
+ WARNING: backward-incompatibility. Multicolumn indexes created before this
+ revision were named using the first column name only. Now they're uniquely
+ named using all indexed columns.
+
+ To remove an old multicolumn index, remove_index :table_name, :first_column
+
+* Fix for deep includes on the same association. *richcollins@gmail.com*
+
+* Tweak fixtures so they don't try to use a non-ActiveRecord class. *Kevin Clark*
+
+* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore. *Rick Olson*
+
+* Document find's :from option. Closes #5762. *andrew@redlinesoftware.com*
+
+* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema. #5280 *guy.naor@famundo.com*
+
+* Replace Reloadable with Reloadable::Deprecated. *Nicholas Seckar*
+
+* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. *Michael Schoen*
+
+* Add documentation for how to disable timestamps on a per model basis. Closes #5684. *matt@mattmargolis.net Marcel Molina Jr.*
+
+* Don't save has_one associations unnecessarily. #5735 *Jonathan Viney*
+
+* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. *Rick Olson*
+
+* Formally deprecate the deprecated finders. *Michael Koziarski*
+
+* Formally deprecate rich associations. *Michael Koziarski*
+
+* Fixed that default timezones for new / initialize should uphold utc setting #5709 *daniluk@yahoo.com*
+
+* Fix announcement of very long migration names. #5722 *blake@near-time.com*
+
+* The exists? class method should treat a string argument as an id rather than as conditions. #5698 *jeremy@planetargon.com*
+
+* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 *alexkwolfe@gmail.com*
+
+* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
+
+ assert (Topic.exists?(:author_name => "David"))
+ assert (Topic.exists?(:author_name => "Mary", :approved => true))
+ assert (Topic.exists?(["parent_id = ?", 1]))
+
+* Schema dumper quotes date :default values. *Dave Thomas*
+
+* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. *Dan Peterson*
+
+* Factor the attribute#{suffix} methods out of method_missing for easier extension. *Jeremy Kemper*
+
+* Patch sql injection vulnerability when using integer or float columns. *Jamis Buck*
+
+* Allow #count through a has_many association to accept :include. *Dan Peterson*
+
+* create_table rdoc: suggest :id => false for habtm join tables. *Zed Shaw*
+
+* PostgreSQL: return array fields as strings. #4664 *Robby Russell*
+
+* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 *Tom Ward*
+
+* SQLServer: fix db:schema:dump case-sensitivity. #4684 *Will Rogers*
+
+* Oracle: BigDecimal support. #5667 *Michael Schoen*
+
+* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 *robbat2@gentoo.org, work@ashleymoran.me.uk*
+
+* Firebird migrations support. #5337 *Ken Kunz <kennethkunz@gmail.com>*
+
+* PostgreSQL: create/drop as postgres user. #4790 *mail@matthewpainter.co.uk, mlaster@metavillage.com*
+
+* Update callbacks documentation. #3970 *Robby Russell <robby@planetargon.com>*
+
+* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 *tietew@tietew.net*
+
+* PostgreSQL: correctly quote microseconds in timestamps. #5641 *rick@rickbradley.com*
+
+* Clearer has_one/belongs_to model names (account has_one :user). #5632 *matt@mattmargolis.net*
+
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 *Michael Schoen*
+
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 *Michael Schoen*
+
+* Added :group to available options for finds done on associations #5516 *mike@michaeldewey.org*
+
+* Minor tweak to improve performance of ActiveRecord::Base#to_param.
+
+* Observers also watch subclasses created after they are declared. #5535 *daniels@pronto.com.au*
+
+* Removed deprecated timestamps_gmt class methods. *Jeremy Kemper*
+
+* rake build_mysql_database grants permissions to rails@localhost. #5501 *brianegge@yahoo.com*
+
+* PostgreSQL: support microsecond time resolution. #5492 *alex@msgpad.com*
+
+* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
+
+* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. *Sam Stephenson*
+
+* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). *Shugo Maeda*
+ # Obtain an exclusive lock on person 1 so we can safely increment visits.
+ Person.transaction do
+ # select * from people where id=1 for update
+ person = Person.find(1, :lock => true)
+ person.visits += 1
+ person.save!
+ end
+
+* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. *Jeremy Kemper*
+
+* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. *Jeremy Kemper*
+
+* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. *Nicholas Seckar*
+
+* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
+
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization *David Heinemeier Hansson*
+
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
+
+ Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
+
+ ...is the same as:
+ Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
+
+ This makes it easier to pass in the options from a form or otherwise outside.
+
+
+* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 *kennethkunz@gmail.com*
+
+* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 *alex@purefiction.net*
+
+* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 *greg@lapcominc.com*
+
+* Fixed problems with eager loading and counting on SQL Server #5212 *kajism@yahoo.com*
+
+* Fixed that count distinct should use the selected column even when using :include #5251 *anna@wota.jp*
+
+* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 *alex@purefiction.net*
+
+* Allow models to override to_xml. #4989 *Blair Zajac <blair@orcaware.com>*
+
+* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket. #5247 *shimbo@is.naist.jp*
+
+* Records and arrays of records are bound as quoted ids. *Jeremy Kemper*
+ Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
+ Foo.find(:first, :conditions => ['bar_id = ?', bar])
+
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty *David Heinemeier Hansson*
+
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite. *Rick Olson*
+
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 *Josh Susser*
+
+* Provide Association Extensions access to the instance that the association is being accessed from.
+ Closes #4433 *Josh Susser*
+
+* Update OpenBase adaterp's maintainer's email address. Closes #5176. *Derrick Spell*
+
+* Add a quick note about :select and eagerly included associations. *Rick Olson*
+
+* Add docs for the :as option in has_one associations. Closes #5144 *cdcarter@gmail.com*
+
+* Fixed that has_many collections shouldn't load the entire association to do build or create *David Heinemeier Hansson*
+
+* Added :allow_nil option for aggregations #5091 *Ian White*
+
+* Fix Oracle boolean support and tests. Closes #5139. *Michael Schoen*
+
+* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) *Jeremy Kemper*
+
+* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 *info@loobmedia.com*
+
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. *pdcawley@bofh.org.uk*
+
+* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 *keegan@thebasement.org*
+
+* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 *kajism@yahoo.com*
+
+* Allow :uniq => true with has_many :through associations. *Jeremy Kemper*
+
+* Ensure that StringIO is always available for the Schema dumper. *Marcel Molina Jr.*
+
+* Allow AR::Base#to_xml to include methods too. Closes #4921. *johan@textdrive.com*
+
+* Replace superfluous name_to_class_name variant with camelize. *Marcel Molina Jr.*
+
+* Replace alias method chaining with Module#alias_method_chain. *Marcel Molina Jr.*
+
+* Replace Ruby's deprecated append_features in favor of included. *Marcel Molina Jr.*
+
+* Remove duplicate fixture entry in comments.yml. Closes #4923. *Blair Zajac <blair@orcaware.com>*
+
+* Update FrontBase adapter to check binding version. Closes #4920. *mlaster@metavillage.com*
+
+* New Frontbase connections don't start in auto-commit mode. Closes #4922. *mlaster@metavillage.com*
+
+* When grouping, use the appropriate option key. *Marcel Molina Jr.*
+
+* Only modify the sequence name in the FrontBase adapter if the FrontBase adapter is actually being used. *Marcel Molina Jr.*
+
+* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. *mlaster@metavillage.com*
+
+* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. *Francois Beausoleil <francois.beausoleil@gmail.com>*
+
+* Fix syntax error in documentation. Closes #4679. *Mislav Marohnić*
+
+* Add Oracle support for CLOB inserts. Closes #4748. *schoenm@earthlink.net sandra.metz@duke.edu*
+
+* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. *kajism@yahoo.com*
+
+* Add support for :order option to with_scope. Closes #3887. *eric.daspet@survol.net*
+
+* Prettify output of schema_dumper by making things line up. Closes #4241 *Caio Chassot <caio@v2studio.com>*
+
+* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. *mlaster@metavillage.com*
+
+* Sybase Adapter type conversion cleanup. Closes #4736. *dev@metacasa.net*
+
+* Fix bug where calculations with long alias names return null. *Rick Olson*
+
+* Raise error when trying to add to a has_many :through association. Use the Join Model instead. *Rick Olson*
+
+ @post.tags << @tag # BAD
+ @post.taggings.create(:tag => @tag) # GOOD
+
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) *Rick Olson*
+
+* Update inconsistent migrations documentation. #4683 *machomagna@gmail.com*
+
+* Add ActiveRecord::Errors#to_xml *Jamis Buck*
+
+* Properly quote index names in migrations (closes #4764) *John Long*
+
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. *Rick Olson*
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions *Rick Olson*
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. *Rick Olson*
+
+* DRY up association collection reader method generation. *Marcel Molina Jr.*
+
+* DRY up and tweak style of the validation error object. *Marcel Molina Jr.*
+
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) *Rick Olson*
+
+ class Account < ActiveRecord::Base
+ validates_uniqueness_of :email, :case_sensitive => false
+ end
+
+* Allow multiple association extensions with :extend option (closes #4666) *Josh Susser*
+
+ class Account < ActiveRecord::Base
+ has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
+ end
+
+ *1.15.3* (March 12th, 2007)
+
+ * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
+
+ * Consistently quote primary key column names. #7763 [toolmantim]
+
+ * Fixtures: fix YAML ordered map support. #2665 [Manuel Holtgrewe, nfbuckley]
+
+ * Fix has_many :through << with custom foreign keys. #6466, #7153 [naffis, Rich Collins]
+
+
+## 1.15.2 (February 5th, 2007) ##
+
+* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 *Dan Manges*
+ Student.find(:all, :conditions => { :grade => 9..12 })
+
+* Don't create instance writer methods for class attributes. *Rick Olson*
+
+* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. *Jamis Buck*
+
+* SQLServer: don't choke on strings containing 'null'. #7083 *Jakob Skjerning*
+
+* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column. #6495 *Si*
+
+* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id. #6778 *Jonathan Viney, timc*
+
+* Fixtures use the table name and connection from set_fixture_class. #7330 *Anthony Eden*
+
+* SQLServer: quote table name in indexes query. #2928 *keithm@infused.org*
+
+
+## 1.15.1 (January 17th, 2007) ##
+
+* Fix nodoc breaking of adapters
+
+
+## 1.15.0 (January 16th, 2007) ##
+
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 *Jeremy McAnally*
+
+* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 *Dan Manges, Jeremy Kemper*
+
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 *Jonathan Viney, Manfred Stienstra, altano@bigfoot.com*
+
+* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 *Michael Schoen*
+
+* MySQL: retain SSL settings on reconnect. #6976 *randyv2*
+
+* SQLServer: handle [quoted] table names. #6635 *rrich*
+
+* acts_as_nested_set works with single-table inheritance. #6030 *Josh Susser*
+
+* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order. #4668, #7021 *eventualbuddha, Michael Schoen*
+
+* Fix the Oracle adapter for serialized attributes stored in CLOBs. Closes #6825 *mschoen, tdfowler*
+
+* [DOCS] Apply more documentation for ActiveRecord Reflection. Closes #4055 *Robby Russell*
+
+* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 *Caio Chassot*
+
+* Bring the sybase adapter up to scratch for 1.2 release. *jsheets*
+
+* Oracle: fix connection reset failure. #6846 *leonlleslie*
+
+* Subclass instantiation doesn't try to explicitly require the corresponding subclass. #6840 *leei, Jeremy Kemper*
+
+* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 *protocool*
+
+* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using. #6764 *vitaly, Jeremy Kemper*
+
+* Support nil and Array in :conditions => { attr => value } hashes. #6548 *Assaf, Jeremy Kemper*
+ find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
+
+* Quote ActiveSupport::Multibyte::Chars. #6653 *Julian Tarkhanov*
+
+* MySQL: detect when a NOT NULL column without a default value is misreported as default ''. Can't detect for string, text, and binary columns since '' is a legitimate default. #6156 *simon@redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper*
+
+* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string. #5716 *Andreas Schwarz*
+
+* Oracle: automatically detect the primary key. #6594 *vesaria, Michael Schoen*
+
+* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml. #6607 *philbogle@gmail.com, ray.fortna@jobster.com, Michael Schoen*
+
+* Firebird: decimal/numeric support. #6408 *macrnic*
+
+* Find with :include respects scoped :order. #5850
+
+* Dynamically generate reader methods for serialized attributes. #6362 *Stefan Kaes*
+
+* Deprecation: object transactions warning. *Jeremy Kemper*
+
+* has_one :dependent => :nullify ignores nil associates. #6528 *janovetz, Jeremy Kemper*
+
+* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters. #6515 *Michael Schoen*
+
+* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 *obrie*
+
+* Document other options available to migration's add_column. #6419 *grg*
+
+* MySQL: all_hashes compatibility with old MysqlRes class. #6429, #6601 *Jeremy Kemper*
+
+* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. *Jonathan Viney*
+
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters *Rick Olson*
+
+* Restore eager condition interpolation, document it's differences *Rick Olson*
+
+* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 *Jacob Fugal, Jeremy Kemper*
+
+* Add #delete support to has_many :through associations. Closes #6049 *Martin Landers*
+
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 *Rick Olson*
+
+* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 *turnip@turnipspatch.com*
+
+* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 *wreese@gmail.com*
+
+* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 *jonathan*
+
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 *Bob Silva*
+
+* The has_many create method works with polymorphic associations. #6361 *Dan Peterson*
+
+* MySQL: introduce Mysql::Result#all_hashes to support further optimization. #5581 *Stefan Kaes*
+
+* save! shouldn't validate twice. #6324 *maiha, Bob Silva*
+
+* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values. #1887, #5780 *Michael Schuerig*
+
+* Add an attribute reader method for ActiveRecord::Base.observers *Rick Olson*
+
+* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins. #6287 *Bob Silva*
+
+* has_one associations with a nil target may be safely marshaled. #6279 *norbauer, Jeremy Kemper*
+
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects *Michael Koziarski*
+
+* Add a :namespace option to AR::Base#to_xml *Michael Koziarski*
+
+* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. *Jeremy Kemper*
+
+* Mock Time.now for more accurate Touch mixin tests. #6213 *Dan Peterson*
+
+* Improve yaml fixtures error reporting. #6205 *Bruce Williams*
+
+* Rename AR::Base#quote so people can use that name in their models. #3628 *Michael Koziarski*
+
+* Add deprecation warning for inferred foreign key. #6029 *Josh Susser*
+
+* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 *jimw@mysql.com*
+
+* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true. #6024 *Josh Susser*
+
+* Optimistic locking: gracefully handle nil versions, treat as zero. #5908 *Tom Ward*
+
+* to_xml: the :methods option works on arrays of records. #5845 *Josh Starcher*
+
+* has_many :through conditions are sanitized by the associating class. #5971 *martin.emde@gmail.com*
+
+* Fix spurious newlines and spaces in AR::Base#to_xml output *Jamis Buck*
+
+* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database. #5927 *Chris Mear, Jonathan Viney*
+
+* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name. #5911 *Jonathan Viney*
+
+* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german. #5894 *Tom Ward, kruth@bfpi*
+
+* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance. #5838 *Kevin Clark*
+
+* Fixtures: correct escaping of \n and \r. #5859 *evgeny.zislis@gmail.com*
+
+* Migrations: gracefully handle missing migration files. #5857 *eli.gordon@gmail.com*
+
+* MySQL: update test schema for MySQL 5 strict mode. #5861 *Tom Ward*
+
+* to_xml: correct naming of included associations. #5831 *Josh Starcher*
+
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 *Josh Susser*
+
+* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. *Jeremy Kemper*
+
+ # Create a tagging to associate the post and tag.
+ post.tags << Tag.find_by_name('old')
+ post.tags.create! :name => 'general'
+
+ # Would have been:
+ post.taggings.create!(:tag => Tag.find_by_name('finally')
+ transaction do
+ post.taggings.create!(:tag => Tag.create!(:name => 'general'))
+ end
+
+* Cache nil results for :included has_one associations also. #5787 *Michael Schoen*
+
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. *Tobias Lütke*
+
+* Nested classes are given table names prefixed by the singular form of the parent's table name. *Jeremy Kemper*
+ Example: Invoice::Lineitem is given table name invoice_lineitems
+
+* Migrations: uniquely name multicolumn indexes so you don't have to. *Jeremy Kemper*
+ # people_active_last_name_index, people_active_deactivated_at_index
+ add_index :people, [:active, :last_name]
+ add_index :people, [:active, :deactivated_at]
+ remove_index :people, [:active, :last_name]
+ remove_index :people, [:active, :deactivated_at]
+
+ WARNING: backward-incompatibility. Multicolumn indexes created before this
+ revision were named using the first column name only. Now they're uniquely
+ named using all indexed columns.
+
+ To remove an old multicolumn index, remove_index :table_name, :first_column
+
+* Fix for deep includes on the same association. *richcollins@gmail.com*
+
+* Tweak fixtures so they don't try to use a non-ActiveRecord class. *Kevin Clark*
+
+* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore. *Rick Olson*
+
+* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema. #5280 *guy.naor@famundo.com*
+
+* Replace Reloadable with Reloadable::Deprecated. *Nicholas Seckar*
+
+* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. *Michael Schoen*
+
+* Don't save has_one associations unnecessarily. #5735 *Jonathan Viney*
+
+* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. *Rick Olson*
+
+* Formally deprecate the deprecated finders. *Michael Koziarski*
+
+* Formally deprecate rich associations. *Michael Koziarski*
+
+* Fixed that default timezones for new / initialize should uphold utc setting #5709 *daniluk@yahoo.com*
+
+* Fix announcement of very long migration names. #5722 *blake@near-time.com*
+
+* The exists? class method should treat a string argument as an id rather than as conditions. #5698 *jeremy@planetargon.com*
+
+* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 *alexkwolfe@gmail.com*
+
+* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
+
+ assert (Topic.exists?(:author_name => "David"))
+ assert (Topic.exists?(:author_name => "Mary", :approved => true))
+ assert (Topic.exists?(["parent_id = ?", 1]))
+
+* Schema dumper quotes date :default values. *Dave Thomas*
+
+* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. *Dan Peterson*
+
+* Factor the attribute#{suffix} methods out of method_missing for easier extension. *Jeremy Kemper*
+
+* Patch sql injection vulnerability when using integer or float columns. *Jamis Buck*
+
+* Allow #count through a has_many association to accept :include. *Dan Peterson*
+
+* create_table rdoc: suggest :id => false for habtm join tables. *Zed Shaw*
+
+* PostgreSQL: return array fields as strings. #4664 *Robby Russell*
+
+* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 *Tom Ward*
+
+* SQLServer: fix db:schema:dump case-sensitivity. #4684 *Will Rogers*
+
+* Oracle: BigDecimal support. #5667 *Michael Schoen*
+
+* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 *robbat2@gentoo.org, work@ashleymoran.me.uk*
+
+* Firebird migrations support. #5337 *Ken Kunz <kennethkunz@gmail.com>*
+
+* PostgreSQL: create/drop as postgres user. #4790 *mail@matthewpainter.co.uk, mlaster@metavillage.com*
+
+* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 *tietew@tietew.net*
+
+* PostgreSQL: correctly quote microseconds in timestamps. #5641 *rick@rickbradley.com*
+
+* Clearer has_one/belongs_to model names (account has_one :user). #5632 *matt@mattmargolis.net*
+
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 *Michael Schoen*
+
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 *Michael Schoen*
+
+* Added :group to available options for finds done on associations #5516 *mike@michaeldewey.org*
+
+* Observers also watch subclasses created after they are declared. #5535 *daniels@pronto.com.au*
+
+* Removed deprecated timestamps_gmt class methods. *Jeremy Kemper*
+
+* rake build_mysql_database grants permissions to rails@localhost. #5501 *brianegge@yahoo.com*
+
+* PostgreSQL: support microsecond time resolution. #5492 *alex@msgpad.com*
+
+* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
+
+* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. *Sam Stephenson*
+
+* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). *Shugo Maeda*
+ # Obtain an exclusive lock on person 1 so we can safely increment visits.
+ Person.transaction do
+ # select * from people where id=1 for update
+ person = Person.find(1, :lock => true)
+ person.visits += 1
+ person.save!
+ end
+
+* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. *Jeremy Kemper*
+
+* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. *Jeremy Kemper*
+
+* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. *Nicholas Seckar*
+
+* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
+
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization *David Heinemeier Hansson*
+
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
+
+ Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
+
+ ...is the same as:
+ Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
+
+ This makes it easier to pass in the options from a form or otherwise outside.
+
+
+* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 *kennethkunz@gmail.com*
+
+* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 *alex@purefiction.net*
+
+* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 *greg@lapcominc.com*
+
+* Fixed problems with eager loading and counting on SQL Server #5212 *kajism@yahoo.com*
+
+* Fixed that count distinct should use the selected column even when using :include #5251 *anna@wota.jp*
+
+* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 *alex@purefiction.net*
+
+* Allow models to override to_xml. #4989 *Blair Zajac <blair@orcaware.com>*
+
+* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket. #5247 *shimbo@is.naist.jp*
+
+* Records and arrays of records are bound as quoted ids. *Jeremy Kemper*
+ Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
+ Foo.find(:first, :conditions => ['bar_id = ?', bar])
+
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty *David Heinemeier Hansson*
+
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite. *Rick Olson*
+
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 *Josh Susser*
+
+* Provide Association Extensions access to the instance that the association is being accessed from.
+ Closes #4433 *Josh Susser*
+
+* Update OpenBase adaterp's maintainer's email address. Closes #5176. *Derrick Spell*
+
+* Add a quick note about :select and eagerly included associations. *Rick Olson*
+
+* Add docs for the :as option in has_one associations. Closes #5144 *cdcarter@gmail.com*
+
+* Fixed that has_many collections shouldn't load the entire association to do build or create *David Heinemeier Hansson*
+
+* Added :allow_nil option for aggregations #5091 *Ian White*
+
+* Fix Oracle boolean support and tests. Closes #5139. *Michael Schoen*
+
+* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) *Jeremy Kemper*
+
+* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 *info@loobmedia.com*
+
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. *pdcawley@bofh.org.uk*
+
+* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 *keegan@thebasement.org*
+
+* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 *kajism@yahoo.com*
+
+* Allow :uniq => true with has_many :through associations. *Jeremy Kemper*
+
+* Ensure that StringIO is always available for the Schema dumper. *Marcel Molina Jr.*
+
+* Allow AR::Base#to_xml to include methods too. Closes #4921. *johan@textdrive.com*
+
+* Remove duplicate fixture entry in comments.yml. Closes #4923. *Blair Zajac <blair@orcaware.com>*
+
+* When grouping, use the appropriate option key. *Marcel Molina Jr.*
+
+* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. *mlaster@metavillage.com*
+
+* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. *Francois Beausoleil <francois.beausoleil@gmail.com>*
+
+* Fix syntax error in documentation. Closes #4679. *Mislav Marohnić*
+
+* Add Oracle support for CLOB inserts. Closes #4748. *schoenm@earthlink.net sandra.metz@duke.edu*
+
+* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. *kajism@yahoo.com*
+
+* Add support for :order option to with_scope. Closes #3887. *eric.daspet@survol.net*
+
+* Prettify output of schema_dumper by making things line up. Closes #4241 *Caio Chassot <caio@v2studio.com>*
+
+* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. *mlaster@metavillage.com*
+
+* Sybase Adapter type conversion cleanup. Closes #4736. *dev@metacasa.net*
+
+* Fix bug where calculations with long alias names return null. *Rick Olson*
+
+* Raise error when trying to add to a has_many :through association. Use the Join Model instead. *Rick Olson*
+
+ @post.tags << @tag # BAD
+ @post.taggings.create(:tag => @tag) # GOOD
+
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) *Rick Olson*
+
+* Add ActiveRecord::Errors#to_xml *Jamis Buck*
+
+* Properly quote index names in migrations (closes #4764) *John Long*
+
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. *Rick Olson*
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions *Rick Olson*
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. *Rick Olson*
+
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) *Rick Olson*
+
+ class Account < ActiveRecord::Base
+ validates_uniqueness_of :email, :case_sensitive => false
+ end
+
+* Allow multiple association extensions with :extend option (closes #4666) *Josh Susser*
+
+ class Account < ActiveRecord::Base
+ has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
+ end
+
+
+## 1.14.4 (August 8th, 2006) ##
+
+* Add warning about the proper way to validate the presence of a foreign key. #4147 *Francois Beausoleil <francois.beausoleil@gmail.com>*
+
+* Fix syntax error in documentation. #4679 *Mislav Marohnić*
+
+* Update inconsistent migrations documentation. #4683 *machomagna@gmail.com*
+
+
+## 1.14.3 (June 27th, 2006) ##
+
+* Fix announcement of very long migration names. #5722 *blake@near-time.com*
+
+* Update callbacks documentation. #3970 *Robby Russell <robby@planetargon.com>*
+
+* Properly quote index names in migrations (closes #4764) *John Long*
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions *Rick Olson*
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. *Rick Olson*
+
+
+## 1.14.2 (April 9th, 2006) ##
+
+* Fixed calculations for the Oracle Adapter (closes #4626) *Michael Schoen*
+
+
+## 1.14.1 (April 6th, 2006) ##
+
+* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. *Nicholas Seckar*
+
+* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) *Lars Pind*
+
+* Enable Limit/Offset in Calculations (closes #4558) *lmarlow*
+
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) *Rick Olson*
+
+* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 *lagroue@free.fr*
+
+* Allow AR::Base#respond_to? to behave when @attributes is nil *Ryan Davis*
+
+* Support eager includes when going through a polymorphic has_many association. *Rick Olson*
+
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) *Rick Olson*
+
+ class Post < ActiveRecord::Base
+ has_one :tagging, :as => :taggable
+ end
+
+ Post.find :all, :include => :tagging
+
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many *Rick Olson*
+
+* Added support for going through a polymorphic has_many association: (closes #4401) *Rick Olson*
+
+ class PhotoCollection < ActiveRecord::Base
+ has_many :photos, :as => :photographic
+ belongs_to :firm
+ end
+
+ class Firm < ActiveRecord::Base
+ has_many :photo_collections
+ has_many :photos, :through => :photo_collections
+ end
+
+* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. *ruben.nine@gmail.com*
+
+* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. *Florian Weber*
+
+* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) *Stefan*
+
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) *Rick Olson*
+
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model *David Heinemeier Hansson*
+
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) *David Heinemeier Hansson*
+
+* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). *Marcel Mollina Jr.*
+
+* Fixed broken OCIAdapter #4457 *Michael Schoen*
+
+
+## 1.14.0 (March 27th, 2006) ##
+
+* Replace 'rescue Object' with a finer grained rescue. Closes #4431. *Nicholas Seckar*
+
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table *Rick Olson*
+
+* Add support for :include to with_scope *andrew@redlinesoftware.com*
+
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 *Michael Schoen*
+
+* Change periods (.) in table aliases to _'s. Closes #4251 *jeff@ministrycentered.com*
+
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 *Rick Olson*
+
+* Fixed issue that kept :select options from being scoped *Rick Olson*
+
+* Fixed db_schema_import when binary types are present #3101 *David Heinemeier Hansson*
+
+* Fixed that MySQL enums should always be returned as strings #3501 *David Heinemeier Hansson*
+
+* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. *Rick Olson*
+
+ class Connection < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :channel
+ end
+
+ class Channel < ActiveRecord::Base
+ has_many :connections
+ has_many :contacts, :through => :connections, :class_name => 'User' # OLD
+ has_many :contacts, :through => :connections, :source => :user # NEW
+ end
+
+* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 *contact@maik-schmidt.de*
+
+* Allow overriding of find parameters in scoped has_many :through calls *Rick Olson*
+
+ In this example, :include => false disables the default eager association from loading. :select changes the standard
+ select clause. :joins specifies a join that is added to the end of the has_many :through query.
+
+ class Post < ActiveRecord::Base
+ has_many :tags, :through => :taggings, :include => :tagging do
+ def add_joins_and_select
+ find :all, :select => 'tags.*, authors.id as author_id', :include => false,
+ :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
+ end
+ end
+ end
+
+* Fixed that schema changes while the database was open would break any connections to an SQLite database (now we reconnect if that error is throw) *David Heinemeier Hansson*
+
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) *Jonathan Viney*
+
+* Quit ignoring default :include options in has_many :through calls *Mark James*
+
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) *Jonathan Viney*
+
+* Eager Loading support added for has_many :through => :has_many associations (see below). *Rick Olson*
+
+* Allow has_many :through to work on has_many associations (closes #3864) [sco@scottraymond.net] Example:
+
+ class Firm < ActiveRecord::Base
+ has_many :clients
+ has_many :invoices, :through => :clients
+ end
+
+ class Client < ActiveRecord::Base
+ belongs_to :firm
+ has_many :invoices
+ end
+
+ class Invoice < ActiveRecord::Base
+ belongs_to :client
+ end
+
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) *Josh Susser*
+
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 *Jonathan Viney*
+
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) *andylien@gmail.com / Rick Olson*
+
+* SQL Server adapter gets some love #4298 *Ryan Tomayko*
+
+* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 *derrickspell@cdmplus.com*
+
+* Rework table aliasing to account for truncated table aliases. Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
+
+ Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
+
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. *Rick Olson*
+
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 *Michael Koziarski*
+
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 *Michael Koziarski*
+
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 *Tom Ward*
+
+* Fixed that Migration#execute would have the table name prefix appended to its query #4110 *mark.imbriaco@pobox.com*
+
+* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) *Jamis Buck*
+
+* Use association's :conditions when eager loading. [Jeremy Evans] #4144
+
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 *Jeremy Evans*
+
+ This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
+
+ Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
+
+* Oracle adapter gets some love #4230 *Michael Schoen*
+
+ * Changes :text to CLOB rather than BLOB [Moses Hohman]
+ * Fixes an issue with nil numeric length/scales (several)
+ * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
+ * Tweaks a unit test to get it all green again
+ * Adds support for #current_database
+
+* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 *Rick Olson*
+
+ class CachedModel < ActiveRecord::Base
+ self.abstract_class = true
+ end
+
+ class Post < CachedModel
+ end
+
+ CachedModel.abstract_class?
+ => true
+
+ Post.abstract_class?
+ => false
+
+ Post.base_class
+ => Post
+
+ Post.table_name
+ => 'posts'
+
+* Allow :dependent options to be used with polymorphic joins. #3820 *Rick Olson*
+
+ class Foo < ActiveRecord::Base
+ has_many :attachments, :as => :attachable, :dependent => :delete_all
+ end
+
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 *court3nay*
+
+* Upgrade to Transaction::Simple 1.3 *Jamis Buck*
+
+* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model *Rick Olson*
+
+* Allow ordering of calculated results and/or grouped fields in calculations *solo@gatelys.com*
+
+* Make ActiveRecord::Base#save! return true instead of nil on success. #4173 *johan@johansorensen.com*
+
+* Dynamically set allow_concurrency. #4044 *Stefan Kaes*
+
+* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
+
+ topic.to_xml
+
+ ...returns:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <topic>
+ <title>The First Topic</title>
+ <author-name>David</author-name>
+ <id type="integer">1</id>
+ <approved type="boolean">false</approved>
+ <replies-count type="integer">0</replies-count>
+ <bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>
+ <written-on type="datetime">2003-07-16 09:28:00</written-on>
+ <content>Have a nice day</content>
+ <author-email-address>david@loudthinking.com</author-email-address>
+ <parent-id></parent-id>
+ <last-read type="date">2004-04-15</last-read>
+ </topic>
+
+ ...and you can configure with:
+
+ topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
+
+ ...that'll return:
+
+ <topic>
+ <title>The First Topic</title>
+ <author-name>David</author-name>
+ <approved type="boolean">false</approved>
+ <content>Have a nice day</content>
+ <author-email-address>david@loudthinking.com</author-email-address>
+ <parent-id></parent-id>
+ <last-read type="date">2004-04-15</last-read>
+ </topic>
+
+ You can even do load first-level associations as part of the document:
+
+ firm.to_xml :include => [ :account, :clients ]
+
+ ...that'll return something like:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <firm>
+ <id type="integer">1</id>
+ <rating type="integer">1</rating>
+ <name>37signals</name>
+ <clients>
+ <client>
+ <rating type="integer">1</rating>
+ <name>Summit</name>
+ </client>
+ <client>
+ <rating type="integer">1</rating>
+ <name>Microsoft</name>
+ </client>
+ </clients>
+ <account>
+ <id type="integer">1</id>
+ <credit-limit type="integer">50</credit-limit>
+ </account>
+ </firm>
+
+* Allow :counter_cache to take a column name for custom counter cache columns *Jamis Buck*
+
+* Documentation fixes for :dependent *robby@planetargon.com*
+
+* Stop the MySQL adapter crashing when views are present. #3782 *Jonathan Viney*
+
+* Don't classify the belongs_to class, it is already singular #4117 *keithm@infused.org*
+
+* Allow set_fixture_class to take Classes instead of strings for a class in a module. Raise FixtureClassNotFound if a fixture can't load. *Rick Olson*
+
+* Fix quoting of inheritance column for STI eager loading #4098 *Jonathan Viney <jonathan@bluewire.net.nz>*
+
+* Added smarter table aliasing for eager associations for multiple self joins #3580 *Rick Olson*
+
+ * The first time a table is referenced in a join, no alias is used.
+ * After that, the parent class name and the reflection name are used.
+
+ Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
+
+ * Any additional join references get a numerical suffix like '_2', '_3', etc.
+
+* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
+
+* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna@wota.jp]. Examples:
+
+ # cascaded in two levels
+ >> Author.find(:all, :include=>{:posts=>:comments})
+ => authors
+ +- posts
+ +- comments
+
+ # cascaded in two levels and normal association
+ >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
+ => authors
+ +- posts
+ +- comments
+ +- categorizations
+
+ # cascaded in two levels with two has_many associations
+ >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
+ => authors
+ +- posts
+ +- comments
+ +- categorizations
+
+ # cascaded in three levels
+ >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
+ => companies
+ +- groups
+ +- members
+ +- favorites
+
+* Make counter cache work when replacing an association #3245 *eugenol@gmail.com*
+
+* Make migrations verbose *Jamis Buck*
+
+* Make counter_cache work with polymorphic belongs_to *Jamis Buck*
+
+* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 *anna@wota.jp*
+
+* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
+
+ * Does not support DATE SQL column types; use DATETIME instead.
+ * Date columns on HABTM join tables are returned as String, not Time.
+ * Insertions are potentially broken for :polymorphic join tables
+ * BLOB column access not yet fully supported
+
+* Clear stale, cached connections left behind by defunct threads. *Jeremy Kemper*
+
+* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. *Jeremy Kemper*
+
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 *Michael Schoen*
+
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 *David Heinemeier Hansson*
+
+* Speed up class -> connection caching and stale connection verification. #3979 *Stefan Kaes*
+
+* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. *Kevin Clark*
+
+* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 *dblack@wobblini.net*
+
+* Define attribute query methods to avoid method_missing calls. #3677 *Jonathan Viney*
+
+* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 *Simon Stapleton, Tom Ward*
+
+* Added support for nested scopes #3407 [anna@wota.jp]. Examples:
+
+ Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
+
+ # inner rule is used. (all previous parameters are ignored)
+ Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (name = 'Jamis')
+ end
+
+ # parameters are merged
+ Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
+ end
+ end
+
+* Fixed db2 connection with empty user_name and auth options #3622 *phurley@gmail.com*
+
+* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 *Masao Mutoh*
+
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 *Lars Pind*
+
+* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
+
+ Person.average :age
+ Person.minimum :age
+ Person.maximum :age
+ Person.sum :salary, :group => :last_name
+
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 *Luke Redpath*
+
+* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. *Jeremy Kemper*
+
+* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 *Aggregated by schoenm@earthlink.net*
+
+* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 *rubyonrails@atyp.de*
+
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 *Stefan Kaes*
+
+* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first *Bob Silva*
+
+* Allow :include to be used with has_many :through associations #3611 *Michael Schoen*
+
+* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table). #2920 *Blair Zajac*
+
+* SQLServer: more compatible limit/offset emulation. #3779 *Tom Ward*
+
+* Polymorphic join support for has_one associations (has_one :foo, :as => :bar) #3785 *Rick Olson*
+
+* PostgreSQL: correctly parse negative integer column defaults. #3776 *bellis@deepthought.org*
+
+* Fix problems with count when used with :include *Jeremy Hopple and Kevin Clark*
+
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message *Tobias Lütke*
+
+* Using AssociationCollection#build with arrays of hashes should call build, not create *David Heinemeier Hansson*
+
+* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. *Nicholas Seckar*
+
+* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 *Maik Schmidt*
+
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 *Shugo Maeda*
+
+* Add documentation for add_index and remove_index. #3600 *Manfred Stienstra <m.stienstra@fngtps.com>*
+
+* If the OCI library is not available, raise an exception indicating as much. #3593 *Michael Schoen*
+
+* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. *Rick Olson*
+
+* Make dynamic finders honor additional passed in :conditions. #3569 *Oleg Pudeyev <pudeyo@rpi.edu>, Marcel Molina Jr.*
+
+* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. *Nicholas Seckar*
+
+* Make .count work for has_many associations with multi line finder sql *Michael Schoen*
+
+* Add AR::Base.base_class for querying the ancestor AR::Base subclass *Jamis Buck*
+
+* Allow configuration of the column used for optimistic locking *wilsonb@gmail.com*
+
+* Don't hardcode 'id' in acts as list. *ror@philippeapril.com*
+
+* Fix date errors for SQLServer in association tests. #3406 *Kevin Clark*
+
+* Escape database name in MySQL adapter when creating and dropping databases. #3409 *anna@wota.jp*
+
+* Disambiguate table names for columns in validates_uniqueness_of's WHERE clause. #3423 *alex.borovsky@gmail.com*
+
+* .with_scope imposed create parameters now bypass attr_protected *Tobias Lütke*
+
+* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. *Marcel Molina Jr.*
+
+* Multiple enhancements and adjustments to DB2 adaptor. #3377 *contact@maik-schmidt.de*
+
+* Sanitize scoped conditions. *Marcel Molina Jr.*
+
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) *David Heinemeier Hansson*
+
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. *Tobias Lütke*
+
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. *Tobias Lütke*
+
+* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 *yanowitz-rubyonrails@quantumfoam.org, Florian Weber*
+
+* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 *wejn@box.cz, Rick Olson, Scott Barron*
+
+* removed :piggyback in favor of just allowing :select on :through associations. *Tobias Lütke*
+
+* made method missing delegation to class methods on relation target work on :through associations. *Tobias Lütke*
+
+* made .find() work on :through relations. *Tobias Lütke*
+
+* Fix typo in association docs. #3296. *Blair Zajac*
+
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model *Tobias Lütke*
+
+## 1.13.2 (December 13th, 2005) ##
+
+* Become part of Rails 1.0
+
+* MySQL: allow encoding option for mysql.rb driver. *Jeremy Kemper*
+
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]. Example:
+
+ class Post
+ has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
+ end
+
+ post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
+ post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
+ post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
+
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations *David Heinemeier Hansson*
+
+* MySQL: fixes for the bundled mysql.rb driver. #3160 *Justin Forder*
+
+* SQLServer: fix obscure optimistic locking bug. #3068 *kajism@yahoo.com*
+
+* SQLServer: support uniqueidentifier columns. #2930 *keithm@infused.org*
+
+* SQLServer: cope with tables names qualified by owner. #3067 *jeff@ministrycentered.com*
+
+* SQLServer: cope with columns with "desc" in the name. #1950 *Ron Lusk, Ryan Tomayko*
+
+* SQLServer: cope with primary keys with "select" in the name. #3057 *rdifrango@captechventures.com*
+
+* Oracle: active? performs a select instead of a commit. #3133 *Michael Schoen*
+
+* MySQL: more robust test for nullified result hashes. #3124 *Stefan Kaes*
+
+* Reloading an instance refreshes its aggregations as well as its associations. #3024 *François Beausoleil*
+
+* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 *Paul Hammmond*
+
+* PostgreSQL: more robust sequence name discovery. #3087 *Rick Olson*
+
+* Oracle: use syntax compatible with Oracle 8. #3131 *Michael Schoen*
+
+* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. *Jeremy Kemper*
+
+* Added preliminary support for polymorphic associations *David Heinemeier Hansson*
+
+* Added preliminary support for join models *David Heinemeier Hansson*
+
+* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. *jeremy@jthopple.com, Marcel Molina Jr.*
+
+* Firebird: active? and reconnect! methods for handling stale connections. #428 *Ken Kunz <kennethkunz@gmail.com>*
+
+* Firebird: updated for FireRuby 0.4.0. #3009 *Ken Kunz <kennethkunz@gmail.com>*
+
+* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 *Jeremy Kemper*
+
+* Oracle: active? check pings the database rather than testing the last command status. #428 *Michael Schoen*
+
+* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 *kajism@yahoo.com*
+
+* Reloading a model doesn't lose track of its connection. #2996 *junk@miriamtech.com, Jeremy Kemper*
+
+* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 *colman@rominato.com, Florian Weber, Michael Schoen*
+
+* MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. #2978 *Shugo Maeda*
+
+* has_and_belongs_to_many: use JOIN instead of LEFT JOIN. *Jeremy Kemper*
+
+* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 *Shugo Maeda*
+
+* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. *Marcel Molina Jr.*
+
+* Correct boolean handling in generated reader methods. #2945 *Don Park, Stefan Kaes*
+
+* Don't generate read methods for columns whose names are not valid ruby method names. #2946 *Stefan Kaes*
+
+* Document :force option to create_table. #2921 *Blair Zajac <blair@orcaware.com>*
+
+* Don't add the same conditions twice in has_one finder sql. #2916 *Jeremy Evans*
+
+* Rename Version constant to VERSION. #2802 *Marcel Molina Jr.*
+
+* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 *Ken Kunz <kennethkunz@gmail.com>*
+
+* SQLServer: active? and reconnect! methods for handling stale connections. #428 *kajism@yahoo.com, Tom Ward <tom@popdog.net>*
+
+* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array. #1345 *MarkusQ@reality.com*
+
+* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 *kajism@yahoo.com, Tom Ward <tom@popdog.net>*
+
+* Oracle: active? and reconnect! methods for handling stale connections. Optionally retry queries after reconnect. #428 *Michael Schoen <schoenm@earthlink.net>*
+
+* Correct documentation for Base.delete_all. #1568 *Newhydra*
+
+* Oracle: test case for column default parsing. #2788 *Michael Schoen <schoenm@earthlink.net>*
+
+* Update documentation for Migrations. #2861 *Tom Werner <tom@cube6media.com>*
+
+* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? #428 *Jeremy Kemper*
+
+* Oracle: Much faster column reflection. #2848 *Michael Schoen <schoenm@earthlink.net>*
+
+* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. *Jeremy Kemper*
+
+* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. *Jeremy Kemper*
+
+* PostgreSQL: correctly discover custom primary key sequences. #2594 *Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper*
+
+* SQLServer: don't report limits for unsupported field types. #2835 *Ryan Tomayko*
+
+* Include the Enumerable module in ActiveRecord::Errors. *Rick Bradley <rick@rickbradley.com>*
+
+* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 *rubyonrails@atyp.de*
+
+* Don't cast nil or empty strings to a dummy date. #2789 *Rick Bradley <rick@rickbradley.com>*
+
+* acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 *rephorm@rephorm.com*
+
+* Fix sqlite adaptor's detection of missing dbfile or database declaration. *Nicholas Seckar*
+
+* Fixed acts_as_list for definitions without an explicit :order #2803 *Jonathan Viney*
+
+* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. *tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper*
+
+* Correct handling of complex order clauses with SQL Server limit emulation. #2770 *Tom Ward <tom@popdog.net>, Matt B.*
+
+* Correct whitespace problem in Oracle default column value parsing. #2788 *rick@rickbradley.com*
+
+* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 *larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper*
+
+* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. *Jeremy Kemper*
+
+* More compatible Oracle column reflection. #2771 *Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>*
+
+
+## 1.13.0 (November 7th, 2005) ##
+
+* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 *Ryan Tomayko*
+
+* Added :include as an option for association declarations [David Heinemeier Hansson]. Example:
+
+ has_many :posts, :include => [ :author, :comments ]
+
+* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints. Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes. [Jeremy Kemper] Example:
+
+ Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
+ # Find where name = ? and active=true
+ Comment.find :all, :conditions => ['name = ?', name]
+ # Create comment associated with :post_id
+ Comment.create :body => "Hello world"
+ end
+
+* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 *Ryan Tomayko*
+
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [David Heinemeier Hansson]. Example:
+
+ Comment.constrain(:creation => { :post_id => 5 }) do
+ # Associated with :post_id
+ Comment.create :body => "Hello world"
+ end
+
+ This is rarely used directly, but allows for find_or_create on associations. So you can do:
+
+ # If the tag doesn't exist, a new one is created that's associated with the person
+ person.tags.find_or_create_by_name("Summer")
+
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [David Heinemeier Hansson]. Example:
+
+ # No 'Summer' tag exists
+ Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
+
+ # Now the 'Summer' tag does exist
+ Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
+
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [David Heinemeier Hansson]. Example:
+
+ class Account < ActiveRecord::Base
+ has_many :people do
+ def find_or_create_by_name(name)
+ first_name, *last_name = name.split
+ last_name = last_name.join " "
+
+ find_or_create_by_first_name_and_last_name(first_name, last_name)
+ end
+ end
+ end
+
+ person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+ person.first_name # => "David"
+ person.last_name # => "Heinemeier Hansson"
+
+ Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
+
+* Omit internal dtproperties table from SQLServer table list. #2729 *Ryan Tomayko*
+
+* Quote column names in generated SQL. #2728 *Ryan Tomayko*
+
+* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 *Jeremy Kemper*
+
+* Add Model.create! to match existing model.save! method. When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors). *Jeremy Kemper*
+
+* Correct fixture behavior when table name pluralization is off. #2719 *Rick Bradley <rick@rickbradley.com>*
+
+* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 *Dan Peterson*
+
+* Added migration support for Oracle #2647 *Michael Schoen*
+
+* Worked around that connection can't be reset if allow_concurrency is off. #2648 *Michael Schoen <schoenm@earthlink.net>*
+
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 *Ryan Tomayko*
+
+* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 *Tom Ward*
+
+* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 *Tom Ward*
+
+* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility. #2618 *dansketcher@gmail.com*
+
+* Constraints are cloned so they can't be inadvertently modified while they're
+ in effect. Added :readonly finder constraint. Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true. [Jeremy Kemper <rails@bitsweat.net>]
+* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 *duane.johnson@gmail.com*
+
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 *Michael Schoen*
+
+* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 *maik schmidt*
+
+
+## 1.12.2 (October 26th, 2005) ##
+
+* Allow symbols to rename columns when using SQLite adapter. #2531 *Kevin Clark*
+
+* Map Active Record time to SQL TIME. #2575, #2576 *Robby Russell <robby@planetargon.com>*
+
+* Clarify semantics of ActiveRecord::Base#respond_to? #2560 *Stefan Kaes*
+
+* Fixed Association#clear for associations which have not yet been accessed. #2524 *Patrick Lenz <patrick@lenz.sh>*
+
+* HABTM finders shouldn't return readonly records. #2525 *Patrick Lenz <patrick@lenz.sh>*
+
+* Make all tests runnable on their own. #2521. *Blair Zajac <blair@orcaware.com>*
+
+
+## 1.12.1 (October 19th, 2005) ##
+
+* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
+
+* Correct PostgreSQL primary key sequence detection. #2507 *tmornini@infomania.com*
+
+* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
+
+
+## 1.12.0 (October 16th, 2005) ##
+
+* Update/clean up documentation (rdoc)
+
+* PostgreSQL sequence support. Use set_sequence_name in your model class to specify its primary key sequence. #2292 *Rick Olson <technoweenie@gmail.com>, Robby Russell <robby@planetargon.com>*
+
+* Change default logging colors to work on both white and black backgrounds. *Sam Stephenson*
+
+* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 *purestorm@ggnore.net*
+
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 *Robby Russell <robby@planetargon.com>*
+
+* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
+
+* Avoid memleak in dev mode when using fcgi
+
+* Simplified .clear on active record associations by using the existing delete_records method. #1906 *Caleb <me@cpb.ca>*
+
+* Delegate access to a customized primary key to the conventional id method. #2444. *Blair Zajac <blair@orcaware.com>*
+
+* Fix errors caused by assigning a has-one or belongs-to property to itself
+
+* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped *Sam Stephenson*
+
+* Update DB2 adapter. #2206. *contact@maik-schmidt.de*
+
+* Corrections to SQLServer native data types. #2267. *rails.20.clarry@spamgourmet.com*
+
+* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
+
+* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. *Blair Zajac <blair@orcaware.com>*
+
+* Misc doc fixes (typos/grammar/etc.). #2430. *coffee2code*
+
+* Add test coverage for content_columns. #2432. *coffee2code*
+
+* Speed up for unthreaded environments. #2431. *Stefan Kaes*
+
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. *Stefan Kaes*
+
+* Speed up the setting of table_name. #2428. *Stefan Kaes*
+
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Fix typo of 'constrains' to 'contraints'. #2069. *Michael Schuerig <michael@schuerig.de>*
+
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. *Michael Schuerig <michael@schuerig.de>*
+
+* Add geometric type for postgresql adapter. #2233 *Andrew Kaspick*
+
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 *Chad Fowler <chad@chadfowler.com>, Nicholas Seckar*
+
+* Added :force option to create_table that'll try to drop the table if it already exists before creating
+
+* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. *Nicholas Seckar*
+
+* Use foreign_key inflection uniformly. #2156 *Blair Zajac <blair@orcaware.com>*
+
+* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys. #2221 *joergd@pobox.com, ObieFernandez <obiefernandez@gmail.com>*
+
+* Returning false from before_destroy should cancel the action. #1829 *Jeremy Huffman*
+
+* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type. #2256 *mat <mat@absolight.fr>*
+
+* Extensive documentation for the abstract database adapter. #2250 *François Beausoleil <fbeausoleil@ftml.net>*
+
+* Clean up Fixtures.reset_sequences for PostgreSQL. Handle tables with no rows and models with custom primary keys. #2174, #2183 *jay@jay.fm, Blair Zajac <blair@orcaware.com>*
+
+* Improve error message when nil is assigned to an attr which validates_size_of within a range. #2022 *Manuel Holtgrewe <purestorm@ggnore.net>*
+
+* Make update_attribute use the same writer method that update_attributes uses.
+ \#2237 [trevor@protocool.com]
+* Make migrations honor table name prefixes and suffixes. #2298 *Jakob Skjerning, Marcel Molina Jr.*
+
+* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 *dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org*
+
+* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id. #1749 *chris@chrisbrinker.com*
+
+* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
+
+* Added new symbol-driven approach to activating observers with Base#observers= [David Heinemeier Hansson]. Example:
+
+ ActiveRecord::Base.observers = :cacher, :garbage_collector
+
+* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 *solo@gatelys.com*
+
+* Wrap :conditions in parentheses to prevent problems with OR's #1871 *Jamis Buck*
+
+* Allow the postgresql adapter to work with the SchemaDumper. *Jamis Buck*
+
+* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. *Jamis Buck*
+
+* Fixed migrations for Windows when using more than 10 *David Naseby*
+
+* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 *Florian Weber*
+
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 *Tobias Lütke*
+
+* Improved migrations' behavior when the schema_info table is empty. *Nicholas Seckar*
+
+* Fixed that Observers didn't observe sub-classes #627 *Florian Weber*
+
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 *Marcel Molina Jr.*
+
+* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 *skae*
+
+* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 *Florian Weber*
+
+* Added better exception error when unknown column types are used with migrations #1814 *François Beausoleil*
+
+* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 *kajism@yahoo.com*
+
+* Fixed comparison of Active Record objects so two new objects are not equal #2099 *deberg*
+
+* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 *Tom Ward*
+
+* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 *coffee2code*
+
+* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 *sd@notso.net*
+
+* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
+
+* Make sure the schema_info table is created before querying the current version #1903
+
+* Fixtures ignore table name prefix and suffix #1987 *Jakob Skjerning*
+
+* Add documentation for index_type argument to add_index method for migrations #2005 *Blaine*
+
+* Modify read_attribute to allow a symbol argument #2024 *Ken Kunz*
+
+* Make destroy return self #1913 *Sebastian Kanthak*
+
+* Fix typo in validations documentation #1938 *court3nay*
+
+* Make acts_as_list work for insert_at(1) #1966 *hensleyl@papermountain.org*
+
+* Fix typo in count_by_sql documentation #1969 *Alexey Verkhovsky*
+
+* Allow add_column and create_table to specify NOT NULL #1712 *emptysands@gmail.com*
+
+* Fix create_table so that id column is implicitly added *Rick Olson*
+
+* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
+
+* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
+
+* Fixed the handling of camelCase columns names in Oracle #1798
+
+* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
+
+* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
+
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 *Tobias Lütke*
+
+ class Comment < AR:B
+ def self.search(q)
+ find(:all, :conditions => ["body = ?", q])
+ end
+ end
+
+ class Post < AR:B
+ has_many :comments
+ end
+
+ Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
+
+ NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
+ by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
+ details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
+ noticed :)
+
+* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 *Sam Stephenson*
+
+* Remove extra definition of supports_migrations? from abstract_adaptor.rb *Nicholas Seckar*
+
+* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
+
+* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 *Maik Schmidt*
+
+* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 *Stefan Kaes*
+
+
+## 1.11.1 (11 July, 2005) ##
+
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 *Rick Olson*
+
+* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 *tyler@kianta.com*
+
+* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 *Sam Stephenson*
+
+* Correct reflected table name for singular associations. #1688 *court3nay*
+
+* Fixed optimistic locking with SQL Server #1660 *tom@popdog.net*
+
+* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
+
+* Added better error message for "packets out of order" #1630 *court3nay*
+
+* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
+
+
+## 1.11.0 (6 July, 2005) ##
+
+* Fixed that Yaml error message in fixtures hid the real error #1623 *Nicholas Seckar*
+
+* Changed logging of SQL statements to use the DEBUG level instead of INFO
+
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Lütke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+
+* Added callback hooks to association collections #1549 [Florian Weber]. Example:
+
+ class Project
+ has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
+
+ def evaluate_velocity(developer)
+ ...
+ end
+ end
+
+ ..raising an exception will cause the object not to be added (or removed, with before_remove).
+
+
+* Fixed Base.content_columns call for SQL Server adapter #1450 *DeLynn Berry*
+
+* Fixed Base#write_attribute to work with both symbols and strings #1190 *Paul Legato*
+
+* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 *Florian Weber*
+
+* Speed up ActiveRecord#method_missing for the common case (read_attribute).
+
+* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 *Stefan Kaes*
+
+* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 *Chris McGrath*
+
+* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 *raidel@onemail.at*
+
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 *Michael Schuerig*
+
+* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 *DeLynn Berry*
+
+* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 *Mark Imbriaco/DeLynn Berry*
+
+* Fixed that multiparameter posts ignored attr_protected #1532 *alec+rails@veryclever.net*
+
+* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 *flash@vanklinkenbergsoftware.nl*
+
+* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be:
+
+ Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
+
+ ...should instead be:
+
+ Developer.find(
+ :all,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :conditions => 'project_id=1'
+ )
+
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 *Marcel Molina Jr.*
+
+* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 *cluon*
+
+* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
+
+* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 *Alisdair McDiarmid*
+
+* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
+
+* Allow any Enumerable, not just Array, to work as bind variables #1344 *Jeremy Kemper*
+
+* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
+ Example:
+
+ david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
+ david.save
+
+ If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
+ project is saved when david.save is called.
+
+ Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
+
+ david.project_ids = [1, 5, 7]
+
+* Corrected typo in find SQL for has_and_belongs_to_many. #1312 *ben@bensinclair.com*
+
+* Fixed sanitized conditions for has_many finder method. #1281 *jackc@hylesanderson.com, pragdave, Tobias Lütke*
+
+* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 *dave@cherryville.org*
+
+* Corrected @@configurations typo #1410 *david@ruppconsulting.com*
+
+* Return PostgreSQL columns in the order they were declared #1374 *perlguy@gmail.com*
+
+* Allow before/after update hooks to work on models using optimistic locking
+
+* Eager loading of dependent has_one associations won't delete the association #1212
+
+* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
+
+* Using transactional fixtures now causes the data to be loaded only once.
+
+* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
+
+ fixtures :web_sites
+
+ def test_something
+ assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+ end
+
+* Added DoubleRenderError exception that'll be raised if render* is called twice #518 *Nicholas Seckar*
+
+* Fixed exceptions occuring after render has been called #1096 *Nicholas Seckar*
+
+* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make " " fail the validation where it didn't before #1309
+
+* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
+
+* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
+
+ Conditional validations such as the following are made possible:
+ validates_numericality_of :income, :if => :employed?
+
+ Conditional validations can also solve the salted login generator problem:
+ validates_confirmation_of :password, :if => :new_password?
+
+ Using blocks:
+ validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
+
+* Fixed use of construct_finder_sql when using :join #1288 *dwlt@dwlt.net*
+
+* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 *Rick Olson*
+
+* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 *Jeremy Kemper*
+
+* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
+
+* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
+
+* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
+
+ # SELECT * FROM topics WHERE title IN ('First', 'Second')
+ Topic.find_all_by_title(["First", "Second"])
+
+* Added compatibility with camelCase column names for dynamic finders #533 *Dee Zsombor*
+
+* Fixed extraneous comma in count() function that made it not work with joins #1156 *Jarkko Laine/Dee Zsombor*
+
+* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 *Alisdair McDiarmid*
+
+* Fixed that validate_length_of lost :on option when :within was specified #1195 *jhosteny@mac.com*
+
+* Added encoding and min_messages options for PostgreSQL #1205 [Shugo Maeda]. Configuration example:
+
+ development:
+ adapter: postgresql
+ database: rails_development
+ host: localhost
+ username: postgres
+ password:
+ encoding: UTF8
+ min_messages: ERROR
+
+* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 *Jamis Buck*
+
+* Added validates_exclusion_of as a negative of validates_inclusion_of
+
+* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
+
+
+## 1.10.1 (20th April, 2005) ##
+
+* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
+
+* Fixed order of loading in eager associations
+
+* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
+
+
+## 1.10.0 (19th April, 2005) ##
+
+* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
+
+ for post in Post.find(:all, :limit => 100)
+ puts "Post: " + post.title
+ puts "Written by: " + post.author.name
+ puts "Last comment on: " + post.comments.first.created_on
+ end
+
+ This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
+
+ for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
+
+ ...and the number of database queries needed is now 1.
+
+* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
+
+ Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
+ Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
+ Person.find(:first, :order => "created_on DESC", :offset => 5)
+ Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
+ Person.find(:all, :offset => 10, :limit => 10)
+
+* Added acts_as_nested_set #1000 [wschenk]. Introduction:
+
+ This acts provides Nested Set functionality. Nested Set is similiar to Tree, but with
+ the added feature that you can select the children and all of it's descendants with
+ a single query. A good use case for this is a threaded post system, where you want
+ to display every reply to a comment without multiple selects.
+
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid *Dave Thomas*
+
+* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 *gnuman1@gmail.com*
+
+* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 *andrew.john.peters@gmail.com*
+
+* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 *stephenh@chase3000.com*
+
+* Fixed page caching for non-vhost applications living underneath the root #1004 *Ben Schumacher*
+
+* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 *adelle*
+
+* Added the option to specify the acceptance string in validates_acceptance_of #1106 *caleb@aei-tech.com*
+
+* Added insert_at(position) to acts_as_list #1083 *DeLynnB*
+
+* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
+
+* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 *yon@milliped.com*
+
+* Fixed boolean saving on Oracle #1093 *mparrish@pearware.org*
+
+* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
+
+* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 *Leon Bredt*
+
+* Added quoting of column names for fixtures #997 *jcfischer@gmail.com*
+
+* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 *Caleb Tennis*
+
+* Fixed that benchmarking times for rendering included db runtimes #987 *Stefan Kaes*
+
+* Fixed boolean queries for t/f fields in PostgreSQL #995 *dave@cherryville.org*
+
+* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 *Jeremy Kemper*
+
+* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 *dave@cherryville.org*
+
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 *Stefan Kaes*
+
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 *Stefan Kaes*
+
+* Fixed bug in Base#hash method that would treat records with the same string-based id as different *Dave Thomas*
+
+* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
+
+
+## 1.9.1 (27th March, 2005) ##
+
+* Fixed that Active Record objects with float attribute could not be cloned #808
+
+* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 *Nicholas Seckar*
+
+* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
+
+* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 *Scott Barron*
+
+* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 *delynnb*
+
+
+## 1.9.0 (22th March, 2005) ##
+
+* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
+
+ Developer.find_all nil, 'id ASC', 5 # return the first five developers
+ Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
+
+ This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
+
+* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 *Nicholas Seckar/Sam Stephenson*
+
+* Improved the performance of the OCI8 adapter for Oracle #723 *pilx/gjenkins*
+
+* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 *dave@cherryville.org*
+
+* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 *mindel*
+
+* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 *Alisdair McDiarmid*
+
+* Added the possibility of specifying fixtures in multiple calls #816 *kim@tinker.com*
+
+* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 *stian@grytoyr.net*
+
+* Added optionally allow for nil or empty strings with validates_numericality_of #801 *Sebastian Kanthak*
+
+* Fixed problem with using slashes in validates_format_of regular expressions #801 *Sebastian Kanthak*
+
+* Fixed that SQLite3 exceptions are caught and reported properly #823 *yerejm*
+
+* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
+
+* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
+
+
+## 1.8.0 (7th March, 2005) ##
+
+* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
+
+* Added support for timestamp with time zone in PostgreSQL #560 *Scott Barron*
+
+* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
+
+ * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
+ +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
+ objects that should be inspected to determine which attributes triggered the errors.
+ * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
+ You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
+
+* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 *rodrigo k*
+
+* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 *Jeremy Kemper*
+
+* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
+
+ class Account < ActiveRecord::Base
+ has_one :credit_card, :dependent => true
+ end
+ class CreditCard < ActiveRecord::Base
+ belongs_to :account
+ end
+
+ account.credit_card # => returns existing credit card, lets say id = 12
+ account.credit_card = CreditCard.create("number" => "123")
+ account.save # => CC with id = 12 is destroyed
+
+
+* Added validates_numericality_of #716 [Sebastian Kanthak/Chris McGrath]. Docuemntation:
+
+ Validates whether the value of the specified attribute is numeric by trying to convert it to
+ a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
+ <tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
+
+ class Person < ActiveRecord::Base
+ validates_numericality_of :value, :on => :create
+ end
+
+ Configuration options:
+ * <tt>message</tt> - A custom error message (default is: "is not a number")
+ * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
+ * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
+
+
+* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 *Scott Barron*
+
+* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
+
+* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 *james@slashetc.com*
+
+
+## 1.7.0 (24th February, 2005) ##
+
+* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 *Jamis Buck*
+
+* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
+
+ 1. Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
+ and safe way of passing table-specific sequence information to the adapter.)
+ 2. Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
+ resort to some hacks to get data converted to Date or Time in Ruby.
+ If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
+ hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
+ This is nasty - but if you use Duck Typing you'll probably not care very much.
+ In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
+ valid - too many databases use DATE for both.
+ Timezones and sub-second precision on timestamps are not supported.
+ 3. Default values that are functions (such as "SYSDATE") are not supported. This is a
+ restriction of the way active record supports default values.
+ 4. Referential integrity constraints are not fully supported. Under at least
+ some circumstances, active record appears to delete parent and child records out of
+ sequence and out of transaction scope. (Or this may just be a problem of test setup.)
+
+ The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
+
+* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 *YuriSchimke*
+
+* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 *yerejm*
+
+* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 *tonka*
+
+* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 *Nicholas Seckar*
+
+* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 *daniel@nightrunner.com*
+
+* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 *adelle@bullet.net.au*
+
+* Fixed that find_by_* would fail when column names had numbers #670 *demetrius*
+
+* Fixed the SQL Server adapter on a bunch of issues #667 *DeLynn*
+
+ 1. Created a new columns method that is much cleaner.
+ 2. Corrected a problem with the select and select_all methods
+ that didn't account for the LIMIT clause being passed into raw SQL statements.
+ 3. Implemented the string_to_time method in order to create proper instances of the time class.
+ 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
+ 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
+
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 *lmarlow*
+
+* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
+
+ class Person < ActiveRecord::Base
+ validates_each :first_name, :last_name do |record, attr|
+ record.errors.add attr, 'starts with z.' if attr[0] == ?z
+ end
+ end
+
+* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 *Jeremy Kemper*
+
+* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
+
+ class Person < ActiveRecord::Base
+ validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
+ end
+
+* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 *Tim Bates*
+
+* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
+
+ class Project < ActiveRecord::Base
+ primary_key "sysid"
+ table_name "XYZ_PROJECT"
+ inheritance_column { original_inheritance_column + "_id" }
+ end
+
+* Fixed Base#clone for use with PostgreSQL #565 *hanson@surgery.wisc.edu*
+
+
+## 1.6.0 (January 25th, 2005) ##
+
+* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
+
+* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
+
+* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
+
+* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
+
+ people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+ Person.update(people.keys, people.values)
+
+* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 *Scott Baron*
+
+* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 *notahat*
+
+* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 *Demetrius*
+
+* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 *Jeremy Kemper*
+
+* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 *Eric Anderson*
+
+* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 *Eric Anderson*
+
+* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
+
+* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
+
+* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 *Demetrius*
+
+* Fixed that find_all would produce invalid sql when called sequentialy #490 *Scott Baron*
+
+
+## 1.5.1 (January 18th, 2005) ##
+
+* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 *Tim Bates*
+
+* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 *Pelle*
+
+
+## 1.5.0 (January 17th, 2005) ##
+
+* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 *Eric Hodel*
+
+* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
+
+ class Book < ActiveRecord::Base
+ has_many :pages
+ belongs_to :library
+
+ validates_associated :pages, :library
+ end
+
+* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
+
+ == Unsaved objects and associations
+
+ You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
+ aware of, mostly involving the saving of associated objects.
+
+ === One-to-one associations
+
+ * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
+ order to update their primary keys - except if the parent object is unsaved (new_record? == true).
+ * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
+ is cancelled.
+ * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
+ * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
+ not save the parent either.
+
+ === Collections
+
+ * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
+ (the owner of the collection) is not yet stored in the database.
+ * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
+ * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
+ * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
+
+* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 *Tim Bates*
+
+* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 *Tim Bates*
+
+* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 *Tim Bates*
+
+* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 *Tim Bates*
+
+* Fixed binary support for PostgreSQL #444 *alex@byzantine.no*
+
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size 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 less SELECT query if you use length.
+
+* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 *atyp.de*
+
+* Fixed that foreign keys named the same as the association would cause stack overflow #437 *Eric Anderson*
+
+* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 *Alexey*
+
+* Added Base#reload that reloads the attributes of an object from the database #422 *Andreas Schwarz*
+
+* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 *Jeremy Kemper*
+
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 *Matt Mower*
+
+* Added that Observers can use the observes class method instead of overwriting self.observed_class().
+
+ Before:
+ class ListSweeper < ActiveRecord::Base
+ def self.observed_class() [ List, Item ]
+ end
+
+ After:
+ class ListSweeper < ActiveRecord::Base
+ observes List, Item
+ end
+
+* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
+
+* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
+
+* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
+
+ Before: topic.update_attribute(:approved, !approved?)
+ After : topic.toggle!(:approved)
+
+* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
+
+ page.views # => 1
+ page.increment!(:views) # executes an UPDATE statement
+ page.views # => 2
+
+ page.increment(:views).increment!(:views)
+ page.views # => 4
+
+* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
+
+
+
+
+## 1.14.2 (April 9th, 2005) ##
+
+* Fixed calculations for the Oracle Adapter (closes #4626) *Michael Schoen*
+
+
+## 1.14.1 (April 6th, 2006) ##
+
+* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. *Nicholas Seckar*
+
+* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) *Lars Pind*
+
+* Enable Limit/Offset in Calculations (closes #4558) *lmarlow*
+
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) *Rick Olson*
+
+* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 *lagroue@free.fr*
+
+* Allow AR::Base#respond_to? to behave when @attributes is nil *Ryan Davis*
+
+* Support eager includes when going through a polymorphic has_many association. *Rick Olson*
+
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) *Rick Olson*
+
+ class Post < ActiveRecord::Base
+ has_one :tagging, :as => :taggable
+ end
+
+ Post.find :all, :include => :tagging
+
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many *Rick Olson*
+
+* Added support for going through a polymorphic has_many association: (closes #4401) *Rick Olson*
+
+ class PhotoCollection < ActiveRecord::Base
+ has_many :photos, :as => :photographic
+ belongs_to :firm
+ end
+
+ class Firm < ActiveRecord::Base
+ has_many :photo_collections
+ has_many :photos, :through => :photo_collections
+ end
+
+* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. *ruben.nine@gmail.com*
+
+* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. *Florian Weber*
+
+* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) *Stefan*
+
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) *Rick Olson*
+
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model *David Heinemeier Hansson*
+
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) *David Heinemeier Hansson*
+
+* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). *Marcel Mollina Jr.*
+
+* Fixed broken OCIAdapter #4457 *Michael Schoen*
+
+
+## 1.14.0 (March 27th, 2006) ##
+
+* Replace 'rescue Object' with a finer grained rescue. Closes #4431. *Nicholas Seckar*
+
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table *Rick Olson*
+
+* Add support for :include to with_scope *andrew@redlinesoftware.com*
+
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 *Michael Schoen*
+
+* Change periods (.) in table aliases to _'s. Closes #4251 *jeff@ministrycentered.com*
+
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 *Rick Olson*
+
+* Fixed issue that kept :select options from being scoped *Rick Olson*
+
+* Fixed db_schema_import when binary types are present #3101 *David Heinemeier Hansson*
+
+* Fixed that MySQL enums should always be returned as strings #3501 *David Heinemeier Hansson*
+
+* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. *Rick Olson*
+
+ class Connection < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :channel
+ end
+
+ class Channel < ActiveRecord::Base
+ has_many :connections
+ has_many :contacts, :through => :connections, :class_name => 'User' # OLD
+ has_many :contacts, :through => :connections, :source => :user # NEW
+ end
+
+* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 *contact@maik-schmidt.de*
+
+* Allow overriding of find parameters in scoped has_many :through calls *Rick Olson*
+
+ In this example, :include => false disables the default eager association from loading. :select changes the standard
+ select clause. :joins specifies a join that is added to the end of the has_many :through query.
+
+ class Post < ActiveRecord::Base
+ has_many :tags, :through => :taggings, :include => :tagging do
+ def add_joins_and_select
+ find :all, :select => 'tags.*, authors.id as author_id', :include => false,
+ :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
+ end
+ end
+ end
+
+* Fixed that schema changes while the database was open would break any connections to an SQLite database (now we reconnect if that error is throw) *David Heinemeier Hansson*
+
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) *Jonathan Viney*
+
+* Quit ignoring default :include options in has_many :through calls *Mark James*
+
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) *Jonathan Viney*
+
+* Eager Loading support added for has_many :through => :has_many associations (see below). *Rick Olson*
+
+* Allow has_many :through to work on has_many associations (closes #3864) [sco@scottraymond.net] Example:
+
+ class Firm < ActiveRecord::Base
+ has_many :clients
+ has_many :invoices, :through => :clients
+ end
+
+ class Client < ActiveRecord::Base
+ belongs_to :firm
+ has_many :invoices
+ end
+
+ class Invoice < ActiveRecord::Base
+ belongs_to :client
+ end
+
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) *Josh Susser*
+
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 *Jonathan Viney*
+
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) *andylien@gmail.com / Rick Olson*
+
+* SQL Server adapter gets some love #4298 *Ryan Tomayko*
+
+* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 *derrickspell@cdmplus.com*
+
+* Rework table aliasing to account for truncated table aliases. Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
+
+ Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
+
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. *Rick Olson*
+
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 *Michael Koziarski*
+
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 *Michael Koziarski*
+
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 *Tom Ward*
+
+* Fixed that Migration#execute would have the table name prefix appended to its query #4110 *mark.imbriaco@pobox.com*
+
+* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) *Jamis Buck*
+
+* Use association's :conditions when eager loading. [Jeremy Evans] #4144
+
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 *Jeremy Evans*
+
+ This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
+
+ Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
+
+* Oracle adapter gets some love #4230 *Michael Schoen*
+
+ * Changes :text to CLOB rather than BLOB [Moses Hohman]
+ * Fixes an issue with nil numeric length/scales (several)
+ * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
+ * Tweaks a unit test to get it all green again
+ * Adds support for #current_database
+
+* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 *Rick Olson*
+
+ class CachedModel < ActiveRecord::Base
+ self.abstract_class = true
+ end
+
+ class Post < CachedModel
+ end
+
+ CachedModel.abstract_class?
+ => true
+
+ Post.abstract_class?
+ => false
+
+ Post.base_class
+ => Post
+
+ Post.table_name
+ => 'posts'
+
+* Allow :dependent options to be used with polymorphic joins. #3820 *Rick Olson*
+
+ class Foo < ActiveRecord::Base
+ has_many :attachments, :as => :attachable, :dependent => :delete_all
+ end
+
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 *court3nay*
+
+* Upgrade to Transaction::Simple 1.3 *Jamis Buck*
+
+* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model *Rick Olson*
+
+* Allow ordering of calculated results and/or grouped fields in calculations *solo@gatelys.com*
+
+* Make ActiveRecord::Base#save! return true instead of nil on success. #4173 *johan@johansorensen.com*
+
+* Dynamically set allow_concurrency. #4044 *Stefan Kaes*
+
+* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
+
+ topic.to_xml
+
+ ...returns:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <topic>
+ <title>The First Topic</title>
+ <author-name>David</author-name>
+ <id type="integer">1</id>
+ <approved type="boolean">false</approved>
+ <replies-count type="integer">0</replies-count>
+ <bonus-time type="datetime">2000-01-01 08:28:00</bonus-time>
+ <written-on type="datetime">2003-07-16 09:28:00</written-on>
+ <content>Have a nice day</content>
+ <author-email-address>david@loudthinking.com</author-email-address>
+ <parent-id></parent-id>
+ <last-read type="date">2004-04-15</last-read>
+ </topic>
+
+ ...and you can configure with:
+
+ topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
+
+ ...that'll return:
+
+ <topic>
+ <title>The First Topic</title>
+ <author-name>David</author-name>
+ <approved type="boolean">false</approved>
+ <content>Have a nice day</content>
+ <author-email-address>david@loudthinking.com</author-email-address>
+ <parent-id></parent-id>
+ <last-read type="date">2004-04-15</last-read>
+ </topic>
+
+ You can even do load first-level associations as part of the document:
+
+ firm.to_xml :include => [ :account, :clients ]
+
+ ...that'll return something like:
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <firm>
+ <id type="integer">1</id>
+ <rating type="integer">1</rating>
+ <name>37signals</name>
+ <clients>
+ <client>
+ <rating type="integer">1</rating>
+ <name>Summit</name>
+ </client>
+ <client>
+ <rating type="integer">1</rating>
+ <name>Microsoft</name>
+ </client>
+ </clients>
+ <account>
+ <id type="integer">1</id>
+ <credit-limit type="integer">50</credit-limit>
+ </account>
+ </firm>
+
+* Allow :counter_cache to take a column name for custom counter cache columns *Jamis Buck*
+
+* Documentation fixes for :dependent *robby@planetargon.com*
+
+* Stop the MySQL adapter crashing when views are present. #3782 *Jonathan Viney*
+
+* Don't classify the belongs_to class, it is already singular #4117 *keithm@infused.org*
+
+* Allow set_fixture_class to take Classes instead of strings for a class in a module. Raise FixtureClassNotFound if a fixture can't load. *Rick Olson*
+
+* Fix quoting of inheritance column for STI eager loading #4098 *Jonathan Viney <jonathan@bluewire.net.nz>*
+
+* Added smarter table aliasing for eager associations for multiple self joins #3580 *Rick Olson*
+
+ * The first time a table is referenced in a join, no alias is used.
+ * After that, the parent class name and the reflection name are used.
+
+ Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
+
+ * Any additional join references get a numerical suffix like '_2', '_3', etc.
+
+* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
+
+* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna@wota.jp]. Examples:
+
+ # cascaded in two levels
+ >> Author.find(:all, :include=>{:posts=>:comments})
+ => authors
+ +- posts
+ +- comments
+
+ # cascaded in two levels and normal association
+ >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
+ => authors
+ +- posts
+ +- comments
+ +- categorizations
+
+ # cascaded in two levels with two has_many associations
+ >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
+ => authors
+ +- posts
+ +- comments
+ +- categorizations
+
+ # cascaded in three levels
+ >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
+ => companies
+ +- groups
+ +- members
+ +- favorites
+
+* Make counter cache work when replacing an association #3245 *eugenol@gmail.com*
+
+* Make migrations verbose *Jamis Buck*
+
+* Make counter_cache work with polymorphic belongs_to *Jamis Buck*
+
+* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 *anna@wota.jp*
+
+* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
+
+ * Does not support DATE SQL column types; use DATETIME instead.
+ * Date columns on HABTM join tables are returned as String, not Time.
+ * Insertions are potentially broken for :polymorphic join tables
+ * BLOB column access not yet fully supported
+
+* Clear stale, cached connections left behind by defunct threads. *Jeremy Kemper*
+
+* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. *Jeremy Kemper*
+
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 *Michael Schoen*
+
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 *David Heinemeier Hansson*
+
+* Speed up class -> connection caching and stale connection verification. #3979 *Stefan Kaes*
+
+* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. *Kevin Clark*
+
+* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 *dblack@wobblini.net*
+
+* Define attribute query methods to avoid method_missing calls. #3677 *Jonathan Viney*
+
+* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 *Simon Stapleton, Tom Ward*
+
+* Added support for nested scopes #3407 [anna@wota.jp]. Examples:
+
+ Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
+
+ # inner rule is used. (all previous parameters are ignored)
+ Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (name = 'Jamis')
+ end
+
+ # parameters are merged
+ Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
+ end
+ end
+
+* Fixed db2 connection with empty user_name and auth options #3622 *phurley@gmail.com*
+
+* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 *Masao Mutoh*
+
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 *Lars Pind*
+
+* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
+
+ Person.average :age
+ Person.minimum :age
+ Person.maximum :age
+ Person.sum :salary, :group => :last_name
+
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 *Luke Redpath*
+
+* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. *Jeremy Kemper*
+
+* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 *Aggregated by schoenm@earthlink.net*
+
+* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 *rubyonrails@atyp.de*
+
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 *Stefan Kaes*
+
+* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first *Bob Silva*
+
+* Allow :include to be used with has_many :through associations #3611 *Michael Schoen*
+
+* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table). #2920 *Blair Zajac*
+
+* SQLServer: more compatible limit/offset emulation. #3779 *Tom Ward*
+
+* Polymorphic join support for has_one associations (has_one :foo, :as => :bar) #3785 *Rick Olson*
+
+* PostgreSQL: correctly parse negative integer column defaults. #3776 *bellis@deepthought.org*
+
+* Fix problems with count when used with :include *Jeremy Hopple and Kevin Clark*
+
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message *Tobias Lütke*
+
+* Using AssociationCollection#build with arrays of hashes should call build, not create *David Heinemeier Hansson*
+
+* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. *Nicholas Seckar*
+
+* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 *Maik Schmidt*
+
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 *Shugo Maeda*
+
+* Add documentation for add_index and remove_index. #3600 *Manfred Stienstra <m.stienstra@fngtps.com>*
+
+* If the OCI library is not available, raise an exception indicating as much. #3593 *Michael Schoen*
+
+* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. *Rick Olson*
+
+* Make dynamic finders honor additional passed in :conditions. #3569 *Oleg Pudeyev <pudeyo@rpi.edu>, Marcel Molina Jr.*
+
+* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. *Nicholas Seckar*
+
+* Make .count work for has_many associations with multi line finder sql *Michael Schoen*
+
+* Add AR::Base.base_class for querying the ancestor AR::Base subclass *Jamis Buck*
+
+* Allow configuration of the column used for optimistic locking *wilsonb@gmail.com*
+
+* Don't hardcode 'id' in acts as list. *ror@philippeapril.com*
+
+* Fix date errors for SQLServer in association tests. #3406 *Kevin Clark*
+
+* Escape database name in MySQL adapter when creating and dropping databases. #3409 *anna@wota.jp*
+
+* Disambiguate table names for columns in validates_uniqueness_of's WHERE clause. #3423 *alex.borovsky@gmail.com*
+
+* .with_scope imposed create parameters now bypass attr_protected *Tobias Lütke*
+
+* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. *Marcel Molina Jr.*
+
+* Multiple enhancements and adjustments to DB2 adaptor. #3377 *contact@maik-schmidt.de*
+
+* Sanitize scoped conditions. *Marcel Molina Jr.*
+
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) *David Heinemeier Hansson*
+
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. *Tobias Lütke*
+
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. *Tobias Lütke*
+
+* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 *yanowitz-rubyonrails@quantumfoam.org, Florian Weber*
+
+* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 *wejn@box.cz, Rick Olson, Scott Barron*
+
+* removed :piggyback in favor of just allowing :select on :through associations. *Tobias Lütke*
+
+* made method missing delegation to class methods on relation target work on :through associations. *Tobias Lütke*
+
+* made .find() work on :through relations. *Tobias Lütke*
+
+* Fix typo in association docs. #3296. *Blair Zajac*
+
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model *Tobias Lütke*
+
+## 1.13.2 (December 13th, 2005) ##
+
+* Become part of Rails 1.0
+
+* MySQL: allow encoding option for mysql.rb driver. *Jeremy Kemper*
+
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]. Example:
+
+ class Post
+ has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
+ end
+
+ post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
+ post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
+ post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
+
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations *David Heinemeier Hansson*
+
+* MySQL: fixes for the bundled mysql.rb driver. #3160 *Justin Forder*
+
+* SQLServer: fix obscure optimistic locking bug. #3068 *kajism@yahoo.com*
+
+* SQLServer: support uniqueidentifier columns. #2930 *keithm@infused.org*
+
+* SQLServer: cope with tables names qualified by owner. #3067 *jeff@ministrycentered.com*
+
+* SQLServer: cope with columns with "desc" in the name. #1950 *Ron Lusk, Ryan Tomayko*
+
+* SQLServer: cope with primary keys with "select" in the name. #3057 *rdifrango@captechventures.com*
+
+* Oracle: active? performs a select instead of a commit. #3133 *Michael Schoen*
+
+* MySQL: more robust test for nullified result hashes. #3124 *Stefan Kaes*
+
+* Reloading an instance refreshes its aggregations as well as its associations. #3024 *François Beausoleil*
+
+* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 *Paul Hammmond*
+
+* PostgreSQL: more robust sequence name discovery. #3087 *Rick Olson*
+
+* Oracle: use syntax compatible with Oracle 8. #3131 *Michael Schoen*
+
+* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. *Jeremy Kemper*
+
+* Added preliminary support for polymorphic associations *David Heinemeier Hansson*
+
+* Added preliminary support for join models *David Heinemeier Hansson*
+
+* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. *jeremy@jthopple.com, Marcel Molina Jr.*
+
+* Firebird: active? and reconnect! methods for handling stale connections. #428 *Ken Kunz <kennethkunz@gmail.com>*
+
+* Firebird: updated for FireRuby 0.4.0. #3009 *Ken Kunz <kennethkunz@gmail.com>*
+
+* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 *Jeremy Kemper*
+
+* Oracle: active? check pings the database rather than testing the last command status. #428 *Michael Schoen*
+
+* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 *kajism@yahoo.com*
+
+* Reloading a model doesn't lose track of its connection. #2996 *junk@miriamtech.com, Jeremy Kemper*
+
+* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 *colman@rominato.com, Florian Weber, Michael Schoen*
+
+* MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. #2978 *Shugo Maeda*
+
+* has_and_belongs_to_many: use JOIN instead of LEFT JOIN. *Jeremy Kemper*
+
+* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 *Shugo Maeda*
+
+* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. *Marcel Molina Jr.*
+
+* Correct boolean handling in generated reader methods. #2945 *Don Park, Stefan Kaes*
+
+* Don't generate read methods for columns whose names are not valid ruby method names. #2946 *Stefan Kaes*
+
+* Document :force option to create_table. #2921 *Blair Zajac <blair@orcaware.com>*
+
+* Don't add the same conditions twice in has_one finder sql. #2916 *Jeremy Evans*
+
+* Rename Version constant to VERSION. #2802 *Marcel Molina Jr.*
+
+* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 *Ken Kunz <kennethkunz@gmail.com>*
+
+* SQLServer: active? and reconnect! methods for handling stale connections. #428 *kajism@yahoo.com, Tom Ward <tom@popdog.net>*
+
+* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array. #1345 *MarkusQ@reality.com*
+
+* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 *kajism@yahoo.com, Tom Ward <tom@popdog.net>*
+
+* Oracle: active? and reconnect! methods for handling stale connections. Optionally retry queries after reconnect. #428 *Michael Schoen <schoenm@earthlink.net>*
+
+* Correct documentation for Base.delete_all. #1568 *Newhydra*
+
+* Oracle: test case for column default parsing. #2788 *Michael Schoen <schoenm@earthlink.net>*
+
+* Update documentation for Migrations. #2861 *Tom Werner <tom@cube6media.com>*
+
+* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? #428 *Jeremy Kemper*
+
+* Oracle: Much faster column reflection. #2848 *Michael Schoen <schoenm@earthlink.net>*
+
+* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. *Jeremy Kemper*
+
+* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. *Jeremy Kemper*
+
+* PostgreSQL: correctly discover custom primary key sequences. #2594 *Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper*
+
+* SQLServer: don't report limits for unsupported field types. #2835 *Ryan Tomayko*
+
+* Include the Enumerable module in ActiveRecord::Errors. *Rick Bradley <rick@rickbradley.com>*
+
+* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 *rubyonrails@atyp.de*
+
+* Don't cast nil or empty strings to a dummy date. #2789 *Rick Bradley <rick@rickbradley.com>*
+
+* acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 *rephorm@rephorm.com*
+
+* Fix sqlite adaptor's detection of missing dbfile or database declaration. *Nicholas Seckar*
+
+* Fixed acts_as_list for definitions without an explicit :order #2803 *Jonathan Viney*
+
+* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. *tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper*
+
+* Correct handling of complex order clauses with SQL Server limit emulation. #2770 *Tom Ward <tom@popdog.net>, Matt B.*
+
+* Correct whitespace problem in Oracle default column value parsing. #2788 *rick@rickbradley.com*
+
+* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 *larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper*
+
+* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. *Jeremy Kemper*
+
+* More compatible Oracle column reflection. #2771 *Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>*
+
+
+## 1.13.0 (November 7th, 2005) ##
+
+* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 *Ryan Tomayko*
+
+* Added :include as an option for association declarations [David Heinemeier Hansson]. Example:
+
+ has_many :posts, :include => [ :author, :comments ]
+
+* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints. Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes. [Jeremy Kemper] Example:
+
+ Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
+ # Find where name = ? and active=true
+ Comment.find :all, :conditions => ['name = ?', name]
+ # Create comment associated with :post_id
+ Comment.create :body => "Hello world"
+ end
+
+* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 *Ryan Tomayko*
+
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [David Heinemeier Hansson]. Example:
+
+ Comment.constrain(:creation => { :post_id => 5 }) do
+ # Associated with :post_id
+ Comment.create :body => "Hello world"
+ end
+
+ This is rarely used directly, but allows for find_or_create on associations. So you can do:
+
+ # If the tag doesn't exist, a new one is created that's associated with the person
+ person.tags.find_or_create_by_name("Summer")
+
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [David Heinemeier Hansson]. Example:
+
+ # No 'Summer' tag exists
+ Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
+
+ # Now the 'Summer' tag does exist
+ Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
+
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [David Heinemeier Hansson]. Example:
+
+ class Account < ActiveRecord::Base
+ has_many :people do
+ def find_or_create_by_name(name)
+ first_name, *last_name = name.split
+ last_name = last_name.join " "
+
+ find_or_create_by_first_name_and_last_name(first_name, last_name)
+ end
+ end
+ end
+
+ person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+ person.first_name # => "David"
+ person.last_name # => "Heinemeier Hansson"
+
+ Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
+
+* Omit internal dtproperties table from SQLServer table list. #2729 *Ryan Tomayko*
+
+* Quote column names in generated SQL. #2728 *Ryan Tomayko*
+
+* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 *Jeremy Kemper*
+
+* Add Model.create! to match existing model.save! method. When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors). *Jeremy Kemper*
+
+* Correct fixture behavior when table name pluralization is off. #2719 *Rick Bradley <rick@rickbradley.com>*
+
+* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 *Dan Peterson*
+
+* Added migration support for Oracle #2647 *Michael Schoen*
+
+* Worked around that connection can't be reset if allow_concurrency is off. #2648 *Michael Schoen <schoenm@earthlink.net>*
+
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 *Ryan Tomayko*
+
+* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 *Tom Ward*
+
+* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 *Tom Ward*
+
+* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility. #2618 *dansketcher@gmail.com*
+
+* Constraints are cloned so they can't be inadvertently modified while they're
+ in effect. Added :readonly finder constraint. Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true. [Jeremy Kemper <rails@bitsweat.net>]
+* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 *duane.johnson@gmail.com*
+
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 *Michael Schoen*
+
+* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 *maik schmidt*
+
+
+## 1.12.2 (October 26th, 2005) ##
+
+* Allow symbols to rename columns when using SQLite adapter. #2531 *Kevin Clark*
+
+* Map Active Record time to SQL TIME. #2575, #2576 *Robby Russell <robby@planetargon.com>*
+
+* Clarify semantics of ActiveRecord::Base#respond_to? #2560 *Stefan Kaes*
+
+* Fixed Association#clear for associations which have not yet been accessed. #2524 *Patrick Lenz <patrick@lenz.sh>*
+
+* HABTM finders shouldn't return readonly records. #2525 *Patrick Lenz <patrick@lenz.sh>*
+
+* Make all tests runnable on their own. #2521. *Blair Zajac <blair@orcaware.com>*
+
+
+## 1.12.1 (October 19th, 2005) ##
+
+* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
+
+* Correct PostgreSQL primary key sequence detection. #2507 *tmornini@infomania.com*
+
+* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
+
+
+## 1.12.0 (October 16th, 2005) ##
+
+* Update/clean up documentation (rdoc)
+
+* PostgreSQL sequence support. Use set_sequence_name in your model class to specify its primary key sequence. #2292 *Rick Olson <technoweenie@gmail.com>, Robby Russell <robby@planetargon.com>*
+
+* Change default logging colors to work on both white and black backgrounds. *Sam Stephenson*
+
+* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 *purestorm@ggnore.net*
+
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 *Robby Russell <robby@planetargon.com>*
+
+* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
+
+* Avoid memleak in dev mode when using fcgi
+
+* Simplified .clear on active record associations by using the existing delete_records method. #1906 *Caleb <me@cpb.ca>*
+
+* Delegate access to a customized primary key to the conventional id method. #2444. *Blair Zajac <blair@orcaware.com>*
+
+* Fix errors caused by assigning a has-one or belongs-to property to itself
+
+* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped *Sam Stephenson*
+
+* Update DB2 adapter. #2206. *contact@maik-schmidt.de*
+
+* Corrections to SQLServer native data types. #2267. *rails.20.clarry@spamgourmet.com*
+
+* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
+
+* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. *Blair Zajac <blair@orcaware.com>*
+
+* Misc doc fixes (typos/grammar/etc.). #2430. *coffee2code*
+
+* Add test coverage for content_columns. #2432. *coffee2code*
+
+* Speed up for unthreaded environments. #2431. *Stefan Kaes*
+
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. *Stefan Kaes*
+
+* Speed up the setting of table_name. #2428. *Stefan Kaes*
+
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Fix typo of 'constrains' to 'contraints'. #2069. *Michael Schuerig <michael@schuerig.de>*
+
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. *Michael Schuerig <michael@schuerig.de>*
+
+* Add geometric type for postgresql adapter. #2233 *Andrew Kaspick*
+
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. *Stefan Kaes*
+
+* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 *Chad Fowler <chad@chadfowler.com>, Nicholas Seckar*
+
+* Added :force option to create_table that'll try to drop the table if it already exists before creating
+
+* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. *Nicholas Seckar*
+
+* Use foreign_key inflection uniformly. #2156 *Blair Zajac <blair@orcaware.com>*
+
+* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys. #2221 *joergd@pobox.com, ObieFernandez <obiefernandez@gmail.com>*
+
+* Returning false from before_destroy should cancel the action. #1829 *Jeremy Huffman*
+
+* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type. #2256 *mat <mat@absolight.fr>*
+
+* Extensive documentation for the abstract database adapter. #2250 *François Beausoleil <fbeausoleil@ftml.net>*
+
+* Clean up Fixtures.reset_sequences for PostgreSQL. Handle tables with no rows and models with custom primary keys. #2174, #2183 *jay@jay.fm, Blair Zajac <blair@orcaware.com>*
+
+* Improve error message when nil is assigned to an attr which validates_size_of within a range. #2022 *Manuel Holtgrewe <purestorm@ggnore.net>*
+
+* Make update_attribute use the same writer method that update_attributes uses.
+ \#2237 [trevor@protocool.com]
+* Make migrations honor table name prefixes and suffixes. #2298 *Jakob Skjerning, Marcel Molina Jr.*
+
+* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 *dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org*
+
+* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id. #1749 *chris@chrisbrinker.com*
+
+* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
+
+* Added new symbol-driven approach to activating observers with Base#observers= [David Heinemeier Hansson]. Example:
+
+ ActiveRecord::Base.observers = :cacher, :garbage_collector
+
+* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 *solo@gatelys.com*
+
+* Wrap :conditions in parentheses to prevent problems with OR's #1871 *Jamis Buck*
+
+* Allow the postgresql adapter to work with the SchemaDumper. *Jamis Buck*
+
+* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. *Jamis Buck*
+
+* Fixed migrations for Windows when using more than 10 *David Naseby*
+
+* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 *Florian Weber*
+
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 *Tobias Lütke*
+
+* Improved migrations' behavior when the schema_info table is empty. *Nicholas Seckar*
+
+* Fixed that Observers didn't observe sub-classes #627 *Florian Weber*
+
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 *Marcel Molina Jr.*
+
+* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 *skae*
+
+* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 *Florian Weber*
+
+* Added better exception error when unknown column types are used with migrations #1814 *François Beausoleil*
+
+* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 *kajism@yahoo.com*
+
+* Fixed comparison of Active Record objects so two new objects are not equal #2099 *deberg*
+
+* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 *Tom Ward*
+
+* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 *coffee2code*
+
+* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 *sd@notso.net*
+
+* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
+
+* Make sure the schema_info table is created before querying the current version #1903
+
+* Fixtures ignore table name prefix and suffix #1987 *Jakob Skjerning*
+
+* Add documentation for index_type argument to add_index method for migrations #2005 *Blaine*
+
+* Modify read_attribute to allow a symbol argument #2024 *Ken Kunz*
+
+* Make destroy return self #1913 *Sebastian Kanthak*
+
+* Fix typo in validations documentation #1938 *court3nay*
+
+* Make acts_as_list work for insert_at(1) #1966 *hensleyl@papermountain.org*
+
+* Fix typo in count_by_sql documentation #1969 *Alexey Verkhovsky*
+
+* Allow add_column and create_table to specify NOT NULL #1712 *emptysands@gmail.com*
+
+* Fix create_table so that id column is implicitly added *Rick Olson*
+
+* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
+
+* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
+
+* Fixed the handling of camelCase columns names in Oracle #1798
+
+* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
+
+* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
+
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 *Tobias Lütke*
+
+ class Comment < AR:B
+ def self.search(q)
+ find(:all, :conditions => ["body = ?", q])
+ end
+ end
+
+ class Post < AR:B
+ has_many :comments
+ end
+
+ Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
+
+ NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
+ by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
+ details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
+ noticed :)
+
+* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 *Sam Stephenson*
+
+* Remove extra definition of supports_migrations? from abstract_adaptor.rb *Nicholas Seckar*
+
+* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
+
+* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 *Maik Schmidt*
+
+* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 *Stefan Kaes*
+
+
+## 1.11.1 (11 July, 2005) ##
+
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 *Rick Olson*
+
+* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 *tyler@kianta.com*
+
+* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 *Sam Stephenson*
+
+* Correct reflected table name for singular associations. #1688 *court3nay*
+
+* Fixed optimistic locking with SQL Server #1660 *tom@popdog.net*
+
+* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
+
+* Added better error message for "packets out of order" #1630 *court3nay*
+
+* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
+
+
+## 1.11.0 (6 July, 2005) ##
+
+* Fixed that Yaml error message in fixtures hid the real error #1623 *Nicholas Seckar*
+
+* Changed logging of SQL statements to use the DEBUG level instead of INFO
+
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Lütke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+
+* Added callback hooks to association collections #1549 [Florian Weber]. Example:
+
+ class Project
+ has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
+
+ def evaluate_velocity(developer)
+ ...
+ end
+ end
+
+ ..raising an exception will cause the object not to be added (or removed, with before_remove).
+
+
+* Fixed Base.content_columns call for SQL Server adapter #1450 *DeLynn Berry*
+
+* Fixed Base#write_attribute to work with both symbols and strings #1190 *Paul Legato*
+
+* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 *Florian Weber*
+
+* Speed up ActiveRecord#method_missing for the common case (read_attribute).
+
+* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 *Stefan Kaes*
+
+* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 *Chris McGrath*
+
+* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 *raidel@onemail.at*
+
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 *Michael Schuerig*
+
+* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 *DeLynn Berry*
+
+* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 *Mark Imbriaco/DeLynn Berry*
+
+* Fixed that multiparameter posts ignored attr_protected #1532 *alec+rails@veryclever.net*
+
+* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 *flash@vanklinkenbergsoftware.nl*
+
+* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be:
+
+ Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
+
+ ...should instead be:
+
+ Developer.find(
+ :all,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :conditions => 'project_id=1'
+ )
+
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 *Marcel Molina Jr.*
+
+* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 *cluon*
+
+* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
+
+* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 *Alisdair McDiarmid*
+
+* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
+
+* Allow any Enumerable, not just Array, to work as bind variables #1344 *Jeremy Kemper*
+
+* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
+ Example:
+
+ david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
+ david.save
+
+ If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
+ project is saved when david.save is called.
+
+ Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
+
+ david.project_ids = [1, 5, 7]
+
+* Corrected typo in find SQL for has_and_belongs_to_many. #1312 *ben@bensinclair.com*
+
+* Fixed sanitized conditions for has_many finder method. #1281 *jackc@hylesanderson.com, pragdave, Tobias Lütke*
+
+* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 *dave@cherryville.org*
+
+* Corrected @@configurations typo #1410 *david@ruppconsulting.com*
+
+* Return PostgreSQL columns in the order they were declared #1374 *perlguy@gmail.com*
+
+* Allow before/after update hooks to work on models using optimistic locking
+
+* Eager loading of dependent has_one associations won't delete the association #1212
+
+* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
+
+* Using transactional fixtures now causes the data to be loaded only once.
+
+* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
+
+ fixtures :web_sites
+
+ def test_something
+ assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+ end
+
+* Added DoubleRenderError exception that'll be raised if render* is called twice #518 *Nicholas Seckar*
+
+* Fixed exceptions occuring after render has been called #1096 *Nicholas Seckar*
+
+* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make " " fail the validation where it didn't before #1309
+
+* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
+
+* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
+
+ Conditional validations such as the following are made possible:
+ validates_numericality_of :income, :if => :employed?
+
+ Conditional validations can also solve the salted login generator problem:
+ validates_confirmation_of :password, :if => :new_password?
+
+ Using blocks:
+ validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
+
+* Fixed use of construct_finder_sql when using :join #1288 *dwlt@dwlt.net*
+
+* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 *Rick Olson*
+
+* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 *Jeremy Kemper*
+
+* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
+
+* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
+
+* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
+
+ # SELECT * FROM topics WHERE title IN ('First', 'Second')
+ Topic.find_all_by_title(["First", "Second"])
+
+* Added compatibility with camelCase column names for dynamic finders #533 *Dee Zsombor*
+
+* Fixed extraneous comma in count() function that made it not work with joins #1156 *Jarkko Laine/Dee Zsombor*
+
+* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 *Alisdair McDiarmid*
+
+* Fixed that validate_length_of lost :on option when :within was specified #1195 *jhosteny@mac.com*
+
+* Added encoding and min_messages options for PostgreSQL #1205 [Shugo Maeda]. Configuration example:
+
+ development:
+ adapter: postgresql
+ database: rails_development
+ host: localhost
+ username: postgres
+ password:
+ encoding: UTF8
+ min_messages: ERROR
+
+* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 *Jamis Buck*
+
+* Added validates_exclusion_of as a negative of validates_inclusion_of
+
+* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
+
+
+## 1.10.1 (20th April, 2005) ##
+
+* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
+
+* Fixed order of loading in eager associations
+
+* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
+
+
+## 1.10.0 (19th April, 2005) ##
+
+* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
+
+ for post in Post.find(:all, :limit => 100)
+ puts "Post: " + post.title
+ puts "Written by: " + post.author.name
+ puts "Last comment on: " + post.comments.first.created_on
+ end
+
+ This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
+
+ for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
+
+ ...and the number of database queries needed is now 1.
+
+* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
+
+ Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
+ Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
+ Person.find(:first, :order => "created_on DESC", :offset => 5)
+ Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
+ Person.find(:all, :offset => 10, :limit => 10)
+
+* Added acts_as_nested_set #1000 [wschenk]. Introduction:
+
+ This acts provides Nested Set functionality. Nested Set is similiar to Tree, but with
+ the added feature that you can select the children and all of it's descendants with
+ a single query. A good use case for this is a threaded post system, where you want
+ to display every reply to a comment without multiple selects.
+
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid *Dave Thomas*
+
+* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 *gnuman1@gmail.com*
+
+* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 *andrew.john.peters@gmail.com*
+
+* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 *stephenh@chase3000.com*
+
+* Fixed page caching for non-vhost applications living underneath the root #1004 *Ben Schumacher*
+
+* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 *adelle*
+
+* Added the option to specify the acceptance string in validates_acceptance_of #1106 *caleb@aei-tech.com*
+
+* Added insert_at(position) to acts_as_list #1083 *DeLynnB*
+
+* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
+
+* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 *yon@milliped.com*
+
+* Fixed boolean saving on Oracle #1093 *mparrish@pearware.org*
+
+* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
+
+* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 *Leon Bredt*
+
+* Added quoting of column names for fixtures #997 *jcfischer@gmail.com*
+
+* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 *Caleb Tennis*
+
+* Fixed that benchmarking times for rendering included db runtimes #987 *Stefan Kaes*
+
+* Fixed boolean queries for t/f fields in PostgreSQL #995 *dave@cherryville.org*
+
+* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 *Jeremy Kemper*
+
+* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 *dave@cherryville.org*
+
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 *Stefan Kaes*
+
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 *Stefan Kaes*
+
+* Fixed bug in Base#hash method that would treat records with the same string-based id as different *Dave Thomas*
+
+* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
+
+
+## 1.9.1 (27th March, 2005) ##
+
+* Fixed that Active Record objects with float attribute could not be cloned #808
+
+* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 *Nicholas Seckar*
+
+* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
+
+* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 *Scott Barron*
+
+* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 *delynnb*
+
+
+## 1.9.0 (22th March, 2005) ##
+
+* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
+
+ Developer.find_all nil, 'id ASC', 5 # return the first five developers
+ Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
+
+ This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
+
+* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 *Nicholas Seckar/Sam Stephenson*
+
+* Improved the performance of the OCI8 adapter for Oracle #723 *pilx/gjenkins*
+
+* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 *dave@cherryville.org*
+
+* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 *mindel*
+
+* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 *Alisdair McDiarmid*
+
+* Added the possibility of specifying fixtures in multiple calls #816 *kim@tinker.com*
+
+* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 *stian@grytoyr.net*
+
+* Added optionally allow for nil or empty strings with validates_numericality_of #801 *Sebastian Kanthak*
+
+* Fixed problem with using slashes in validates_format_of regular expressions #801 *Sebastian Kanthak*
+
+* Fixed that SQLite3 exceptions are caught and reported properly #823 *yerejm*
+
+* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
+
+* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
+
+
+## 1.8.0 (7th March, 2005) ##
+
+* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
+
+* Added support for timestamp with time zone in PostgreSQL #560 *Scott Barron*
+
+* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
+
+ * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
+ +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
+ objects that should be inspected to determine which attributes triggered the errors.
+ * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
+ You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
+
+* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 *rodrigo k*
+
+* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 *Jeremy Kemper*
+
+* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
+
+ class Account < ActiveRecord::Base
+ has_one :credit_card, :dependent => true
+ end
+ class CreditCard < ActiveRecord::Base
+ belongs_to :account
+ end
+
+ account.credit_card # => returns existing credit card, lets say id = 12
+ account.credit_card = CreditCard.create("number" => "123")
+ account.save # => CC with id = 12 is destroyed
+
+
+* Added validates_numericality_of #716 [Sebastian Kanthak/Chris McGrath]. Docuemntation:
+
+ Validates whether the value of the specified attribute is numeric by trying to convert it to
+ a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
+ <tt>/^[\+\-]?\d+$/</tt> (if <tt>integer</tt> is set to true).
+
+ class Person < ActiveRecord::Base
+ validates_numericality_of :value, :on => :create
+ end
+
+ Configuration options:
+ * <tt>message</tt> - A custom error message (default is: "is not a number")
+ * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
+ * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
+
+
+* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 *Scott Barron*
+
+* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
+
+* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 *james@slashetc.com*
+
+
+## 1.7.0 (24th February, 2005) ##
+
+* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 *Jamis Buck*
+
+* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
+
+ 1. Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
+ and safe way of passing table-specific sequence information to the adapter.)
+ 2. Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
+ resort to some hacks to get data converted to Date or Time in Ruby.
+ If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
+ hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
+ This is nasty - but if you use Duck Typing you'll probably not care very much.
+ In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
+ valid - too many databases use DATE for both.
+ Timezones and sub-second precision on timestamps are not supported.
+ 3. Default values that are functions (such as "SYSDATE") are not supported. This is a
+ restriction of the way active record supports default values.
+ 4. Referential integrity constraints are not fully supported. Under at least
+ some circumstances, active record appears to delete parent and child records out of
+ sequence and out of transaction scope. (Or this may just be a problem of test setup.)
+
+ The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
+
+* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 *YuriSchimke*
+
+* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 *yerejm*
+
+* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 *tonka*
+
+* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 *Nicholas Seckar*
+
+* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 *daniel@nightrunner.com*
+
+* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 *adelle@bullet.net.au*
+
+* Fixed that find_by_* would fail when column names had numbers #670 *demetrius*
+
+* Fixed the SQL Server adapter on a bunch of issues #667 *DeLynn*
+
+ 1. Created a new columns method that is much cleaner.
+ 2. Corrected a problem with the select and select_all methods
+ that didn't account for the LIMIT clause being passed into raw SQL statements.
+ 3. Implemented the string_to_time method in order to create proper instances of the time class.
+ 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
+ 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
+
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 *lmarlow*
+
+* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
+
+ class Person < ActiveRecord::Base
+ validates_each :first_name, :last_name do |record, attr|
+ record.errors.add attr, 'starts with z.' if attr[0] == ?z
+ end
+ end
+
+* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 *Jeremy Kemper*
+
+* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
+
+ class Person < ActiveRecord::Base
+ validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
+ end
+
+* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 *Tim Bates*
+
+* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
+
+ class Project < ActiveRecord::Base
+ primary_key "sysid"
+ table_name "XYZ_PROJECT"
+ inheritance_column { original_inheritance_column + "_id" }
+ end
+
+* Fixed Base#clone for use with PostgreSQL #565 *hanson@surgery.wisc.edu*
+
+
+## 1.6.0 (January 25th, 2005) ##
+
+* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
+
+* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
+
+* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
+
+* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
+
+ people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+ Person.update(people.keys, people.values)
+
+* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 *Scott Baron*
+
+* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 *notahat*
+
+* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 *Demetrius*
+
+* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 *Jeremy Kemper*
+
+* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 *Eric Anderson*
+
+* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 *Eric Anderson*
+
+* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
+
+* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
+
+* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 *Demetrius*
+
+* Fixed that find_all would produce invalid sql when called sequentialy #490 *Scott Baron*
+
+
+## 1.5.1 (January 18th, 2005) ##
+
+* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 *Tim Bates*
+
+* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 *Pelle*
+
+
+## 1.5.0 (January 17th, 2005) ##
+
+* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 *Eric Hodel*
+
+* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
+
+ class Book < ActiveRecord::Base
+ has_many :pages
+ belongs_to :library
+
+ validates_associated :pages, :library
+ end
+
+* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
+
+ == Unsaved objects and associations
+
+ You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
+ aware of, mostly involving the saving of associated objects.
+
+ === One-to-one associations
+
+ * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
+ order to update their primary keys - except if the parent object is unsaved (new_record? == true).
+ * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
+ is cancelled.
+ * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
+ * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
+ not save the parent either.
+
+ === Collections
+
+ * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
+ (the owner of the collection) is not yet stored in the database.
+ * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
+ * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
+ * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
+
+* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 *Tim Bates*
+
+* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 *Tim Bates*
+
+* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 *Tim Bates*
+
+* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 *Tim Bates*
+
+* Fixed binary support for PostgreSQL #444 *alex@byzantine.no*
+
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size 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 less SELECT query if you use length.
+
+* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 *atyp.de*
+
+* Fixed that foreign keys named the same as the association would cause stack overflow #437 *Eric Anderson*
+
+* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 *Alexey*
+
+* Added Base#reload that reloads the attributes of an object from the database #422 *Andreas Schwarz*
+
+* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 *Jeremy Kemper*
+
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 *Matt Mower*
+
+* Added that Observers can use the observes class method instead of overwriting self.observed_class().
+
+ Before:
+ class ListSweeper < ActiveRecord::Base
+ def self.observed_class() [ List, Item ]
+ end
+
+ After:
+ class ListSweeper < ActiveRecord::Base
+ observes List, Item
+ end
+
+* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
+
+* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
+
+* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
+
+ Before: topic.update_attribute(:approved, !approved?)
+ After : topic.toggle!(:approved)
+
+* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
+
+ page.views # => 1
+ page.increment!(:views) # executes an UPDATE statement
+ page.views # => 2
+
+ page.increment(:views).increment!(:views)
+ page.views # => 4
+
+* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
+
+
+## 1.4.0 (January 4th, 2005) ##
+
+* Added automated optimistic locking if the field <tt>lock_version</tt> is present. Each update to the
+ record increments the lock_version column and the locking facilities ensure that records instantiated twice
+ will let the last one saved raise a StaleObjectError if the first was also updated. Example:
+
+ p1 = Person.find(1)
+ p2 = Person.find(1)
+
+ p1.first_name = "Michael"
+ p1.save
+
+ p2.first_name = "should fail"
+ p2.save # Raises a ActiveRecord::StaleObjectError
+
+ You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
+ or otherwise apply the business logic needed to resolve the conflict.
+
+ \#384 *Michael Koziarski*
+
+* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL.
+ They work by appending the name of an attribute to <tt>find_by_</tt>, so you get finders like <tt>Person.find_by_user_name,
+ Payment.find_by_transaction_id</tt>. So instead of writing <tt>Person.find_first(["user_name = ?", user_name])</tt>, you just do
+ <tt>Person.find_by_user_name(user_name)</tt>.
+
+ It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
+ <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
+ <tt>Person.find_first(["user_name = ? AND password = ?", user_name, password])</tt>, you just do
+ <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
+
+ While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like
+ <tt>Payment.find_all_by_amount(50)</tt> that is turned into <tt>Payment.find_all(["amount = ?", 50])</tt>. This is something not as equally useful,
+ though, as it's not possible to specify the order in which the objects are returned.
+
+* Added block-style for callbacks #332 [Jeremy Kemper].
+
+ Before:
+ before_destroy(Proc.new{ |record| Person.destroy_all "firm_id = #{record.id}" })
+
+ After:
+ before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
+
+* Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 *Josh Peek*
+
+* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates
+ and times from the database. This is set to :local by default.
+
+* Added the possibility for adapters to overwrite add_limit! to implement a different limiting scheme than "LIMIT X" used by MySQL, PostgreSQL, and SQLite.
+
+* Added the possibility of having objects with acts_as_list created before their scope is available or...
+
+* Added a db2 adapter that only depends on the Ruby/DB2 bindings (http://raa.ruby-lang.org/project/ruby-db2/) #386 *Maik Schmidt*
+
+* Added the final touches to the Microsoft SQL Server adapter by Joey Gibson that makes it suitable for actual use #394 *DeLynn Barry*
+
+* Added that Base#find takes an optional options hash, including :conditions. Base#find_on_conditions deprecated in favor of #find with :conditions #407 *Jeremy Kemper*
+
+* Added HasManyAssociation#count that works like Base#count #413 *intinig*
+
+* Fixed handling of binary content in blobs and similar fields for Ruby/MySQL and SQLite #409 *xal*
+
+* Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 *Tobias Lütke*
+
+* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it.
+ If require_association did not set the constant then const_get will call const_missing, resulting in an infinite loop #380 *Jeremy Kemper*
+
+* Fixed broken transactions that were actually only running object-level and not db level transactions *andreas*
+
+* Fixed that validates_uniqueness_of used 'id' instead of defined primary key #406
+
+* Fixed that the overwritten respond_to? method didn't take two parameters like the original #391
+
+* Fixed quoting in validates_format_of that would allow some rules to pass regardless of input #390 *Dmitry V. Sabanin*
+
+
+## 1.3.0 (December 23, 2004) ##
+
+* Added a require_association hook on const_missing that makes it possible to use any model class without requiring it first. This makes STI look like:
+
+ before:
+ require_association 'person'
+ class Employee < Person
+ end
+
+ after:
+ class Employee < Person
+ end
+
+ This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes.
+
+* Added that Base.update_all and Base.delete_all return an integer of the number of affected rows #341
+
+* Added scope option to validation_uniqueness #349 *Kent Sibilev*
+
+* Added respondence to *_before_type_cast for all attributes to return their string-state before they were type casted by the column type.
+ This is helpful for getting "100,000" back on a integer-based validation where the value would normally be "100".
+
+* Added allow_nil options to validates_inclusion_of so that validation is only triggered if the attribute is not nil *what-a-day*
+
+* Added work-around for PostgreSQL and the problem of getting fixtures to be created from id 1 on each test case.
+ This only works for auto-incrementing primary keys called "id" for now #359 *Scott Baron*
+
+* Added Base#clear_association_cache to empty all the cached associations #347 *Tobias Lütke*
+
+* Added more informative exceptions in establish_connection #356 *Jeremy Kemper*
+
+* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise).
+
+ Before:
+ person.attributes = @params["person"]
+ person.save
+
+ Now:
+ person.update_attributes(@params["person"])
+
+* Added Base.destroy and Base.delete to remove records without holding a reference to them first.
+
+* Added that query benchmarking will only happen if its going to be logged anyway #344
+
+* Added higher_item and lower_item as public methods for acts_as_list #342 *Tobias Lütke*
+
+* Fixed that options[:counter_sql] was overwritten with interpolated sql rather than original sql #355 *Jeremy Kemper*
+
+* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used
+ the attributes[] hash instead of checking for @base.respond_to?(attr.to_s). *Marten*
+
+* Fixed that Base.table_name would expect a parameter when used in has_and_belongs_to_many joins *Anna Lissa Cruz*
+
+* Fixed that nested transactions now work by letting the outer most transaction have the responsibilty of starting and rolling back the transaction.
+ If any of the inner transactions swallow the exception raised, though, the transaction will not be rolled back. So always let the transaction
+ bubble up even when you've dealt with local issues. Closes #231 and #340.
+
+* Fixed validates_{confirmation,acceptance}_of to only happen when the virtual attributes are not nil #348 *dpiddy@gmail.com*
+
+* Changed the interface on AbstractAdapter to require that adapters return the number of affected rows on delete and update operations.
+
+* Fixed the automated timestamping feature when running under Rails' development environment that resets the inheritable attributes on each request.
+
+
+
+## 1.2.0 ##
+
+* Added Base.validates_inclusion_of that validates whether the value of the specified attribute is available in a particular enumerable
+ object. *what-a-day*
+
+ class Person < ActiveRecord::Base
+ validates_inclusion_of :gender, :in=>%w( m f ), :message=>"woah! what are you then!??!!"
+ validates_inclusion_of :age, :in=>0..99
+ end
+
+* Added acts_as_list that can decorates an existing class with methods like move_higher/lower, move_to_top/bottom. [Tobias Lütke] Example:
+
+ class TodoItem < ActiveRecord::Base
+ acts_as_list :scope => :todo_list_id
+ belongs_to :todo_list
+ end
+
+* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in
+ categories and the likes. *Tobias Lütke*
+
+* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names
+ created_at/created_on or updated_at/updated_on are present. *Tobias Lütke*
+
+* Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place *Tobias Lütke*
+
+* Added automatic transaction block around AssociationCollection.<<, AssociationCollection.delete, and AssociationCollection.destroy_all
+
+* Fixed that Base#find will return an array if given an array -- regardless of the number of elements #270 *Marten*
+
+* Fixed that has_and_belongs_to_many would generate bad sql when naming conventions differed from using vanilla "id" everywhere *RedTerror*
+
+* Added a better exception for when a type column is used in a table without the intention of triggering single-table inheritance. Example:
+
+ ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'bad_class!'.
+ This error is raised because the column 'type' is reserved for storing the class in case of inheritance.
+ Please rename this column if you didn't intend it to be used for storing the inheritance class or
+ overwrite Company.inheritance_column to use another column for that information.
+
+* Added that single-table inheritance will only kick in if the inheritance_column (by default "type") is present. Otherwise, inheritance won't
+ have any magic side effects.
+
+* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 *mjobin*
+
+* Fixed Base.errors to be indifferent as to whether strings or symbols are used. Examples:
+
+ Before:
+ errors.add(:name, "must be shorter") if name.size > 10
+ errors.on(:name) # => "must be shorter"
+ errors.on("name") # => nil
+
+ After:
+ errors.add(:name, "must be shorter") if name.size > 10
+ errors.on(:name) # => "must be shorter"
+ errors.on("name") # => "must be shorter"
+
+* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching
+ it against the regular expression provided. *Marcel Molina Jr.*
+
+ class Person < ActiveRecord::Base
+ validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :on => :create
+ end
+
+* Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Lütke]. Example:
+
+ Validates that the specified attribute matches the length restrictions supplied in either:
+
+ - configuration[:minimum]
+ - configuration[:maximum]
+ - configuration[:is]
+ - configuration[:within] (aka. configuration[:in])
+
+ Only one option can be used at a time.
+
+ class Person < ActiveRecord::Base
+ validates_length_of :first_name, :maximum=>30
+ validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
+ validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
+ validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
+ validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
+ end
+
+* Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself.
+
+* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system.
+ Useful for making sure that only one user can be named "davidhh".
+
+ class Person < ActiveRecord::Base
+ validates_uniqueness_of :user_name
+ end
+
+ When the record is created, a check is performed to make sure that no record exist in the database with the given value for the specified
+ attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
+
+
+* Added Base.validates_confirmation_of that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
+
+ Model:
+ class Person < ActiveRecord::Base
+ validates_confirmation_of :password
+ end
+
+ View:
+ <%= password_field "person", "password" %>
+ <%= password_field "person", "password_confirmation" %>
+
+ The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
+ It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
+
+
+* Added Base.validates_acceptance_of that encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
+
+ class Person < ActiveRecord::Base
+ validates_acceptance_of :terms_of_service
+ end
+
+ The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
+
+ NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
+
+
+* Added validation macros to make the stackable just like the life cycle callbacks. Examples:
+
+ class Person < ActiveRecord::Base
+ validate { |record| record.errors.add("name", "too short") unless name.size > 10 }
+ validate { |record| record.errors.add("name", "too long") unless name.size < 20 }
+ validate_on_create :validate_password
+
+ private
+ def validate_password
+ errors.add("password", "too short") unless password.size > 6
+ end
+ end
+
+* Added the option for sanitizing find_by_sql and the offset parts in regular finds [Sam Stephenson]. Examples:
+
+ Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
+ Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
+
+* Fixed value quoting in all generated SQL statements, so that integers are not surrounded in quotes and that all sanitation are happening
+ through the database's own quoting routine. This should hopefully make it lots easier for new adapters that doesn't accept '1' for integer
+ columns.
+
+* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient
+ *Florian Weber*
+
+* Added counter_sql option for has_many associations [Jeremy Kemper]. Documentation:
+
+ <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
+ specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
+
+* Fixed that methods wrapped in callbacks still return their original result #260 *Jeremy Kemper*
+
+* Fixed the Inflector to handle the movie/movies pair correctly #261 *Scott Baron*
+
+* Added named bind-style variable interpolation #281 [Michael Koziarski]. Example:
+
+ Person.find(["id = :id and first_name = :first_name", { :id => 5, :first_name = "bob' or 1=1" }])
+
+* Added bind-style variable interpolation for the condition arrays that uses the adapter's quote method *Michael Koziarski*
+
+ Before:
+ find_first([ "user_name = '%s' AND password = '%s'", user_name, password ])]
+ find_first([ "firm_id = %s", firm_id ])] # unsafe!
+
+ After:
+ find_first([ "user_name = ? AND password = ?", user_name, password ])]
+ find_first([ "firm_id = ?", firm_id ])]
+
+* Added CSV format for fixtures #272 [what-a-day]. (See the new and expanded documentation on fixtures for more information)
+
+* Fixed fixtures using primary key fields called something else than "id" *dave*
+
+* Added proper handling of time fields that are turned into Time objects with the dummy date of 2000/1/1 *HariSeldon*
+
+* Added reverse order of deleting fixtures, so referential keys can be maintained #247 *Tim Bates*
+
+* Added relative path search for sqlite dbfiles in database.yml (if RAILS_ROOT is defined) #233 *Jeremy Kemper*
+
+* Added option to establish_connection where you'll be able to leave out the parameter to have it use the RAILS_ENV environment variable
+
+* Fixed problems with primary keys and postgresql sequences (#230) *Tim Bates*
+
+* Added reloading for associations under cached environments like FastCGI and mod_ruby. This makes it possible to use those environments for development.
+ This is turned on by default, but can be turned off with ActiveRecord::Base.reload_dependencies = false in production environments.
+
+ NOTE: This will only have an effect if you let the associations manage the requiring of model classes. All libraries loaded through
+ require will be "forever" cached. You can, however, use ActiveRecord::Base.load_or_require("library") to get this behavior outside of the
+ auto-loading associations.
+
+* Added ERB capabilities to the fixture files for dynamic fixture generation. You don't need to do anything, just include ERB blocks like:
+
+ david:
+ id: 1
+ name: David
+
+ jamis:
+ id: 2
+ name: Jamis
+
+ <% for digit in 3..10 %>
+ dev_<%= digit %>:
+ id: <%= digit %>
+ name: fixture_<%= digit %>
+ <% end %>
+
+* Changed the yaml fixture searcher to look in the root of the fixtures directory, so when you before could have something like:
+
+ fixtures/developers/fixtures.yaml
+ fixtures/accounts/fixtures.yaml
+
+ ...you now need to do:
+
+ fixtures/developers.yaml
+ fixtures/accounts.yaml
+
+* Changed the fixture format from:
+
+ name: david
+ data:
+ id: 1
+ name: David Heinemeier Hansson
+ birthday: 1979-10-15
+ profession: Systems development
+ ---
+ name: steve
+ data:
+ id: 2
+ name: Steve Ross Kellock
+ birthday: 1974-09-27
+ profession: guy with keyboard
+
+ ...to:
+
+ david:
+ id: 1
+ name: David Heinemeier Hansson
+ birthday: 1979-10-15
+ profession: Systems development
+
+ steve:
+ id: 2
+ name: Steve Ross Kellock
+ birthday: 1974-09-27
+ profession: guy with keyboard
+
+ The change is NOT backwards compatible. Fixtures written in the old YAML style needs to be rewritten!
+
+* All associations will now attempt to require the classes that they associate to. Relieving the need for most explicit 'require' statements.
+
+
+## 1.1.0 (34) ##
+
+* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically
+ instantiated in instance variables relating to their names using the following style:
+
+ class FixturesTest < Test::Unit::TestCase
+ fixtures :developers # you can add more with comma separation
+
+ def test_developers
+ assert_equal 3, @developers.size # the container for all the fixtures is automatically set
+ assert_kind_of Developer, @david # works like @developers["david"].find
+ assert_equal "David Heinemeier Hansson", @david.name
+ end
+ end
+
+* Added HasAndBelongsToManyAssociation#push_with_attributes(object, join_attributes) that can create associations in the join table with additional
+ attributes. This is really useful when you have information that's only relevant to the join itself, such as a "added_on" column for an association
+ between post and category. The added attributes will automatically be injected into objects retrieved through the association similar to the piggy-back
+ approach:
+
+ post.categories.push_with_attributes(category, :added_on => Date.today)
+ post.categories.first.added_on # => Date.today
+
+ NOTE: The categories table doesn't have a added_on column, it's the categories_post join table that does!
+
+* Fixed that :exclusively_dependent and :dependent can't be activated at the same time on has_many associations *Jeremy Kemper*
+
+* Fixed that database passwords couldn't be all numeric *Jeremy Kemper*
+
+* Fixed that calling id would create the instance variable for new_records preventing them from being saved correctly *Jeremy Kemper*
+
+* Added sanitization feature to HasManyAssociation#find_all so it works just like Base.find_all *Sam Stephenson/Jeremy Kemper*
+
+* Added that you can pass overlapping ids to find without getting duplicated records back *Jeremy Kemper*
+
+* Added that Base.benchmark returns the result of the block *Jeremy Kemper*
+
+* Fixed problem with unit tests on Windows with SQLite *paterno*
+
+* Fixed that quotes would break regular non-yaml fixtures *Dmitry Sabanin/daft*
+
+* Fixed fixtures on windows with line endings cause problems under unix / mac *Tobias Lütke*
+
+* Added HasAndBelongsToManyAssociation#find(id) that'll search inside the collection and find the object or record with that id
+
+* Added :conditions option to has_and_belongs_to_many that works just like the one on all the other associations
+
+* Added AssociationCollection#clear to remove all associations from has_many and has_and_belongs_to_many associations without destroying the records *geech*
+
+* Added type-checking and remove in 1-instead-of-N sql statements to AssociationCollection#delete *geech*
+
+* Added a return of self to AssociationCollection#<< so appending can be chained, like project << Milestone.create << Milestone.create *geech*
+
+* Added Base#hash and Base#eql? which means that all of the equality using features of array and other containers now works:
+
+ [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
+
+* Added :uniq as an option to has_and_belongs_to_many which will automatically ensure that AssociateCollection#uniq is called
+ before pulling records out of the association. This is especially useful for three-way (and above) has_and_belongs_to_many associations.
+
+* Added AssociateCollection#uniq which is especially useful for has_and_belongs_to_many associations that can include duplicates,
+ which is common on associations that also use metadata. Usage: post.categories.uniq
+
+* Fixed respond_to? to use a subclass specific hash instead of an Active Record-wide one
+
+* Fixed has_and_belongs_to_many to treat associations between classes in modules properly *Florian Weber*
+
+* Added a NoMethod exception to be raised when query and writer methods are called for attributes that doesn't exist *geech*
+
+* Added a more robust version of Fixtures that throws meaningful errors when on formatting issues *geech*
+
+* Added Base#transaction as a compliment to Base.transaction for prettier use in instance methods *geech*
+
+* Improved the speed of respond_to? by placing the dynamic methods lookup table in a hash *geech*
+
+* Added that any additional fields added to the join table in a has_and_belongs_to_many association
+ will be placed as attributes when pulling records out through has_and_belongs_to_many associations.
+ This is helpful when have information about the association itself that you want available on retrival.
+
+* Added better loading exception catching and RubyGems retries to the database adapters *alexeyv*
+
+* Fixed bug with per-model transactions *daniel*
+
+* Fixed Base#transaction so that it returns the result of the last expression in the transaction block *alexeyv*
+
+* Added Fixture#find to find the record corresponding to the fixture id. The record
+ class name is guessed by using Inflector#classify (also new) on the fixture directory name.
+
+ Before: Document.find(@documents["first"]["id"])
+ After : @documents["first"].find
+
+* Fixed that the table name part of column names ("TABLE.COLUMN") wasn't removed properly *Andreas Schwarz*
+
+* Fixed a bug with Base#size when a finder_sql was used that didn't capitalize SELECT and FROM *geech*
+
+* Fixed quoting problems on SQLite by adding quote_string to the AbstractAdapter that can be overwritten by the concrete
+ adapters for a call to the dbm. *Andreas Schwarz*
+
+* Removed RubyGems backup strategy for requiring SQLite-adapter -- if people want to use gems, they're already doing it with AR.
+
+
+## 1.0.0 (35) ##
+
+* Added OO-style associations methods [Florian Weber]. Examples:
+
+ Project#milestones_count => Project#milestones.size
+ Project#build_to_milestones => Project#milestones.build
+ Project#create_for_milestones => Project#milestones.create
+ Project#find_in_milestones => Project#milestones.find
+ Project#find_all_in_milestones => Project#milestones.find_all
+
+* Added serialize as a new class method to control when text attributes should be YAMLized or not. This means that automated
+ serialization of hashes, arrays, and so on WILL NO LONGER HAPPEN (#10). You need to do something like this:
+
+ class User < ActiveRecord::Base
+ serialize :settings
+ end
+
+ This will assume that settings is a text column and will now YAMLize any object put in that attribute. You can also specify
+ an optional :class_name option that'll raise an exception if a serialized object is retrieved as a descendant of a class not in
+ the hierarchy. Example:
+
+ class User < ActiveRecord::Base
+ serialize :settings, :class_name => "Hash"
+ end
+
+ user = User.create("settings" => %w( one two three ))
+ User.find(user.id).settings # => raises SerializationTypeMismatch
+
+* Added the option to connect to a different database for one model at a time. Just call establish_connection on the class
+ you want to have connected to another database than Base. This will automatically also connect decendents of that class
+ to the different database [Renald Buter].
+
+* Added transactional protection for Base#save. Validations can now check for values knowing that it happens in a transaction and callbacks
+ can raise exceptions knowing that the save will be rolled back. *Suggested by Alexey Verkhovsky*
+
+* Added column name quoting so reserved words, such as "references", can be used as column names *Ryan Platte*
+
+* Added the possibility to chain the return of what happened inside a logged block [geech]:
+
+ This now works:
+ log { ... }.map { ... }
+
+ Instead of doing:
+ result = []
+ log { result = ... }
+ result.map { ... }
+
+* Added "socket" option for the MySQL adapter, so you can change it to something else than "/tmp/mysql.sock" *Anna Lissa Cruz*
+
+* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema,
+ person.respond_to? "name" will return true.
+
+* Added Base.benchmark which can be used to aggregate logging and benchmark, so you can measure and represent multiple statements in a single block.
+ Usage (hides all the SQL calls for the individual actions and calculates total runtime for them all):
+
+ Project.benchmark("Creating project") do
+ project = Project.create("name" => "stuff")
+ project.create_manager("name" => "David")
+ project.milestones << Milestone.find_all
+ end
+
+* Added logging of invalid SQL statements *Daniel Von Fange*
+
+* Added alias Errors#[] for Errors#on, so you can now say person.errors["name"] to retrieve the errors for name *Andreas Schwarz*
+
+* Added RubyGems require attempt if sqlite-ruby is not available through regular methods.
+
+* Added compatibility with 2.x series of sqlite-ruby drivers. *Jamis Buck*
+
+* Added type safety for association assignments, so a ActiveRecord::AssociationTypeMismatch will be raised if you attempt to
+ assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on
+ mistaken association assignments. *Reported by Andreas Schwarz*
+
+* Added the option to keep many fixtures in one single YAML document *what-a-day*
+
+* Added the class method "inheritance_column" that can be overwritten to return the name of an alternative column than "type" for storing
+ the type for inheritance hierarchies. *Dave Steinberg*
+
+* Added [] and []= as an alternative way to access attributes when the regular methods have been overwritten *Dave Steinberg*
+
+* Added the option to observer more than one class at the time by specifying observed_class as an array
+
+* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them
+ on PostgreSQL. *Dave Steinberg*
+
+* Changed that integer and floats set to "" through attributes= remain as NULL. This was especially a problem for scaffolding and postgresql. (#49)
+
+* Changed the MySQL Adapter to rely on MySQL for its defaults for socket, host, and port *Andreas Schwarz*
+
+* Changed ActionControllerError to decent from StandardError instead of Exception. It can now be caught by a generic rescue.
+
+* Changed class inheritable attributes to not use eval *Caio Chassot*
+
+* Changed Errors#add to now use "invalid" as the default message instead of true, which means full_messages work with those *Marcel Molina Jr.*
+
+* Fixed spelling on Base#add_on_boundry_breaking to Base#add_on_boundary_breaking (old naming still works) *Marcel Molina Jr.*
+
+* Fixed that entries in the has_and_belongs_to_many join table didn't get removed when an associated object was destroyed.
+
+* Fixed unnecessary calls to SET AUTOCOMMIT=0/1 for MySQL adapter *Andreas Schwarz*
+
+* Fixed PostgreSQL defaults are now handled gracefully *Dave Steinberg*
+
+* Fixed increment/decrement_counter are now atomic updates *Andreas Schwarz*
+
+* Fixed the problems the Inflector had turning Attachment into attuchments and Cases into Casis *radsaq/Florian Gross*
+
+* Fixed that cloned records would point attribute references on the parent object *Andreas Schwarz*
+
+* Fixed SQL for type call on inheritance hierarchies *Caio Chassot*
+
+* Fixed bug with typed inheritance *Florian Weber*
+
+* Fixed a bug where has_many collection_count wouldn't use the conditions specified for that association
+
+
+## 0.9.5 ##
+
+* Expanded the table_name guessing rules immensely [Florian Green]. Documentation:
+
+ Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
+ directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
+ to guess the table name from even when called on Reply. The guessing rules are as follows:
+ * Class name ends in "x", "ch" or "ss": "es" is appended, so a Search class becomes a searches table.
+ * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies",
+ so a Category class becomes a categories table.
+ * Class name ends in "fe": The "fe" is replaced with "ves", so a Wife class becomes a wives table.
+ * Class name ends in "lf" or "rf": The "f" is replaced with "ves", so a Half class becomes a halves table.
+ * Class name ends in "person": The "person" is replaced with "people", so a Salesperson class becomes a salespeople table.
+ * Class name ends in "man": The "man" is replaced with "men", so a Spokesman class becomes a spokesmen table.
+ * Class name ends in "sis": The "i" is replaced with an "e", so a Basis class becomes a bases table.
+ * Class name ends in "tum" or "ium": The "um" is replaced with an "a", so a Datum class becomes a data table.
+ * Class name ends in "child": The "child" is replaced with "children", so a NodeChild class becomes a node_children table.
+ * Class name ends in an "s": No additional characters are added or removed.
+ * Class name doesn't end in "s": An "s" is appended, so a Comment class becomes a comments table.
+ * Class name with word compositions: Compositions are underscored, so CreditCard class becomes a credit_cards table.
+ Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended.
+ So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts".
+
+ You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a
+ "mice" table. Example:
+
+ class Mouse < ActiveRecord::Base
+ def self.table_name() "mice" end
+ end
+
+ This conversion is now done through an external class called Inflector residing in lib/active_record/support/inflector.rb.
+
+* Added find_all_in_collection to has_many defined collections. Works like this:
+
+ class Firm < ActiveRecord::Base
+ has_many :clients
+ end
+
+ firm.id # => 1
+ firm.find_all_in_clients "revenue > 1000" # SELECT * FROM clients WHERE firm_id = 1 AND revenue > 1000
+
+ *Requested by Dave Thomas*
+
+* Fixed finders for inheritance hierarchies deeper than one level *Florian Weber*
+
+* Added add_on_boundry_breaking to errors to accompany add_on_empty as a default validation method. It's used like this:
+
+ class Person < ActiveRecord::Base
+ protected
+ def validation
+ errors.add_on_boundry_breaking "password", 3..20
+ end
+ end
+
+ This will add an error to the tune of "is too short (minimum is 3 characters)" or "is too long (minimum is 20 characters)" if
+ the password is outside the boundry. The messages can be changed by passing a third and forth parameter as message strings.
+
+* Implemented a clone method that works properly with AR. It returns a clone of the record that
+ hasn't been assigned an id yet and is treated as a new record.
+
+* Allow for domain sockets in PostgreSQL by not assuming localhost when no host is specified *Scott Barron*
+
+* Fixed that bignums are saved properly instead of attempted to be YAMLized *Andreas Schwartz*
+
+* Fixed a bug in the GEM where the rdoc options weren't being passed according to spec *Chad Fowler*
+
+* Fixed a bug with the exclusively_dependent option for has_many
+
+
+## 0.9.4 ##
+
+* Correctly guesses the primary key when the class is inside a module [Dave Steinberg].
+
+* Added [] and []= as alternatives to read_attribute and write_attribute *Dave Steinberg*
+
+* has_and_belongs_to_many now accepts an :order key to determine in which order the collection is returned [radsaq].
+
+* The ids passed to find and find_on_conditions are now automatically sanitized.
+
+* Added escaping of plings in YAML content.
+
+* Multi-parameter assigns where all the parameters are empty will now be set to nil instead of a new instance of their class.
+
+* Proper type within an inheritance hierarchy is now ensured already at object initialization (instead of first at create)
+
+
+## 0.9.3 ##
+
+* Fixed bug with using a different primary key name together with has_and_belongs_to_many *Investigation by Scott*
+
+* Added :exclusively_dependent option to the has_many association macro. The doc reads:
+
+ If set to true all the associated object are deleted in one SQL statement without having their
+ before_destroy callback run. This should only be used on associations that depend solely on
+ this class and don't need to do any clean-up in before_destroy. The upside is that it's much
+ faster, especially if there's a counter_cache involved.
+
+* Added :port key to connection options, so the PostgreSQL and MySQL adapters can connect to a database server
+ running on another port than the default.
+
+* Converted the new natural singleton methods that prevented AR objects from being saved by PStore
+ (and hence be placed in a Rails session) to a module. *Florian Weber*
+
+* Fixed the use of floats (was broken since 0.9.0+)
+
+* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with
+ Action Pack scaffolding.
+
+* Fixed booleans support for PostgreSQL (use real true/false on boolean fields instead of 0/1 on tinyints) *radsaq*
+
+
+## 0.9.2 ##
+
+* Added static method for instantly updating a record
+
+* Treat decimal and numeric as Ruby floats *Andreas Schwartz*
+
+* Treat chars as Ruby strings (fixes problem for Action Pack form helpers too)
+
+* Removed debugging output accidently left in (which would screw web applications)
+
+
+## 0.9.1 ##
+
+* Added MIT license
+
+* Added natural object-style assignment for has_and_belongs_to_many associations. Consider the following model:
+
+ class Event < ActiveRecord::Base
+ has_one_and_belongs_to_many :sponsors
+ end
+
+ class Sponsor < ActiveRecord::Base
+ has_one_and_belongs_to_many :sponsors
+ end
+
+ Earlier, you'd have to use synthetic methods for creating associations between two objects of the above class:
+
+ roskilde_festival.add_to_sponsors(carlsberg)
+ roskilde_festival.remove_from_sponsors(carlsberg)
+
+ nike.add_to_events(world_cup)
+ nike.remove_from_events(world_cup)
+
+ Now you can use regular array-styled methods:
+
+ roskilde_festival.sponsors << carlsberg
+ roskilde_festival.sponsors.delete(carlsberg)
+
+ nike.events << world_cup
+ nike.events.delete(world_cup)
+
+* Added delete method for has_many associations. Using this will nullify an association between the has_many and the belonging
+ object by setting the foreign key to null. Consider this model:
+
+ class Post < ActiveRecord::Base
+ has_many :comments
+ end
+
+ class Comment < ActiveRecord::Base
+ belongs_to :post
+ end
+
+ You could do something like:
+
+ funny_comment.has_post? # => true
+ announcement.comments.delete(funny_comment)
+ funny_comment.has_post? # => false
+
+
+## 0.9.0 ##
+
+* Active Record is now thread safe! (So you can use it with Cerise and WEBrick applications)
+ *Implementation idea by Michael Neumann, debugging assistance by Jamis Buck*
+
+* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute.
+ This brings the tax for using Active Record instead of "riding on the metal" (using MySQL-ruby C-driver directly) down to ~50%.
+ Done by doing lazy type conversions and caching column information on the class-level.
+
+* Added callback objects and procs as options for implementing the target for callback macros.
+
+* Added "counter_cache" option to belongs_to that automates the usage of increment_counter and decrement_counter. Consider:
+
+ class Post < ActiveRecord::Base
+ has_many :comments
+ end
+
+ class Comment < ActiveRecord::Base
+ belongs_to :post
+ end
+
+ Iterating over 100 posts like this:
+
+ <% for post in @posts %>
+ <%= post.title %> has <%= post.comments_count %> comments
+ <% end %>
+
+ Will generate 100 SQL count queries -- one for each call to post.comments_count. If you instead add a "comments_count" int column
+ to the posts table and rewrite the comments association macro with:
+
+ class Comment < ActiveRecord::Base
+ belongs_to :post, :counter_cache => true
+ end
+
+ Those 100 SQL count queries will be reduced to zero. Beware that counter caching is only appropriate for objects that begin life
+ with the object it's specified to belong with and is destroyed like that as well. Typically objects where you would also specify
+ :dependent => true. If your objects switch from one belonging to another (like a post that can be move from one category to another),
+ you'll have to manage the counter yourself.
+
+* Added natural object-style assignment for has_one and belongs_to associations. Consider the following model:
+
+ class Project < ActiveRecord::Base
+ has_one :manager
+ end
+
+ class Manager < ActiveRecord::Base
+ belongs_to :project
+ end
+
+ Earlier, assignments would work like following regardless of which way the assignment told the best story:
+
+ active_record.manager_id = david.id
+
+ Now you can do it either from the belonging side:
+
+ david.project = active_record
+
+ ...or from the having side:
+
+ active_record.manager = david
+
+ If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the
+ project_id attribute on david would be set to the id of active_record, then david would be saved.
+
+* Added natural object-style assignment for has_many associations [Florian Weber]. Consider the following model:
+
+ class Project < ActiveRecord::Base
+ has_many :milestones
+ end
+
+ class Milestone < ActiveRecord::Base
+ belongs_to :project
+ end
+
+ Earlier, assignments would work like following regardless of which way the assignment told the best story:
+
+ deadline.project_id = active_record.id
+
+ Now you can do it either from the belonging side:
+
+ deadline.project = active_record
+
+ ...or from the having side:
+
+ active_record.milestones << deadline
+
+ The milestone is automatically saved with the new foreign key.
+
+* API CHANGE: Attributes for text (or blob or similar) columns will now have unknown classes stored using YAML instead of using
+ to_s. (Known classes that won't be yamelized are: String, NilClass, TrueClass, FalseClass, Fixnum, Date, and Time).
+ Likewise, data pulled out of text-based attributes will be attempted converged using Yaml if they have the "--- " header.
+ This was primarily done to be enable the storage of hashes and arrays without wrapping them in aggregations, so now you can do:
+
+ user = User.find(1)
+ user.preferences = { "background" => "black", "display" => large }
+ user.save
+
+ User.find(1).preferences # => { "background" => "black", "display" => large }
+
+ Please note that this method should only be used when you don't care about representing the object in proper columns in
+ the database. A money object consisting of an amount and a currency is still a much better fit for a value object done through
+ aggregations than this new option.
+
+* POSSIBLE CODE BREAKAGE: As a consequence of the lazy type conversions, it's a bad idea to reference the @attributes hash
+ directly (it always was, but now it's paramount that you don't). If you do, you won't get the type conversion. So to implement
+ new accessors for existing attributes, use read_attribute(attr_name) and write_attribute(attr_name, value) instead. Like this:
+
+ class Song < ActiveRecord::Base
+ # Uses an integer of seconds to hold the length of the song
+
+ def length=(minutes)
+ write_attribute("length", minutes * 60)
+ end
+
+ def length
+ read_attribute("length") / 60
+ end
+ end
+
+ The clever kid will notice that this opens a door to sidestep the automated type conversion by using @attributes directly.
+ This is not recommended as read/write_attribute may be granted additional responsibilities in the future, but if you think
+ you know what you're doing and aren't afraid of future consequences, this is an option.
+
+* Applied a few minor bug fixes reported by Daniel Von Fange.
+
+
+## 0.8.4 ##
+
+ _Reflection_
+* Added ActiveRecord::Reflection with a bunch of methods and classes for reflecting in aggregations and associations.
+
+* Added Base.columns and Base.content_columns which returns arrays of column description (type, default, etc) objects.
+
+* Added Base#attribute_names which returns an array of names for the attributes available on the object.
+
+* Added Base#column_for_attribute(name) which returns the column description object for the named attribute.
+
+
+ _Misc_
+* Added multi-parameter assignment:
+
+ # Instantiate objects for all attribute classes that needs more than one constructor parameter. This is done
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
+ # parenteses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
+ # s for String, and a for Array.
+
+ This is incredibly useful for assigning dates from HTML drop-downs of month, year, and day.
+
+* Fixed bug with custom primary key column name and Base.find on multiple parameters.
+
+* Fixed bug with dependent option on has_one associations if there was no associated object.
+
+
+## 0.8.3 ##
+
+ _Transactions_
+* Added transactional protection for destroy (important for the new :dependent option) *Suggested by Carl Youngblood*
+
+* Fixed so transactions are ignored on MyISAM tables for MySQL (use InnoDB to get transactions)
+
+* Changed transactions so only exceptions will cause a rollback, not returned false.
+
+
+ _Mapping_
+* Added support for non-integer primary keys *Aredridel/earlier work by Michael Neumann*
+
+ User.find "jdoe"
+ Product.find "PDKEY-INT-12"
+
+* Added option to specify naming method for primary key column. ActiveRecord::Base.primary_key_prefix_type can either
+ be set to nil, :table_name, or :table_name_with_underscore. :table_name will assume that Product class has a primary key
+ of "productid" and :table_name_with_underscore will assume "product_id". The default nil will just give "id".
+
+* Added an overwriteable primary_key method that'll instruct AR to the name of the
+ id column *Aredridele/earlier work by Guan Yang*
+
+ class Project < ActiveRecord::Base
+ def self.primary_key() "project_id" end
+ end
+
+* Fixed that Active Records can safely associate inside and out of modules.
+
+ class MyApplication::Account < ActiveRecord::Base
+ has_many :clients # will look for MyApplication::Client
+ has_many :interests, :class_name => "Business::Interest" # will look for Business::Interest
+ end
+
+* Fixed that Active Records can safely live inside modules *Aredridel*
+
+ class MyApplication::Account < ActiveRecord::Base
+ end
+
+
+ _Misc_
+* Added freeze call to value object assignments to ensure they remain immutable *Spotted by Gavin Sinclair*
+
+* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is
+ observed_class() class method. This is more consistant with things like self.table_name(). Works like this:
+
+ class AuditObserver < ActiveRecord::Observer
+ def self.observed_class() Account end
+ def after_update(account)
+ AuditTrail.new(account, "UPDATED")
+ end
+ end
+
+ *Suggested by Gavin Sinclair*
+
+* Create new Active Record objects by setting the attributes through a block. Like this:
+
+ person = Person.new do |p|
+ p.name = 'Freddy'
+ p.age = 19
+ end
+
+ *Suggested by Gavin Sinclair*
+
+
+## 0.8.2 ##
+
+* Added inheritable callback queues that can ensure that certain callback methods or inline fragments are
+ run throughout the entire inheritance hierarchy. Regardless of whether a descendant overwrites the callback
+ method:
+
+ class Topic < ActiveRecord::Base
+ before_destroy :destroy_author, 'puts "I'm an inline fragment"'
+ end
+
+ Learn more in link:classes/ActiveRecord/Callbacks.html
+
+* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when
+ the holder is destroyed:
+
+ class Album < ActiveRecord::Base
+ has_many :tracks, :dependent => true
+ end
+
+ All the associated tracks are destroyed when the album is.
+
+* Added Base.create as a factory that'll create, save, and return a new object in one step.
+
+* Automatically convert strings in config hashes to symbols for the _connection methods. This allows you
+ to pass the argument hashes directly from yaml. (Luke)
+
+* Fixed the install.rb to include simple.rb *Spotted by Kevin Bullock*
+
+* Modified block syntax to better follow our code standards outlined in
+ http://www.rubyonrails.org/CodingStandards
+
+
+## 0.8.1 ##
+
+* Added object-level transactions *Austin Ziegler*
+
+* Changed adapter-specific connection methods to use centralized ActiveRecord::Base.establish_connection,
+ which is parametized through a config hash with symbol keys instead of a regular parameter list.
+ This will allow for database connections to be opened in a more generic fashion. (Luke)
+
+ NOTE: This requires all *_connections to be updated! Read more in:
+ http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000081
+
+* Fixed SQLite adapter so objects fetched from has_and_belongs_to_many have proper attributes
+ (t.name is now name). *Spotted by Garrett Rooney*
+
+* Fixed SQLite adapter so dates are returned as Date objects, not Time objects *Spotted by Gavin Sinclair*
+
+* Fixed requirement of date class, so date conversions are succesful regardless of whether you
+ manually require date or not.
+
+
+## 0.8.0 ##
+
+* Added transactions
+
+* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7])
+ as parameter and then return an array of objects instead of just an object
+
+* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a
+ collection, not an association
+
+* Fixed SQLite adapter so empty or nil values in columns of datetime, date, or time type
+ aren't treated as current time *Spotted by Gavin Sinclair*
+
+
+## 0.7.6 ##
+
+* Fixed the install.rb to create the lib/active_record/support directory *Spotted by Gavin Sinclair*
+* Fixed that has_association? would always return true *Daniel Von Fange*
diff --git a/activerecord/MIT-LICENSE b/activerecord/MIT-LICENSE
index c73d1af096..03bde18130 100644
--- a/activerecord/MIT-LICENSE
+++ b/activerecord/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2011 David Heinemeier Hansson
+Copyright (c) 2004-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc
index b5db57569c..30a66ff5f0 100644
--- a/activerecord/README.rdoc
+++ b/activerecord/README.rdoc
@@ -143,14 +143,14 @@ A short rundown of some of the major features:
* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
- ActiveRecord::Base.logger = Logger.new(STDOUT)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
* Database agnostic schema management with Migrations.
class AddSystemSettings < ActiveRecord::Migration
- def self.up
+ def up
create_table :system_settings do |t|
t.string :name
t.string :label
@@ -162,7 +162,7 @@ A short rundown of some of the major features:
SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
end
- def self.down
+ def down
drop_table :system_settings
end
end
@@ -208,7 +208,9 @@ Source code can be downloaded as part of the Rails project on GitHub
== License
-Active Record is released under the MIT license.
+Active Record is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index d769a73dba..4090293b56 100755
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -48,8 +48,8 @@ end
|x| x =~ /\/adapters\//
} + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb")).sort
- t.verbose = true
t.warning = true
+ t.verbose = true
}
task "isolated_test_#{adapter}" do
@@ -187,15 +187,15 @@ end
task :lines do
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
- for file_name in FileList["lib/active_record/**/*.rb"]
+ FileList["lib/active_record/**/*.rb"].each do |file_name|
next if file_name =~ /vendor/
- f = File.open(file_name)
-
- while line = f.gets
- lines += 1
- next if line =~ /^\s*$/
- next if line =~ /^\s*#/
- codelines += 1
+ File.open(file_name, 'r') do |f|
+ while line = f.gets
+ lines += 1
+ next if line =~ /^\s*$/
+ next if line =~ /^\s*#/
+ codelines += 1
+ end
end
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index 2de81c31a3..8f4c957dbd 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -7,13 +7,13 @@ Gem::Specification.new do |s|
s.summary = 'Object-relational mapper framework (part of Rails).'
s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']
+ s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']
s.require_path = 'lib'
s.extra_rdoc_files = %w( README.rdoc )
@@ -21,6 +21,5 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', version)
s.add_dependency('activemodel', version)
- s.add_dependency('arel', '~> 2.2.1')
- s.add_dependency('tzinfo', '~> 0.3.29')
+ s.add_dependency('arel', '~> 3.0.0')
end
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index 63822731d5..31f3e02bb8 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -1,6 +1,6 @@
TIMES = (ENV['N'] || 10000).to_i
-require 'rubygems'
+require File.expand_path('../../../load_paths', __FILE__)
require "active_record"
conn = { :adapter => 'sqlite3', :database => ':memory:' }
@@ -29,6 +29,14 @@ class Exhibit < ActiveRecord::Base
def look; attributes end
def feel; look; user.name end
+ def self.with_name
+ where("name IS NOT NULL")
+ end
+
+ def self.with_notes
+ where("notes IS NOT NULL")
+ end
+
def self.look(exhibits) exhibits.each { |e| e.look } end
def self.feel(exhibits) exhibits.each { |e| e.feel } end
end
@@ -37,7 +45,14 @@ puts 'Generating data...'
module ActiveRecord
class Faker
- LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit. Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem. Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim, tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae, varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero. Praesent varius tincidunt commodo".split
+ LOREM = %Q{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
+ Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem.
+ Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim,
+ tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae,
+ varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum
+ tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero.
+ Praesent varius tincidunt commodo}.split
+
def self.name
LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join ' '
end
@@ -102,6 +117,10 @@ Benchmark.bm(46) do |x|
TIMES.times { Exhibit.first.look }
end
+ x.report 'Model.named_scope' do
+ TIMES.times { Exhibit.limit(10).with_name.with_notes }
+ end
+
x.report("Model.all limit(100) (x#{(TIMES / 10).ceil})") do
(TIMES / 10).ceil.times { Exhibit.look Exhibit.limit(100) }
end
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 3572c640eb..73c8a06ab7 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2011 David Heinemeier Hansson
+# Copyright (c) 2004-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -39,9 +39,11 @@ module ActiveRecord
autoload :Aggregations
autoload :Associations
autoload :AttributeMethods
+ autoload :AttributeAssignment
autoload :AutosaveAssociation
autoload :Relation
+ autoload :NullRelation
autoload_under 'relation' do
autoload :QueryMethods
@@ -50,31 +52,45 @@ module ActiveRecord
autoload :PredicateBuilder
autoload :SpawnMethods
autoload :Batches
+ autoload :Explain
+ autoload :Delegation
end
autoload :Base
autoload :Callbacks
+ autoload :Core
autoload :CounterCache
+ autoload :ConnectionHandling
+ autoload :DynamicMatchers
autoload :DynamicFinderMatch
autoload :DynamicScopeMatch
+ autoload :Explain
+ autoload :IdentityMap
+ autoload :Inheritance
+ autoload :Integration
autoload :Migration
autoload :Migrator, 'active_record/migration'
- autoload :NamedScope
+ autoload :Model
+ autoload :ModelSchema
autoload :NestedAttributes
autoload :Observer
autoload :Persistence
autoload :QueryCache
+ autoload :Querying
+ autoload :ReadonlyAttributes
autoload :Reflection
autoload :Result
+ autoload :Sanitization
autoload :Schema
autoload :SchemaDumper
+ autoload :Scoping
autoload :Serialization
- autoload :Store
autoload :SessionStore
+ autoload :Store
autoload :Timestamp
autoload :Transactions
+ autoload :Translation
autoload :Validations
- autoload :IdentityMap
end
module Coders
@@ -92,6 +108,7 @@ module ActiveRecord
autoload :Read
autoload :TimeZoneConversion
autoload :Write
+ autoload :Serialization
end
end
@@ -113,6 +130,15 @@ module ActiveRecord
end
end
+ module Scoping
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Named
+ autoload :Default
+ end
+ end
+
autoload :TestCase
autoload :TestFixtures, 'active_record/fixtures'
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 34684ad2f5..958821add6 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
@@ -70,7 +69,7 @@ module ActiveRecord
end
end
- class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc
+ class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc:
def initialize(owner, reflection)
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
end
@@ -196,6 +195,26 @@ module ActiveRecord
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
# <tt>Project#categories.delete(category1)</tt>
#
+ # === Overriding generated methods
+ #
+ # Association methods are generated in a module that is included into the model class,
+ # which allows you to easily override with your own methods and call the original
+ # generated method with +super+. For example:
+ #
+ # class Car < ActiveRecord::Base
+ # belongs_to :owner
+ # belongs_to :old_owner
+ # def owner=(new_owner)
+ # self.old_owner = self.owner
+ # super
+ # end
+ # end
+ #
+ # If your model class is <tt>Project</tt>, the module is
+ # named <tt>Project::GeneratedFeatureMethods</tt>. The GeneratedFeatureMethods module is
+ # included in the model class immediately after the (anonymous) generated attributes methods
+ # module, meaning an association will override the methods for an attribute with the same name.
+ #
# === A word of warning
#
# Don't create associations that have the same name as instance methods of
@@ -1078,8 +1097,8 @@ module ActiveRecord
# alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
# objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
# objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
- # <tt>:restrict</tt> this object raises an <tt>ActiveRecord::DeleteRestrictionError</tt> exception and
- # cannot be deleted if it has any associated objects.
+ # <tt>:restrict</tt> an error will be added to the object, preventing its deletion, if any associated
+ # objects are present.
#
# If using with the <tt>:through</tt> option, the association on the join model must be
# a +belongs_to+, and the records which get deleted are the join records, rather than
@@ -1165,7 +1184,7 @@ module ActiveRecord
# has_many :subscribers, :through => :subscriptions, :source => :user
# has_many :subscribers, :class_name => "Person", :finder_sql => Proc.new {
# %Q{
- # SELECT DISTINCT people.*
+ # SELECT DISTINCT *
# FROM people p, post_subscriptions ps
# WHERE ps.post_id = #{id} AND ps.person_id = p.id
# ORDER BY p.first_name
@@ -1232,8 +1251,8 @@ module ActiveRecord
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
# If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
- # Also, association is assigned. If set to <tt>:restrict</tt> this object raises an
- # <tt>ActiveRecord::DeleteRestrictionError</tt> exception and cannot be deleted if it has any associated object.
+ # If set to <tt>:restrict</tt>, an error will be added to the object, preventing its deletion, if an
+ # associated object is present.
# [:foreign_key]
# Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 0248c7483c..84540a7000 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -5,12 +5,13 @@ module ActiveRecord
# Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
# ActiveRecord::Associations::ThroughAssociationScope
class AliasTracker # :nodoc:
- attr_reader :aliases, :table_joins
+ attr_reader :aliases, :table_joins, :connection
# table_joins is an array of arel joins which might conflict with the aliases we assign here
- def initialize(table_joins = [])
+ def initialize(connection = ActiveRecord::Model.connection, table_joins = [])
@aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
@table_joins = table_joins
+ @connection = connection
end
def aliased_table_for(table_name, aliased_name = nil)
@@ -70,10 +71,6 @@ module ActiveRecord
def truncate(name)
name.slice(0, connection.table_alias_length - 2)
end
-
- def connection
- ActiveRecord::Base.connection
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index d1e3ff8e38..7887d59aad 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -231,10 +231,7 @@ module ActiveRecord
def build_record(attributes, options)
reflection.build_association(attributes, options) do |record|
- record.assign_attributes(
- create_scope.except(*record.changed),
- :without_protection => true
- )
+ record.assign_attributes(create_scope.except(*record.changed), :without_protection => true)
end
end
end
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 6cc401e6cc..982084c9b8 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -10,41 +10,29 @@ module ActiveRecord
def initialize(association)
@association = association
- @alias_tracker = AliasTracker.new
+ @alias_tracker = AliasTracker.new klass.connection
end
def scope
scope = klass.unscoped
- scope = scope.extending(*Array.wrap(options[:extend]))
+ scope = scope.extending(*Array(options[:extend]))
# It's okay to just apply all these like this. The options will only be present if the
# association supports that option; this is enforced by the association builder.
scope = scope.apply_finder_options(options.slice(
- :readonly, :include, :order, :limit, :joins, :group, :having, :offset))
+ :readonly, :include, :references, :order, :limit, :joins, :group, :having, :offset, :select))
if options[:through] && !options[:include]
scope = scope.includes(source_options[:include])
end
- if select = select_value
- scope = scope.select(select)
- end
+ scope = scope.uniq if options[:uniq]
add_constraints(scope)
end
private
- def select_value
- select_value = options[:select]
-
- if reflection.collection?
- select_value ||= options[:uniq] && "DISTINCT #{reflection.quoted_table_name}.*"
- end
-
- select_value
- end
-
def add_constraints(scope)
tables = construct_tables
@@ -102,8 +90,11 @@ module ActiveRecord
scope = scope.joins(join(foreign_table, constraint))
- unless conditions.empty?
- scope = scope.where(sanitize(conditions, table))
+ conditions.each do |condition|
+ condition = interpolate(condition)
+ condition = { (table.table_alias || table.name) => condition } unless i == 0
+
+ scope = scope.where(condition)
end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 96fca97440..2059d8acdf 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -1,7 +1,7 @@
module ActiveRecord::Associations::Builder
class Association #:nodoc:
class_attribute :valid_options
- self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate]
+ self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate, :references]
# Set by subclasses
class_attribute :macro
@@ -16,6 +16,10 @@ module ActiveRecord::Associations::Builder
@model, @name, @options = model, name, options
end
+ def mixin
+ @model.generated_feature_methods
+ end
+
def build
validate_options
reflection = model.create_reflection(self.class.macro, name, options, model)
@@ -36,18 +40,47 @@ module ActiveRecord::Associations::Builder
def define_readers
name = self.name
-
- model.redefine_method(name) do |*params|
+ mixin.redefine_method(name) do |*params|
association(name).reader(*params)
end
end
def define_writers
name = self.name
-
- model.redefine_method("#{name}=") do |value|
+ mixin.redefine_method("#{name}=") do |value|
association(name).writer(value)
end
end
+
+ def dependent_restrict_raises?
+ ActiveRecord::Base.dependent_restrict_raises == true
+ end
+
+ def dependent_restrict_deprecation_warning
+ if dependent_restrict_raises?
+ msg = "In the next release, `:dependent => :restrict` will not raise a `DeleteRestrictionError`. "\
+ "Instead, it will add an error on the model. To fix this warning, make sure your code " \
+ "isn't relying on a `DeleteRestrictionError` and then add " \
+ "`config.active_record.dependent_restrict_raises = false` to your application config."
+ ActiveSupport::Deprecation.warn msg
+ end
+ end
+
+ def define_restrict_dependency_method
+ name = self.name
+ mixin.redefine_method(dependency_method_name) do
+ # has_many or has_one associations
+ if send(name).respond_to?(:exists?) ? send(name).exists? : !send(name).nil?
+ if dependent_restrict_raises?
+ raise ActiveRecord::DeleteRestrictionError.new(name)
+ else
+ key = association(name).reflection.macro == :has_one ? "one" : "many"
+ errors.add(:base, :"restrict_dependent_destroy.#{key}",
+ :record => self.class.human_attribute_name(name).downcase)
+ return false
+ end
+ end
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index f6d26840c2..4183c222de 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -25,16 +25,18 @@ module ActiveRecord::Associations::Builder
name = self.name
method_name = "belongs_to_counter_cache_after_create_for_#{name}"
- model.redefine_method(method_name) do
+ mixin.redefine_method(method_name) do
record = send(name)
record.class.increment_counter(cache_column, record.id) unless record.nil?
end
model.after_create(method_name)
method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
- model.redefine_method(method_name) do
- record = send(name)
- record.class.decrement_counter(cache_column, record.id) unless record.nil?
+ mixin.redefine_method(method_name) do
+ unless marked_for_destruction?
+ record = send(name)
+ record.class.decrement_counter(cache_column, record.id) unless record.nil?
+ end
end
model.before_destroy(method_name)
@@ -48,7 +50,7 @@ module ActiveRecord::Associations::Builder
method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
touch = options[:touch]
- model.redefine_method(method_name) do
+ mixin.redefine_method(method_name) do
record = send(name)
unless record.nil?
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index f62209a226..768f70b6c9 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -32,7 +32,7 @@ module ActiveRecord::Associations::Builder
private
def wrap_block_extension
- options[:extend] = Array.wrap(options[:extend])
+ options[:extend] = Array(options[:extend])
if block_extension
silence_warnings do
@@ -51,14 +51,14 @@ module ActiveRecord::Associations::Builder
# TODO : why do i need method_defined? I think its because of the inheritance chain
model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
- model.send("#{full_callback_name}=", Array.wrap(options[callback_name.to_sym]))
+ model.send("#{full_callback_name}=", Array(options[callback_name.to_sym]))
end
def define_readers
super
name = self.name
- model.redefine_method("#{name.to_s.singularize}_ids") do
+ mixin.redefine_method("#{name.to_s.singularize}_ids") do
association(name).ids_reader
end
end
@@ -67,7 +67,7 @@ module ActiveRecord::Associations::Builder
super
name = self.name
- model.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
+ mixin.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
association(name).ids_writer(ids)
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 30fc44b4c2..0b634ab944 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -18,7 +18,7 @@ module ActiveRecord::Associations::Builder
model.send(:include, Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def destroy_associations
- association(#{name.to_sym.inspect}).delete_all
+ association(#{name.to_sym.inspect}).delete_all_on_destroy
super
end
RUBY
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index ecbc70888f..9ddfd433e4 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -21,6 +21,7 @@ module ActiveRecord::Associations::Builder
":nullify or :restrict (#{options[:dependent].inspect})"
end
+ dependent_restrict_deprecation_warning if options[:dependent] == :restrict
send("define_#{options[:dependent]}_dependency_method")
model.before_destroy dependency_method_name
end
@@ -28,15 +29,10 @@ module ActiveRecord::Associations::Builder
def define_destroy_dependency_method
name = self.name
- model.send(:define_method, dependency_method_name) do
+ mixin.redefine_method(dependency_method_name) do
send(name).each do |o|
# No point in executing the counter update since we're going to destroy the parent anyway
- counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
- if o.respond_to?(counter_method)
- class << o
- self
- end.send(:define_method, counter_method, Proc.new {})
- end
+ o.mark_for_destruction
end
send(name).delete_all
@@ -45,16 +41,15 @@ module ActiveRecord::Associations::Builder
def define_delete_all_dependency_method
name = self.name
- model.send(:define_method, dependency_method_name) do
- send(name).delete_all
+ mixin.redefine_method(dependency_method_name) do
+ association(name).delete_all_on_destroy
end
end
- alias :define_nullify_dependency_method :define_delete_all_dependency_method
- def define_restrict_dependency_method
+ def define_nullify_dependency_method
name = self.name
- model.send(:define_method, dependency_method_name) do
- raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).empty?
+ mixin.redefine_method(dependency_method_name) do
+ send(name).delete_all
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index 88c0d3e90f..bc8a212bee 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -34,30 +34,23 @@ module ActiveRecord::Associations::Builder
":nullify or :restrict (#{options[:dependent].inspect})"
end
+ dependent_restrict_deprecation_warning if options[:dependent] == :restrict
send("define_#{options[:dependent]}_dependency_method")
model.before_destroy dependency_method_name
end
end
- def dependency_method_name
- "has_one_dependent_#{options[:dependent]}_for_#{name}"
- end
-
def define_destroy_dependency_method
- model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
- def #{dependency_method_name}
- association(#{name.to_sym.inspect}).delete
- end
- eoruby
+ name = self.name
+ mixin.redefine_method(dependency_method_name) do
+ association(name).delete
+ end
end
alias :define_delete_dependency_method :define_destroy_dependency_method
alias :define_nullify_dependency_method :define_destroy_dependency_method
- def define_restrict_dependency_method
- name = self.name
- model.redefine_method(dependency_method_name) do
- raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).nil?
- end
+ def dependency_method_name
+ "has_one_dependent_#{options[:dependent]}_for_#{name}"
end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 0cbbba041a..436b6c1524 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -16,15 +16,15 @@ module ActiveRecord::Associations::Builder
def define_constructors
name = self.name
- model.redefine_method("build_#{name}") do |*params, &block|
+ mixin.redefine_method("build_#{name}") do |*params, &block|
association(name).build(*params, &block)
end
- model.redefine_method("create_#{name}") do |*params, &block|
+ mixin.redefine_method("create_#{name}") do |*params, &block|
association(name).create(*params, &block)
end
- model.redefine_method("create_#{name}!") do |*params, &block|
+ mixin.redefine_method("create_#{name}!") do |*params, &block|
association(name).create!(*params, &block)
end
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index cec876149c..b2136605e1 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/array/wrap'
-
module ActiveRecord
module Associations
# = Active Record Association Collection
@@ -49,17 +47,25 @@ module ActiveRecord
end
else
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
+ relation = scoped
- scoped.select(column).map! do |record|
- record.send(reflection.association_primary_key)
+ including = (relation.eager_load_values + relation.includes_values).uniq
+
+ if including.any?
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
+ relation = join_dependency.join_associations.inject(relation) do |r, association|
+ association.join_relation(r)
+ end
end
+
+ relation.pluck(column)
end
end
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
def ids_writer(ids)
pk_column = reflection.primary_key_column
- ids = Array.wrap(ids).reject { |id| id.blank? }
+ ids = Array(ids).reject { |id| id.blank? }
ids.map! { |i| pk_column.type_cast(i) }
replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
end
@@ -152,6 +158,13 @@ module ActiveRecord
end
end
+ # Called when the association is declared as :dependent => :delete_all. This is
+ # an optimised version which avoids loading the records into memory. Not really
+ # for public consumption.
+ def delete_all_on_destroy
+ scoped.delete_all
+ end
+
# Destroy all the records from this association.
#
# See destroy for more info.
@@ -235,7 +248,7 @@ module ActiveRecord
# This method is abstract in the sense that it relies on
# +count_records+, which is a method descendants have to provide.
def size
- if owner.new_record? || (loaded? && !options[:uniq])
+ if !find_target? || (loaded? && !options[:uniq])
target.size
elsif !loaded? && options[:group]
load_target.size
@@ -344,8 +357,12 @@ module ActiveRecord
if options[:counter_sql]
interpolate(options[:counter_sql])
else
- # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
- interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
+ # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
+ interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
+ count_with = $2.to_s
+ count_with = '*' if count_with.blank? || count_with =~ /,/
+ "SELECT #{$1}COUNT(#{count_with}) FROM"
+ end
end
end
@@ -381,12 +398,7 @@ module ActiveRecord
return memory if persisted.empty?
persisted.map! do |record|
- # Unfortunately we cannot simply do memory.delete(record) since on 1.8 this returns
- # record rather than memory.at(memory.index(record)). The behavior is fixed in 1.9.
- mem_index = memory.index(record)
-
- if mem_index
- mem_record = memory.delete_at(mem_index)
+ if mem_record = memory.delete(record)
(record.attribute_names - mem_record.changes.keys).each do |name|
mem_record[name] = record[name]
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 3181ca9a32..5eda0387c4 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -39,10 +39,9 @@ module ActiveRecord
instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
- :lock, :readonly, :having, :to => :scoped
+ :lock, :readonly, :having, :pluck, :to => :scoped
- delegate :target, :load_target, :loaded?, :scoped,
- :to => :@association
+ delegate :target, :load_target, :loaded?, :to => :@association
delegate :select, :find, :first, :last,
:build, :create, :create!,
@@ -53,7 +52,7 @@ module ActiveRecord
def initialize(association)
@association = association
- Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) }
+ Array(association.options[:extend]).each { |ext| proxy_extend(ext) }
end
alias_method :new, :build
@@ -62,6 +61,13 @@ module ActiveRecord
@association
end
+ def scoped
+ association = @association
+ association.scoped.extending do
+ define_method(:proxy_association) { association }
+ end
+ end
+
def respond_to?(name, include_private = false)
super ||
(load_target && target.respond_to?(name, include_private)) ||
@@ -76,9 +82,8 @@ module ActiveRecord
proxy_association.send :add_to_target, r
yield(r) if block_given?
end
- end
- if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
+ elsif target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
if load_target
if target.respond_to?(method)
target.send(method, *args, &block)
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index 1f917f58f2..a4cea99372 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -32,6 +32,10 @@ module ActiveRecord
record
end
+ # ActiveRecord::Relation#delete_all needs to support joins before we can use a
+ # SQL-only implementation.
+ alias delete_all_on_destroy delete_all
+
private
def count_records
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 50ee60284c..059e6c77bc 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -99,6 +99,10 @@ module ActiveRecord
end
end
end
+
+ def foreign_key_present?
+ owner.attribute_present?(reflection.association_primary_key)
+ 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 2e818dca5d..9657cb081d 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -6,6 +6,13 @@ module ActiveRecord
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
include ThroughAssociation
+ def initialize(owner, reflection)
+ super
+
+ @through_records = {}
+ @through_association = nil
+ 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
@@ -42,27 +49,40 @@ module ActiveRecord
end
end
- through_record(record).save!
+ save_through_record(record)
update_counter(1)
record
end
- private
+ # ActiveRecord::Relation#delete_all needs to support joins before we can use a
+ # SQL-only implementation.
+ alias delete_all_on_destroy delete_all
- def through_record(record)
- through_association = owner.association(through_reflection.name)
- attributes = construct_join_attributes(record)
+ private
- through_record = Array.wrap(through_association.target).find { |candidate|
- candidate.attributes.slice(*attributes.keys) == attributes
- }
+ def through_association
+ @through_association ||= owner.association(through_reflection.name)
+ end
- unless through_record
- through_record = through_association.build(attributes)
+ # We temporarily cache through record that has been build, because if we build a
+ # through record in build_record and then subsequently call insert_record, then we
+ # want to use the exact same object.
+ #
+ # However, after insert_record has been called, we clear the cache entry because
+ # we want it to be possible to have multiple instances of the same record in an
+ # association
+ def build_through_record(record)
+ @through_records[record.object_id] ||= begin
+ through_record = through_association.build(construct_join_attributes(record))
through_record.send("#{source_reflection.name}=", record)
+ through_record
end
+ end
- through_record
+ def save_through_record(record)
+ build_through_record(record).save!
+ ensure
+ @through_records.delete(record.object_id)
end
def build_record(attributes, options = {})
@@ -73,9 +93,9 @@ module ActiveRecord
inverse = source_reflection.inverse_of
if inverse
if inverse.macro == :has_many
- record.send(inverse.name) << through_record(record)
+ record.send(inverse.name) << build_through_record(record)
elsif inverse.macro == :has_one
- record.send("#{inverse.name}=", through_record(record))
+ record.send("#{inverse.name}=", build_through_record(record))
end
end
@@ -104,8 +124,7 @@ module ActiveRecord
def delete_records(records, method)
ensure_not_nested
- through = owner.association(through_reflection.name)
- scope = through.scoped.where(construct_join_attributes(*records))
+ scope = through_association.scoped.where(construct_join_attributes(*records))
case method
when :destroy
@@ -116,7 +135,7 @@ module ActiveRecord
count = scope.delete_all
end
- delete_through_records(through, records)
+ delete_through_records(records)
if through_reflection.macro == :has_many && update_through_counter?(method)
update_counter(-count, through_reflection)
@@ -125,15 +144,25 @@ module ActiveRecord
update_counter(-count)
end
- def delete_through_records(through, records)
- if through_reflection.macro == :has_many
- records.each do |record|
- through.target.delete(through_record(record))
- end
- else
- records.each do |record|
- through.target = nil if through.target == through_record(record)
+ def through_records_for(record)
+ attributes = construct_join_attributes(record)
+ candidates = Array.wrap(through_association.target)
+ candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
+ end
+
+ def delete_through_records(records)
+ records.each do |record|
+ through_records = through_records_for(record)
+
+ if through_reflection.macro == :has_many
+ through_records.each { |r| through_association.target.delete(r) }
+ else
+ if through_records.include?(through_association.target)
+ through_association.target = nil
+ end
end
+
+ @through_records.delete(record.object_id)
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 6c878f0f00..cd366ac8b7 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -13,7 +13,7 @@ module ActiveRecord
@join_parts = [JoinBase.new(base)]
@associations = {}
@reflections = []
- @alias_tracker = AliasTracker.new(joins)
+ @alias_tracker = AliasTracker.new(base.connection, joins)
@alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
build(associations)
end
@@ -184,7 +184,7 @@ module ActiveRecord
macro = join_part.reflection.macro
if macro == :has_one
- return if record.association_cache.key?(join_part.reflection.name)
+ return record.association(join_part.reflection.name).target if record.association_cache.key?(join_part.reflection.name)
association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
set_target_and_inverse(join_part, association, record)
else
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index 03963ab060..0d7d28e458 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -95,8 +95,11 @@ module ActiveRecord
conditions = self.conditions[i].dup
conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
- unless conditions.empty?
- constraint = constraint.and(sanitize(conditions, table))
+ conditions.each do |condition|
+ condition = active_record.send(:sanitize_sql, interpolate(condition), table.table_alias || table.name)
+ condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
+
+ constraint = constraint.and(condition)
end
relation.from(join(table, constraint))
diff --git a/activerecord/lib/active_record/associations/join_helper.rb b/activerecord/lib/active_record/associations/join_helper.rb
index f83138195c..cea6ad6944 100644
--- a/activerecord/lib/active_record/associations/join_helper.rb
+++ b/activerecord/lib/active_record/associations/join_helper.rb
@@ -40,16 +40,6 @@ module ActiveRecord
def join(table, constraint)
table.create_join(table, table.create_on(constraint), join_type)
end
-
- def sanitize(conditions, table)
- conditions = conditions.map do |condition|
- condition = active_record.send(:sanitize_sql, interpolate(condition), table.table_alias || table.name)
- condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
- condition
- end
-
- conditions.length == 1 ? conditions.first : Arel::Nodes::And.new(conditions)
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 779f8164cc..253998fb23 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -95,8 +95,8 @@ module ActiveRecord
def build_scope
scope = klass.scoped
- scope = scope.where(process_conditions(options[:conditions]))
- scope = scope.where(process_conditions(preload_options[:conditions]))
+ scope = scope.where(interpolate(options[:conditions]))
+ scope = scope.where(interpolate(preload_options[:conditions]))
scope = scope.select(preload_options[:select] || options[:select] || table[Arel.star])
scope = scope.includes(preload_options[:include] || options[:include])
@@ -112,13 +112,11 @@ module ActiveRecord
scope
end
- def process_conditions(conditions)
+ def interpolate(conditions)
if conditions.respond_to?(:to_proc)
- conditions = klass.send(:instance_eval, &conditions)
- end
-
- if conditions
- klass.send(:sanitize_sql, conditions)
+ klass.send(:instance_eval, &conditions)
+ else
+ conditions
end
end
end
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index b347a94978..f95e5337c2 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -44,7 +44,7 @@ module ActiveRecord
join_attributes = {
source_reflection.foreign_key =>
records.map { |record|
- record.send(source_reflection.association_primary_key)
+ record.send(source_reflection.association_primary_key(reflection.klass))
}
}
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
new file mode 100644
index 0000000000..bf9fe70b31
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -0,0 +1,221 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module AttributeAssignment
+ extend ActiveSupport::Concern
+ include ActiveModel::MassAssignmentSecurity
+
+ module ClassMethods
+ private
+
+ # The primary key and inheritance column can never be set by mass-assignment for security reasons.
+ def attributes_protected_by_default
+ default = [ primary_key, inheritance_column ]
+ default << 'id' unless primary_key.eql? 'id'
+ default
+ end
+ end
+
+ # Allows you to set all the attributes at once by passing in a hash with keys
+ # matching the attribute names (which again matches the column names).
+ #
+ # If any attributes are protected by either +attr_protected+ or
+ # +attr_accessible+ then only settable attributes will be assigned.
+ #
+ # class User < ActiveRecord::Base
+ # attr_protected :is_admin
+ # end
+ #
+ # user = User.new
+ # user.attributes = { :username => 'Phusion', :is_admin => true }
+ # user.username # => "Phusion"
+ # user.is_admin? # => false
+ def attributes=(new_attributes)
+ return unless new_attributes.is_a?(Hash)
+
+ assign_attributes(new_attributes)
+ end
+
+ # Allows you to set all the attributes for a particular mass-assignment
+ # security role by passing in a hash of attributes with keys matching
+ # the attribute names (which again matches the column names) and the role
+ # name using the :as option.
+ #
+ # To bypass mass-assignment security you can use the :without_protection => true
+ # option.
+ #
+ # class User < ActiveRecord::Base
+ # attr_accessible :name
+ # attr_accessible :name, :is_admin, :as => :admin
+ # end
+ #
+ # user = User.new
+ # user.assign_attributes({ :name => 'Josh', :is_admin => true })
+ # user.name # => "Josh"
+ # user.is_admin? # => false
+ #
+ # user = User.new
+ # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
+ # user.name # => "Josh"
+ # user.is_admin? # => true
+ #
+ # user = User.new
+ # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
+ # user.name # => "Josh"
+ # user.is_admin? # => true
+ def assign_attributes(new_attributes, options = {})
+ return unless new_attributes
+
+ attributes = new_attributes.stringify_keys
+ multi_parameter_attributes = []
+ nested_parameter_attributes = []
+ @mass_assignment_options = options
+
+ unless options[:without_protection]
+ attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
+ end
+
+ attributes.each do |k, v|
+ if k.include?("(")
+ multi_parameter_attributes << [ k, v ]
+ elsif respond_to?("#{k}=")
+ if v.is_a?(Hash)
+ nested_parameter_attributes << [ k, v ]
+ else
+ send("#{k}=", v)
+ end
+ else
+ raise(UnknownAttributeError, "unknown attribute: #{k}")
+ end
+ end
+
+ # assign any deferred nested attributes after the base attributes have been set
+ nested_parameter_attributes.each do |k,v|
+ send("#{k}=", v)
+ end
+
+ @mass_assignment_options = nil
+ assign_multiparameter_attributes(multi_parameter_attributes)
+ end
+
+ protected
+
+ def mass_assignment_options
+ @mass_assignment_options ||= {}
+ end
+
+ def mass_assignment_role
+ mass_assignment_options[:as] || :default
+ end
+
+ private
+
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
+ # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
+ # attribute will be set to nil.
+ def assign_multiparameter_attributes(pairs)
+ execute_callstack_for_multiparameter_attributes(
+ extract_callstack_for_multiparameter_attributes(pairs)
+ )
+ end
+
+ def instantiate_time_object(name, values)
+ if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
+ Time.zone.local(*values)
+ else
+ Time.time_with_datetime_fallback(self.class.default_timezone, *values)
+ end
+ end
+
+ def execute_callstack_for_multiparameter_attributes(callstack)
+ errors = []
+ callstack.each do |name, values_with_empty_parameters|
+ begin
+ send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
+ rescue => ex
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
+ end
+ end
+ unless errors.empty?
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
+ end
+ end
+
+ def read_value_from_parameter(name, values_hash_from_param)
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
+ if values_hash_from_param.values.all?{|v|v.nil?}
+ nil
+ elsif klass == Time
+ read_time_parameter_value(name, values_hash_from_param)
+ elsif klass == Date
+ read_date_parameter_value(name, values_hash_from_param)
+ else
+ read_other_parameter_value(klass, name, values_hash_from_param)
+ end
+ end
+
+ def read_time_parameter_value(name, values_hash_from_param)
+ # If Date bits were not provided, error
+ raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
+ max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
+ # If Date bits were provided but blank, then return nil
+ return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
+
+ set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
+ # If Time bits are not there, then default to 0
+ (3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
+ instantiate_time_object(name, set_values)
+ end
+
+ def read_date_parameter_value(name, values_hash_from_param)
+ return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
+ set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
+ begin
+ Date.new(*set_values)
+ rescue ArgumentError # if Date.new raises an exception on an invalid date
+ instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
+ end
+ end
+
+ def read_other_parameter_value(klass, name, values_hash_from_param)
+ max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
+ values = (1..max_position).collect do |position|
+ raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
+ values_hash_from_param[position]
+ end
+ klass.new(*values)
+ end
+
+ def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
+ [values_hash_from_param.keys.max,upper_cap].min
+ end
+
+ def extract_callstack_for_multiparameter_attributes(pairs)
+ attributes = { }
+
+ pairs.each do |pair|
+ multiparameter_name, value = pair
+ attribute_name = multiparameter_name.split("(").first
+ attributes[attribute_name] = {} unless attributes.include?(attribute_name)
+
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
+ attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
+ end
+
+ attributes
+ end
+
+ def type_cast_attribute_value(multiparameter_name, value)
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
+ end
+
+ def find_parameter_position(multiparameter_name)
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
+ end
+
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index d7bfaa5655..3e27e85f02 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -7,35 +7,59 @@ module ActiveRecord
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
+ included do
+ include Read
+ include Write
+ include BeforeTypeCast
+ include Query
+ include PrimaryKey
+ include TimeZoneConversion
+ include Dirty
+ include Serialization
+
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+ # (Alias for the protected read_attribute method).
+ def [](attr_name)
+ read_attribute(attr_name)
+ end
+
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
+ # (Alias for the protected write_attribute method).
+ def []=(attr_name, value)
+ write_attribute(attr_name, value)
+ end
+ end
+
module ClassMethods
# Generates all the attribute related methods for columns in the database
# accessors, mutators and query methods.
def define_attribute_methods
- return if attribute_methods_generated?
-
- if base_class == self
+ # Use a mutex; we don't want two thread simaltaneously trying to define
+ # attribute methods.
+ @attribute_methods_mutex.synchronize do
+ return if attribute_methods_generated?
+ superclass.define_attribute_methods unless self == base_class
super(column_names)
@attribute_methods_generated = true
- else
- base_class.define_attribute_methods
end
end
def attribute_methods_generated?
- if base_class == self
- @attribute_methods_generated ||= false
- else
- base_class.attribute_methods_generated?
- end
+ @attribute_methods_generated ||= false
end
- def undefine_attribute_methods(*args)
- if base_class == self
- super
- @attribute_methods_generated = false
- else
- base_class.undefine_attribute_methods(*args)
- end
+ # We will define the methods as instance methods, but will call them as singleton
+ # methods. This allows us to use method_defined? to check if the method exists,
+ # which is fast and won't give any false positives from the ancestors (because
+ # there are no ancestors).
+ def generated_external_attribute_methods
+ @generated_external_attribute_methods ||= Module.new { extend self }
+ end
+
+ def undefine_attribute_methods
+ super if attribute_methods_generated?
+ @attribute_methods_generated = false
end
def instance_method_already_implemented?(method_name)
@@ -43,19 +67,46 @@ module ActiveRecord
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
end
- super
+ if [Base, Model].include?(active_record_super)
+ super
+ else
+ # If B < A and A defines its own attribute method, then we don't want to overwrite that.
+ defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
+ defined && !ActiveRecord::Base.method_defined?(method_name) || super
+ end
end
# A method name is 'dangerous' if it is already defined by Active Record, but
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
- def dangerous_attribute_method?(method_name)
- active_record = ActiveRecord::Base
- superclass = ActiveRecord::Base.superclass
+ def dangerous_attribute_method?(name)
+ method_defined_within?(name, Base)
+ end
+
+ def method_defined_within?(name, klass, sup = klass.superclass)
+ if klass.method_defined?(name) || klass.private_method_defined?(name)
+ if sup.method_defined?(name) || sup.private_method_defined?(name)
+ klass.instance_method(name).owner != sup.instance_method(name).owner
+ else
+ true
+ end
+ else
+ false
+ end
+ end
- (active_record.method_defined?(method_name) ||
- active_record.private_method_defined?(method_name)) &&
- !superclass.method_defined?(method_name) &&
- !superclass.private_method_defined?(method_name)
+ def attribute_method?(attribute)
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
+ end
+
+ # Returns an array of column names as strings if it's not
+ # an abstract class and table exists.
+ # Otherwise it returns an empty array.
+ def attribute_names
+ @attribute_names ||= if !abstract_class? && table_exists?
+ column_names
+ else
+ []
+ end
end
end
@@ -94,9 +145,106 @@ module ActiveRecord
super
end
+ # Returns true if the given attribute is in the attributes hash
+ def has_attribute?(attr_name)
+ @attributes.has_key?(attr_name.to_s)
+ end
+
+ # Returns an array of names for the attributes available on this object.
+ def attribute_names
+ @attributes.keys
+ end
+
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
+ def attributes
+ Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
+ end
+
+ # Returns an <tt>#inspect</tt>-like string for the value of the
+ # attribute +attr_name+. String attributes are truncated upto 50
+ # characters, and Date and Time attributes are returned in the
+ # <tt>:db</tt> format. Other attributes return the value of
+ # <tt>#inspect</tt> without modification.
+ #
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
+ #
+ # person.attribute_for_inspect(:name)
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
+ #
+ # person.attribute_for_inspect(:created_at)
+ # # => '"2009-01-12 04:48:57"'
+ def attribute_for_inspect(attr_name)
+ value = read_attribute(attr_name)
+
+ if value.is_a?(String) && value.length > 50
+ "#{value[0..50]}...".inspect
+ elsif value.is_a?(Date) || value.is_a?(Time)
+ %("#{value.to_s(:db)}")
+ else
+ value.inspect
+ end
+ end
+
+ # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
+ # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
+ def attribute_present?(attribute)
+ value = read_attribute(attribute)
+ !value.nil? || (value.respond_to?(:empty?) && !value.empty?)
+ end
+
+ # Returns the column object for the named attribute.
+ def column_for_attribute(name)
+ # FIXME: should this return a null object for columns that don't exist?
+ self.class.columns_hash[name.to_s]
+ end
+
protected
- def attribute_method?(attr_name)
- attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
+
+ def clone_attributes(reader_method = :read_attribute, attributes = {})
+ attribute_names.each do |name|
+ attributes[name] = clone_attribute_value(reader_method, name)
end
+ attributes
+ end
+
+ def clone_attribute_value(reader_method, attribute_name)
+ value = send(reader_method, attribute_name)
+ value.duplicable? ? value.clone : value
+ rescue TypeError, NoMethodError
+ value
+ end
+
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
+ # an Arel insert/update method.
+ def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
+ attrs = {}
+ klass = self.class
+ arel_table = klass.arel_table
+
+ attribute_names.each do |name|
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
+
+ if include_readonly_attributes || !self.class.readonly_attributes.include?(name)
+
+ value = if klass.serialized_attributes.include?(name)
+ @attributes[name].serialized_value
+ else
+ # FIXME: we need @attributes to be used consistently.
+ # If the values stored in @attributes were already type
+ # casted, this code could be simplified
+ read_attribute(name)
+ end
+
+ attrs[arel_table[name]] = value
+ end
+ end
+ end
+
+ attrs
+ end
+
+ def attribute_method?(attr_name)
+ defined?(@attributes) && @attributes.include?(attr_name)
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index bde11d0494..d4f529acbf 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -20,11 +20,7 @@ module ActiveRecord
# Handle *_before_type_cast for method_missing.
def attribute_before_type_cast(attribute_name)
- if attribute_name == 'id'
- read_attribute_before_type_cast(self.class.primary_key)
- else
- read_attribute_before_type_cast(attribute_name)
- end
+ read_attribute_before_type_cast(attribute_name)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 3eff3d54e3..433d508357 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -13,7 +13,7 @@ module ActiveRecord
raise "You cannot include Dirty after Timestamp"
end
- class_attribute :partial_updates
+ config_attribute :partial_updates
self.partial_updates = true
end
@@ -34,9 +34,9 @@ module ActiveRecord
@previously_changed = changes
@changed_attributes.clear
end
- rescue
- IdentityMap.remove(self) if IdentityMap.enabled?
- raise
+ rescue
+ IdentityMap.remove(self) if IdentityMap.enabled?
+ raise
end
# <tt>reload</tt> the record and clears changed attributes.
@@ -55,12 +55,12 @@ module ActiveRecord
# The attribute already has an unsaved change.
if attribute_changed?(attr)
old = @changed_attributes[attr]
- @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
+ @changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
else
old = clone_attribute_value(:read_attribute, attr)
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
- @changed_attributes[attr] = old if field_changed?(attr, old, value)
+ @changed_attributes[attr] = old if _field_changed?(attr, old, value)
end
# Carry on.
@@ -77,7 +77,7 @@ module ActiveRecord
end
end
- def field_changed?(attr, old, value)
+ def _field_changed?(attr, old, value)
if column = column_for_attribute(attr)
if column.number? && column.null && (old.nil? || old == 0) && value.blank?
# For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index a404a5edd7..8e0b3e1402 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -1,3 +1,5 @@
+require 'set'
+
module ActiveRecord
module AttributeMethods
module PrimaryKey
@@ -5,15 +7,63 @@ module ActiveRecord
# Returns this record's primary key value wrapped in an Array if one is available
def to_key
- key = send(self.class.primary_key)
+ key = self.id
[key] if key
end
+ # Returns the primary key value
+ def id
+ read_attribute(self.class.primary_key)
+ end
+
+ # Sets the primary key value
+ def id=(value)
+ write_attribute(self.class.primary_key, value)
+ end
+
+ # Queries the primary key value
+ def id?
+ query_attribute(self.class.primary_key)
+ end
+
+ # Returns the primary key value before type cast
+ def id_before_type_cast
+ read_attribute_before_type_cast(self.class.primary_key)
+ end
+
+ protected
+
+ def attribute_method?(attr_name)
+ attr_name == 'id' || super
+ end
+
module ClassMethods
+ def define_method_attribute(attr_name)
+ super
+
+ if attr_name == primary_key && attr_name != 'id'
+ generated_attribute_methods.send(:alias_method, :id, primary_key)
+ generated_external_attribute_methods.module_eval <<-CODE, __FILE__, __LINE__
+ def id(v, attributes, attributes_cache, attr_name)
+ attr_name = '#{primary_key}'
+ send(attr_name, attributes[attr_name], attributes, attributes_cache, attr_name)
+ end
+ CODE
+ end
+ end
+
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast).to_set
+
+ def dangerous_attribute_method?(method_name)
+ super && !ID_ATTRIBUTE_METHODS.include?(method_name)
+ end
+
# Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
- # primary_key_prefix_type setting, though.
+ # primary_key_prefix_type setting, though. Since primary keys are usually protected from mass assignment,
+ # remember to let your database generate them or include the key in +attr_accessible+.
def primary_key
- @primary_key ||= reset_primary_key
+ @primary_key = reset_primary_key unless defined? @primary_key
+ @primary_key
end
# Returns a quoted version of the primary key name, used to construct SQL statements.
@@ -22,11 +72,11 @@ module ActiveRecord
end
def reset_primary_key #:nodoc:
- key = self == base_class ? get_primary_key(base_class.name) :
- base_class.primary_key
-
- set_primary_key(key)
- key
+ if self == base_class
+ self.primary_key = get_primary_key(base_class.name)
+ else
+ self.primary_key = base_class.primary_key
+ end
end
def get_primary_key(base_name) #:nodoc:
@@ -38,35 +88,32 @@ module ActiveRecord
when :table_name_with_underscore
base_name.foreign_key
else
- if ActiveRecord::Base != self && connection.table_exists?(table_name)
- connection.primary_key(table_name)
+ if ActiveRecord::Base != self && table_exists?
+ connection.schema_cache.primary_keys[table_name]
else
'id'
end
end
end
- attr_accessor :original_primary_key
-
- # Attribute writer for the primary key column
- def primary_key=(value)
- @quoted_primary_key = nil
- @primary_key = value
- end
-
- # Sets the name of the primary key column to use to the given value,
- # or (if the value is nil or false) to the value returned by the given
- # block.
+ # Sets the name of the primary key column.
#
# class Project < ActiveRecord::Base
- # set_primary_key "sysid"
+ # self.primary_key = "sysid"
# end
- def set_primary_key(value = nil, &block)
- @quoted_primary_key = nil
- @primary_key ||= ''
- self.original_primary_key = @primary_key
- value &&= value.to_s
- self.primary_key = block_given? ? instance_eval(&block) : value
+ #
+ # You can also define the primary_key method yourself:
+ #
+ # class Project < ActiveRecord::Base
+ # def self.primary_key
+ # "foo_" + super
+ # end
+ # end
+ # Project.primary_key # => "foo_id"
+ def primary_key=(value)
+ @original_primary_key = @primary_key if defined?(@primary_key)
+ @primary_key = value && value.to_s
+ @quoted_primary_key = nil
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 948809c65a..1e841dc8e0 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -10,8 +10,11 @@ module ActiveRecord
end
def query_attribute(attr_name)
- unless value = read_attribute(attr_name)
- false
+ value = read_attribute(attr_name)
+
+ case value
+ when true then true
+ when false, nil then false
else
column = self.class.columns_hash[attr_name]
if column.nil?
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 4174e4da09..846ac03d82 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -6,11 +6,8 @@ module ActiveRecord
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
included do
- cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+ config_attribute :attribute_types_cached_by_default, :global => true
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
-
- # Undefine id so it can be used as an attribute name
- undef_method(:id) if method_defined?(:id)
end
module ClassMethods
@@ -32,113 +29,117 @@ module ActiveRecord
cached_attributes.include?(attr_name)
end
+ def undefine_attribute_methods
+ generated_external_attribute_methods.module_eval do
+ instance_methods.each { |m| undef_method(m) }
+ end
+
+ super
+ end
+
+ def type_cast_attribute(attr_name, attributes, cache = {}) #:nodoc:
+ return unless attr_name
+ attr_name = attr_name.to_s
+
+ if generated_external_attribute_methods.method_defined?(attr_name)
+ if attributes.has_key?(attr_name) || attr_name == 'id'
+ generated_external_attribute_methods.send(attr_name, attributes[attr_name], attributes, cache, attr_name)
+ end
+ elsif !attribute_methods_generated?
+ # If we haven't generated the caster methods yet, do that and
+ # then try again
+ define_attribute_methods
+ type_cast_attribute(attr_name, attributes, cache)
+ else
+ # If we get here, the attribute has no associated DB column, so
+ # just return it verbatim.
+ attributes[attr_name]
+ end
+ end
+
protected
- def define_method_attribute(attr_name)
- if serialized_attributes.include?(attr_name)
- define_read_method_for_serialized_attribute(attr_name)
- else
- define_read_method(attr_name, attr_name, columns_hash[attr_name])
+ # We want to generate the methods via module_eval rather than define_method,
+ # because define_method is slower on dispatch and uses more memory (because it
+ # creates a closure).
+ #
+ # But sometimes the database might return columns with characters that are not
+ # allowed in normal method names (like 'my_column(omg)'. So to work around this
+ # we first define with the __temp__ identifier, and then use alias method to
+ # rename it to what we want.
+ def define_method_attribute(attr_name)
+ cast_code = attribute_cast_code(attr_name)
+
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def __temp__
+ #{internal_attribute_access_code(attr_name, cast_code)}
end
+ alias_method '#{attr_name}', :__temp__
+ undef_method :__temp__
+ STR
- if attr_name == primary_key && attr_name != "id"
- define_read_method('id', attr_name, columns_hash[attr_name])
+ generated_external_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def __temp__(v, attributes, attributes_cache, attr_name)
+ #{external_attribute_access_code(attr_name, cast_code)}
end
- end
+ alias_method '#{attr_name}', :__temp__
+ undef_method :__temp__
+ STR
+ end
private
- def cacheable_column?(column)
- serialized_attributes.include?(column.name) || attribute_types_cached_by_default.include?(column.type)
+ def cacheable_column?(column)
+ if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+ ! serialized_attributes.include? column.name
+ else
+ attribute_types_cached_by_default.include?(column.type)
end
+ end
- # Define read method for serialized attribute.
- def define_read_method_for_serialized_attribute(attr_name)
- access_code = "@attributes_cache['#{attr_name}'] ||= @attributes['#{attr_name}']"
- generated_attribute_methods.module_eval("def _#{attr_name}; #{access_code}; end; alias #{attr_name} _#{attr_name}", __FILE__, __LINE__)
- end
+ def internal_attribute_access_code(attr_name, cast_code)
+ "read_attribute('#{attr_name}') { |n| missing_attribute(n, caller) }"
+ end
- # Define an attribute reader method. Cope with nil column.
- # method_name is the same as attr_name except when a non-standard primary key is used,
- # we still define #id as an accessor for the key
- def define_read_method(method_name, attr_name, column)
- cast_code = column.type_cast_code('v')
- access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}"
+ def external_attribute_access_code(attr_name, cast_code)
+ access_code = "v && #{cast_code}"
- unless attr_name.to_s == self.primary_key.to_s
- access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
- end
+ if cache_attribute?(attr_name)
+ access_code = "attributes_cache[attr_name] ||= (#{access_code})"
+ end
- if cache_attribute?(attr_name)
- access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
- end
+ access_code
+ end
- # Where possible, generate the method by evalling a string, as this will result in
- # faster accesses because it avoids the block eval and then string eval incurred
- # by the second branch.
- #
- # The second, slower, branch is necessary to support instances where the database
- # returns columns with extra stuff in (like 'my_column(omg)').
- if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
- def _#{method_name}
- #{access_code}
- end
-
- alias #{method_name} _#{method_name}
- STR
- else
- generated_attribute_methods.module_eval do
- define_method("_#{method_name}") { eval(access_code) }
- alias_method(method_name, "_#{method_name}")
- end
- end
- end
+ def attribute_cast_code(attr_name)
+ columns_hash[attr_name].type_cast_code('v')
+ end
end
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
- method = "_#{attr_name}"
- if respond_to? method
- send method if @attributes.has_key?(attr_name.to_s)
- else
- _read_attribute attr_name
- end
- end
-
- def _read_attribute(attr_name)
- attr_name = attr_name.to_s
- attr_name = self.class.primary_key if attr_name == 'id'
- value = @attributes[attr_name]
- unless value.nil?
- if column = column_for_attribute(attr_name)
- if unserializable_attribute?(attr_name, column)
- unserialize_attribute(attr_name)
- else
- column.type_cast(value)
- end
+ # If it's cached, just return it
+ @attributes_cache.fetch(attr_name) { |name|
+ column = @columns_hash.fetch(name) {
+ return self.class.type_cast_attribute(name, @attributes, @attributes_cache)
+ }
+
+ value = @attributes.fetch(name) {
+ return block_given? ? yield(name) : nil
+ }
+
+ if self.class.cache_attribute?(name)
+ @attributes_cache[name] = column.type_cast(value)
else
- value
+ column.type_cast value
end
- end
- end
-
- # Returns true if the attribute is of a text column and marked for serialization.
- def unserializable_attribute?(attr_name, column)
- column.text? && self.class.serialized_attributes.include?(attr_name)
+ }
end
- # Returns the unserialized object of the attribute.
- def unserialize_attribute(attr_name)
- coder = self.class.serialized_attributes[attr_name]
- unserialized_object = coder.load(@attributes[attr_name])
+ private
- @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
+ def attribute(attribute_name)
+ read_attribute(attribute_name)
end
-
- private
- def attribute(attribute_name)
- read_attribute(attribute_name)
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
new file mode 100644
index 0000000000..165785c8fb
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -0,0 +1,115 @@
+module ActiveRecord
+ module AttributeMethods
+ module Serialization
+ extend ActiveSupport::Concern
+
+ included do
+ # Returns a hash of all the attributes that have been specified for serialization as
+ # keys and their class restriction as values.
+ config_attribute :serialized_attributes
+ self.serialized_attributes = {}
+ end
+
+ class Type # :nodoc:
+ def initialize(column)
+ @column = column
+ end
+
+ def type_cast(value)
+ value.unserialized_value
+ end
+
+ def type
+ @column.type
+ end
+ end
+
+ class Attribute < Struct.new(:coder, :value, :state)
+ def unserialized_value
+ state == :serialized ? unserialize : value
+ end
+
+ def serialized_value
+ state == :unserialized ? serialize : value
+ end
+
+ def unserialize
+ self.state = :unserialized
+ self.value = coder.load(value)
+ end
+
+ def serialize
+ self.state = :serialized
+ self.value = coder.dump(value)
+ end
+ end
+
+ module ClassMethods
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
+ # then specify the name of that attribute using this method and it will be handled automatically.
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
+ # class on retrieval or SerializationTypeMismatch will be raised.
+ #
+ # ==== Parameters
+ #
+ # * +attr_name+ - The field name that should be serialized.
+ # * +class_name+ - Optional, class name that the object type should be equal to.
+ #
+ # ==== Example
+ # # Serialize a preferences attribute
+ # class User < ActiveRecord::Base
+ # serialize :preferences
+ # end
+ def serialize(attr_name, class_name = Object)
+ coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
+ class_name
+ else
+ Coders::YAMLColumn.new(class_name)
+ end
+
+ # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
+ # has its own hash of own serialized attributes
+ self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
+ end
+
+ def initialize_attributes(attributes) #:nodoc:
+ super
+
+ serialized_attributes.each do |key, coder|
+ if attributes.key?(key)
+ attributes[key] = Attribute.new(coder, attributes[key], :serialized)
+ end
+ end
+
+ attributes
+ end
+
+ private
+
+ def attribute_cast_code(attr_name)
+ if serialized_attributes.include?(attr_name)
+ "v.unserialized_value"
+ else
+ super
+ end
+ end
+ end
+
+ def type_cast_attribute_for_write(column, value)
+ if column && coder = self.class.serialized_attributes[column.name]
+ Attribute.new(coder, value, :unserialized)
+ else
+ super
+ end
+ end
+
+ def read_attribute_before_type_cast(attr_name)
+ if serialized_attributes.include?(attr_name)
+ super.unserialized_value
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 62a3cfa9a5..20372c5c18 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -4,63 +4,75 @@ require 'active_support/core_ext/object/inclusion'
module ActiveRecord
module AttributeMethods
module TimeZoneConversion
+ class Type # :nodoc:
+ def initialize(column)
+ @column = column
+ end
+
+ def type_cast(value)
+ value = @column.type_cast(value)
+ value.acts_like?(:time) ? value.in_time_zone : value
+ end
+
+ def type
+ @column.type
+ end
+ end
+
extend ActiveSupport::Concern
included do
- cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+ config_attribute :time_zone_aware_attributes, :global => true
self.time_zone_aware_attributes = false
- class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ config_attribute :skip_time_zone_conversion_for_attributes
self.skip_time_zone_conversion_for_attributes = []
end
module ClassMethods
protected
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced read method automatically converts the UTC time stored in the database to the time
- # zone stored in Time.zone.
- def define_method_attribute(attr_name)
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body, line = <<-EOV, __LINE__ + 1
- def _#{attr_name}
- cached = @attributes_cache['#{attr_name}']
- return cached if cached
- time = _read_attribute('#{attr_name}')
- @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
- end
- alias #{attr_name} _#{attr_name}
- EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, line)
- else
- super
- end
+ # The enhanced read method automatically converts the UTC time stored in the database to the time
+ # zone stored in Time.zone.
+ def attribute_cast_code(attr_name)
+ column = columns_hash[attr_name]
+
+ if create_time_zone_conversion_attribute?(attr_name, column)
+ typecast = "v = #{super}"
+ time_zone_conversion = "v.acts_like?(:time) ? v.in_time_zone : v"
+
+ "((#{typecast}) && (#{time_zone_conversion}))"
+ else
+ super
end
+ end
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
- def define_method_attribute=(attr_name)
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body, line = <<-EOV, __LINE__ + 1
- def #{attr_name}=(original_time)
- time = original_time
- unless time.acts_like?(:time)
- time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
- end
- time = time.in_time_zone rescue nil if time
- write_attribute(:#{attr_name}, original_time)
- @attributes_cache["#{attr_name}"] = time
+ # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
+ # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
+ def define_method_attribute=(attr_name)
+ if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
+ method_body, line = <<-EOV, __LINE__ + 1
+ def #{attr_name}=(original_time)
+ time = original_time
+ unless time.acts_like?(:time)
+ time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
end
- EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, line)
- else
- super
- end
+ time = time.in_time_zone rescue nil if time
+ write_attribute(:#{attr_name}, original_time)
+ @attributes_cache["#{attr_name}"] = time
+ end
+ EOV
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
+ else
+ super
end
+ end
private
- def create_time_zone_conversion_attribute?(name, column)
- time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
- end
+ def create_time_zone_conversion_attribute?(name, column)
+ time_zone_aware_attributes &&
+ !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
+ [:datetime, :timestamp].include?(column.type)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index e9cdb130db..50435921b1 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -10,17 +10,13 @@ module ActiveRecord
module ClassMethods
protected
def define_method_attribute=(attr_name)
- if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
+ if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
else
generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
write_attribute(attr_name, new_value)
end
end
-
- if attr_name == primary_key && attr_name != "id"
- generated_attribute_methods.module_eval("alias :id= :'#{primary_key}='")
- end
end
end
@@ -32,10 +28,14 @@ module ActiveRecord
@attributes_cache.delete(attr_name)
column = column_for_attribute(attr_name)
- if column && column.number?
- @attributes[attr_name] = convert_number_column_value(value)
- elsif column || @attributes.has_key?(attr_name)
- @attributes[attr_name] = value
+ # If we're dealing with a binary column, write the data to the cache
+ # so we don't attempt to typecast multiple times.
+ if column && column.binary?
+ @attributes_cache[attr_name] = value
+ end
+
+ if column || @attributes.has_key?(attr_name)
+ @attributes[attr_name] = type_cast_attribute_for_write(column, value)
else
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
end
@@ -43,10 +43,16 @@ module ActiveRecord
alias_method :raw_write_attribute, :write_attribute
private
- # Handle *= for method_missing.
- def attribute=(attribute_name, value)
- write_attribute(attribute_name, value)
- end
+ # Handle *= for method_missing.
+ def attribute=(attribute_name, value)
+ write_attribute(attribute_name, value)
+ end
+
+ def type_cast_attribute_for_write(column, value)
+ return value unless column
+
+ column.type_cast_for_write value
+ end
end
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 056170d82a..d468663084 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/array/wrap'
-
module ActiveRecord
# = Active Record Autosave Association
#
@@ -21,6 +19,21 @@ module ActiveRecord
# Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>.
# When the <tt>:autosave</tt> option is not present new associations are saved.
#
+ # == Validation
+ #
+ # Children records are validated unless <tt>:validate</tt> is +false+.
+ #
+ # == Callbacks
+ #
+ # Association with autosave option defines several callbacks on your
+ # model (before_save, after_create, after_update). Please note that
+ # callbacks are executed in the order they were defined in
+ # model. You should avoid modyfing the association content, before
+ # autosave callbacks are executed. Placing your callbacks after
+ # associations is usually a good practice.
+ #
+ # == Examples
+ #
# === One-to-one Example
#
# class Post
@@ -109,10 +122,7 @@ module ActiveRecord
# Now it _is_ removed from the database:
#
# Comment.find_by_id(id).nil? # => true
- #
- # === Validation
- #
- # Children records are validated unless <tt>:validate</tt> is +false+.
+
module AutosaveAssociation
extend ActiveSupport::Concern
@@ -264,7 +274,7 @@ module ActiveRecord
# turned on for the association.
def validate_single_association(reflection)
association = association_instance_get(reflection.name)
- record = association && association.target
+ record = association && association.reader
association_valid?(reflection, record) if record
end
@@ -285,7 +295,7 @@ module ActiveRecord
def association_valid?(reflection, record)
return true if record.destroyed? || record.marked_for_destruction?
- unless valid = record.valid?
+ unless valid = record.valid?(validation_context)
if reflection.options[:autosave]
record.errors.each do |attribute, message|
attribute = "#{reflection.name}.#{attribute}"
@@ -331,7 +341,7 @@ module ActiveRecord
if autosave
saved = association.insert_record(record, false)
else
- association.insert_record(record)
+ association.insert_record(record) unless reflection.nested?
end
elsif autosave
saved = record.save(:validate => false)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 360e494af1..d4d0220fb7 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1,8 +1,3 @@
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
require 'set'
require 'active_support/benchmarkable'
@@ -12,7 +7,6 @@ require 'active_support/time'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/class/delegating_attributes'
-require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/hash/indifferent_access'
@@ -23,9 +17,11 @@ require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/introspection'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/blank'
+require 'active_support/deprecation'
require 'arel'
require 'active_record/errors'
require 'active_record/log_subscriber'
+require 'active_record/explain_subscriber'
module ActiveRecord #:nodoc:
# = Active Record
@@ -325,1842 +321,8 @@ module ActiveRecord #:nodoc:
# So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
# instances in the current object space.
class Base
- ##
- # :singleton-method:
- # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
- # which is then passed on to any new database connections made and which can be retrieved on both
- # a class and instance level by calling +logger+.
- cattr_accessor :logger, :instance_writer => false
-
- ##
- # :singleton-method:
- # Contains the database configuration - as is typically stored in config/database.yml -
- # as a Hash.
- #
- # For example, the following database.yml...
- #
- # development:
- # adapter: sqlite3
- # database: db/development.sqlite3
- #
- # production:
- # adapter: sqlite3
- # database: db/production.sqlite3
- #
- # ...would result in ActiveRecord::Base.configurations to look like this:
- #
- # {
- # 'development' => {
- # 'adapter' => 'sqlite3',
- # 'database' => 'db/development.sqlite3'
- # },
- # 'production' => {
- # 'adapter' => 'sqlite3',
- # 'database' => 'db/production.sqlite3'
- # }
- # }
- cattr_accessor :configurations, :instance_writer => false
- @@configurations = {}
-
- ##
- # :singleton-method:
- # Accessor for the prefix type that will be prepended to every primary key column name.
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
- # the Product class will look for "productid" instead of "id" as the primary column. If the
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
- # that this is a global setting for all Active Records.
- cattr_accessor :primary_key_prefix_type, :instance_writer => false
- @@primary_key_prefix_type = nil
-
- ##
- # :singleton-method:
- # Accessor for the name of the prefix string to prepend to every table name. So if set
- # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
- # etc. This is a convenient way of creating a namespace for tables in a shared database.
- # By default, the prefix is the empty string.
- #
- # If you are organising your models within modules you can add a prefix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_prefix which
- # returns your chosen prefix.
- class_attribute :table_name_prefix, :instance_writer => false
- self.table_name_prefix = ""
-
- ##
- # :singleton-method:
- # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
- # "people_basecamp"). By default, the suffix is the empty string.
- class_attribute :table_name_suffix, :instance_writer => false
- self.table_name_suffix = ""
-
- ##
- # :singleton-method:
- # Indicates whether table names should be the pluralized versions of the corresponding class names.
- # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
- # See table_name for the full rules on table/class naming. This is true, by default.
- class_attribute :pluralize_table_names, :instance_writer => false
- self.pluralize_table_names = true
-
- ##
- # :singleton-method:
- # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling
- # dates and times from the database. This is set to :local by default.
- cattr_accessor :default_timezone, :instance_writer => false
- @@default_timezone = :local
-
- ##
- # :singleton-method:
- # Specifies the format to use when dumping the database schema with Rails'
- # Rakefile. If :sql, the schema is dumped as (potentially database-
- # specific) SQL statements. If :ruby, the schema is dumped as an
- # ActiveRecord::Schema file which can be loaded into any database that
- # supports migrations. Use :ruby if you want to have different database
- # adapters for, e.g., your development and test environments.
- cattr_accessor :schema_format , :instance_writer => false
- @@schema_format = :ruby
-
- ##
- # :singleton-method:
- # Specify whether or not to use timestamps for migration versions
- cattr_accessor :timestamped_migrations , :instance_writer => false
- @@timestamped_migrations = true
-
- # Determine whether to store the full constant name including namespace when using STI
- class_attribute :store_full_sti_class
- self.store_full_sti_class = true
-
- # Stores the default scope for the class
- class_attribute :default_scopes, :instance_writer => false
- self.default_scopes = []
-
- # Returns a hash of all the attributes that have been specified for serialization as
- # keys and their class restriction as values.
- class_attribute :serialized_attributes
- self.serialized_attributes = {}
-
- class_attribute :_attr_readonly, :instance_writer => false
- self._attr_readonly = []
-
- class << self # Class methods
- delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
- delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped
- delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
- delegate :find_each, :find_in_batches, :to => :scoped
- delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
- delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
-
- # Executes a custom SQL query against your database and returns all the results. The results will
- # be returned as an array with columns requested encapsulated as attributes of the model you call
- # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
- # a Product object with the attributes you specified in the SQL query.
- #
- # If you call a complicated SQL query which spans multiple tables the columns specified by the
- # SELECT will be attributes of the model, whether or not they are columns of the corresponding
- # table.
- #
- # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
- # no database agnostic conversions performed. This should be a last resort because using, for example,
- # MySQL specific terms will lock you to using that particular database engine or require you to
- # change your call if you switch engines.
- #
- # ==== Examples
- # # A simple SQL query spanning multiple tables
- # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
- # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
- #
- # # You can use the same string replacement techniques as you can with ActiveRecord#find
- # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
- # > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
- def find_by_sql(sql, binds = [])
- connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
- end
-
- # Creates an object (or multiple objects) and saves it to the database, if validations pass.
- # The resulting object is returned whether the object was saved successfully to the database or not.
- #
- # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
- # attributes on the objects that are to be created.
- #
- # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
- # in the +options+ parameter.
- #
- # ==== Examples
- # # Create a single new object
- # User.create(:first_name => 'Jamie')
- #
- # # Create a single new object using the :admin mass-assignment security role
- # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
- #
- # # Create a single new object bypassing mass-assignment security
- # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
- #
- # # Create an Array of new objects
- # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
- #
- # # Create a single object and pass it into a block to set other attributes.
- # User.create(:first_name => 'Jamie') do |u|
- # u.is_admin = false
- # end
- #
- # # Creating an Array of new objects using a block, where the block is executed for each object:
- # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
- # u.is_admin = false
- # end
- def create(attributes = nil, options = {}, &block)
- if attributes.is_a?(Array)
- attributes.collect { |attr| create(attr, options, &block) }
- else
- object = new(attributes, options, &block)
- object.save
- object
- end
- end
-
- # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
- # The use of this method should be restricted to complicated SQL queries that can't be executed
- # using the ActiveRecord::Calculations class methods. Look into those before using this.
- #
- # ==== Parameters
- #
- # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
- #
- # ==== Examples
- #
- # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
- def count_by_sql(sql)
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
- end
-
- # Attributes listed as readonly will be used to create a new record but update operations will
- # ignore these fields.
- def attr_readonly(*attributes)
- self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
- end
-
- # Returns an array of all the attributes that have been specified as readonly.
- def readonly_attributes
- self._attr_readonly
- end
-
- # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
- # then specify the name of that attribute using this method and it will be handled automatically.
- # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
- # class on retrieval or SerializationTypeMismatch will be raised.
- #
- # ==== Parameters
- #
- # * +attr_name+ - The field name that should be serialized.
- # * +class_name+ - Optional, class name that the object type should be equal to.
- #
- # ==== Example
- # # Serialize a preferences attribute
- # class User < ActiveRecord::Base
- # serialize :preferences
- # end
- def serialize(attr_name, class_name = Object)
- coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
- class_name
- else
- Coders::YAMLColumn.new(class_name)
- end
-
- # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
- # has its own hash of own serialized attributes
- self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
- end
-
- # Guesses the table name (in forced lower-case) based on the name of the class in the
- # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
- # looks like: Reply < Message < ActiveRecord::Base, then Message is used
- # to guess the table name even when called on Reply. The rules used to do the guess
- # are handled by the Inflector class in Active Support, which knows almost all common
- # English inflections. You can add new inflections in config/initializers/inflections.rb.
- #
- # Nested classes are given table names prefixed by the singular form of
- # the parent's table name. Enclosing modules are not considered.
- #
- # ==== Examples
- #
- # class Invoice < ActiveRecord::Base
- # end
- #
- # file class table_name
- # invoice.rb Invoice invoices
- #
- # class Invoice < ActiveRecord::Base
- # class Lineitem < ActiveRecord::Base
- # end
- # end
- #
- # file class table_name
- # invoice.rb Invoice::Lineitem invoice_lineitems
- #
- # module Invoice
- # class Lineitem < ActiveRecord::Base
- # end
- # end
- #
- # file class table_name
- # invoice/lineitem.rb Invoice::Lineitem lineitems
- #
- # Additionally, the class-level +table_name_prefix+ is prepended and the
- # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
- # the table name guess for an Invoice class becomes "myapp_invoices".
- # Invoice::Lineitem becomes "myapp_invoice_lineitems".
- #
- # You can also overwrite this class method to allow for unguessable
- # links, such as a Mouse class with a link to a "mice" table. Example:
- #
- # class Mouse < ActiveRecord::Base
- # set_table_name "mice"
- # end
- def table_name
- reset_table_name
- end
-
- # Returns a quoted version of the table name, used to construct SQL statements.
- def quoted_table_name
- @quoted_table_name ||= connection.quote_table_name(table_name)
- end
-
- # Computes the table name, (re)sets it internally, and returns it.
- def reset_table_name #:nodoc:
- return if abstract_class?
-
- self.table_name = compute_table_name
- end
-
- def full_table_name_prefix #:nodoc:
- (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
- end
-
- # Defines the column name for use with single table inheritance. Use
- # <tt>set_inheritance_column</tt> to set a different value.
- def inheritance_column
- @inheritance_column ||= "type"
- end
-
- # Lazy-set the sequence name to the connection's default. This method
- # is only ever called once since set_sequence_name overrides it.
- def sequence_name #:nodoc:
- reset_sequence_name
- end
-
- def reset_sequence_name #:nodoc:
- default = connection.default_sequence_name(table_name, primary_key)
- set_sequence_name(default)
- default
- end
-
- # Sets the table name. If the value is nil or false then the value returned by the given
- # block is used.
- #
- # class Project < ActiveRecord::Base
- # set_table_name "project"
- # end
- def set_table_name(value = nil, &block)
- @quoted_table_name = nil
- define_attr_method :table_name, value, &block
- @arel_table = nil
-
- @relation = Relation.new(self, arel_table)
- end
- alias :table_name= :set_table_name
-
- # Sets the name of the inheritance column to use to the given value,
- # or (if the value # is nil or false) to the value returned by the
- # given block.
- #
- # class Project < ActiveRecord::Base
- # set_inheritance_column do
- # original_inheritance_column + "_id"
- # end
- # end
- def set_inheritance_column(value = nil, &block)
- define_attr_method :inheritance_column, value, &block
- end
- alias :inheritance_column= :set_inheritance_column
-
- # Sets the name of the sequence to use when generating ids to the given
- # value, or (if the value is nil or false) to the value returned by the
- # given block. This is required for Oracle and is useful for any
- # database which relies on sequences for primary key generation.
- #
- # If a sequence name is not explicitly set when using Oracle or Firebird,
- # it will default to the commonly used pattern of: #{table_name}_seq
- #
- # If a sequence name is not explicitly set when using PostgreSQL, it
- # will discover the sequence corresponding to your primary key for you.
- #
- # class Project < ActiveRecord::Base
- # set_sequence_name "projectseq" # default would have been "project_seq"
- # end
- def set_sequence_name(value = nil, &block)
- define_attr_method :sequence_name, value, &block
- end
- alias :sequence_name= :set_sequence_name
-
- # Indicates whether the table associated with this class exists
- def table_exists?
- connection.table_exists?(table_name)
- end
-
- # Returns an array of column objects for the table associated with this class.
- def columns
- if defined?(@primary_key)
- connection_pool.primary_keys[table_name] ||= primary_key
- end
-
- connection_pool.columns[table_name]
- end
-
- # Returns a hash of column objects for the table associated with this class.
- def columns_hash
- connection_pool.columns_hash[table_name]
- end
-
- # Returns a hash where the keys are column names and the values are
- # default values when instantiating the AR object for this table.
- def column_defaults
- connection_pool.column_defaults[table_name]
- end
-
- # Returns an array of column names as strings.
- def column_names
- @column_names ||= columns.map { |column| column.name }
- end
-
- # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
- # and columns used for single table inheritance have been removed.
- def content_columns
- @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
- end
-
- # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
- # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
- # is available.
- def column_methods_hash #:nodoc:
- @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
- attr_name = attr.to_s
- methods[attr.to_sym] = attr_name
- methods["#{attr}=".to_sym] = attr_name
- methods["#{attr}?".to_sym] = attr_name
- methods["#{attr}_before_type_cast".to_sym] = attr_name
- methods
- end
- end
-
- # Resets all the cached information about columns, which will cause them
- # to be reloaded on the next request.
- #
- # The most common usage pattern for this method is probably in a migration,
- # when just after creating a table you want to populate it with some default
- # values, eg:
- #
- # class CreateJobLevels < ActiveRecord::Migration
- # def self.up
- # create_table :job_levels do |t|
- # t.integer :id
- # t.string :name
- #
- # t.timestamps
- # end
- #
- # JobLevel.reset_column_information
- # %w{assistant executive manager director}.each do |type|
- # JobLevel.create(:name => type)
- # end
- # end
- #
- # def self.down
- # drop_table :job_levels
- # end
- # end
- def reset_column_information
- connection.clear_cache!
- undefine_attribute_methods
- connection_pool.clear_table_cache!(table_name) if table_exists?
-
- @column_names = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
- @arel_engine = @relation = nil
- end
-
- def clear_cache! # :nodoc:
- connection_pool.clear_cache!
- end
-
- def attribute_method?(attribute)
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
- end
-
- # Returns an array of column names as strings if it's not
- # an abstract class and table exists.
- # Otherwise it returns an empty array.
- def attribute_names
- @attribute_names ||= if !abstract_class? && table_exists?
- column_names
- else
- []
- end
- end
-
- # Set the lookup ancestors for ActiveModel.
- def lookup_ancestors #:nodoc:
- klass = self
- classes = [klass]
- return classes if klass == ActiveRecord::Base
-
- while klass != klass.base_class
- classes << klass = klass.superclass
- end
- classes
- end
-
- # Set the i18n scope to overwrite ActiveModel.
- def i18n_scope #:nodoc:
- :activerecord
- end
-
- # True if this isn't a concrete subclass needing a STI type condition.
- def descends_from_active_record?
- if superclass.abstract_class?
- superclass.descends_from_active_record?
- else
- superclass == Base || !columns_hash.include?(inheritance_column)
- end
- end
-
- def finder_needs_type_condition? #:nodoc:
- # This is like this because benchmarking justifies the strange :false stuff
- :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
- end
-
- # Returns a string like 'Post(id:integer, title:string, body:text)'
- def inspect
- if self == Base
- super
- elsif abstract_class?
- "#{super}(abstract)"
- elsif table_exists?
- attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
- "#{super}(#{attr_list})"
- else
- "#{super}(Table doesn't exist)"
- end
- end
-
- def quote_value(value, column = nil) #:nodoc:
- connection.quote(value,column)
- end
-
- # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
- def sanitize(object) #:nodoc:
- connection.quote(object)
- end
-
- # Overwrite the default class equality method to provide support for association proxies.
- def ===(object)
- object.is_a?(self)
- end
-
- def symbolized_base_class
- @symbolized_base_class ||= base_class.to_s.to_sym
- end
-
- def symbolized_sti_name
- @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
- end
-
- # Returns the base AR subclass that this class descends from. If A
- # extends AR::Base, A.base_class will return A. If B descends from A
- # through some arbitrarily deep hierarchy, B.base_class will return A.
- #
- # If B < A and C < B and if A is an abstract_class then both B.base_class
- # and C.base_class would return B as the answer since A is an abstract_class.
- def base_class
- class_of_active_record_descendant(self)
- end
-
- # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
- attr_accessor :abstract_class
-
- # Returns whether this class is an abstract class or not.
- def abstract_class?
- defined?(@abstract_class) && @abstract_class == true
- end
-
- def respond_to?(method_id, include_private = false)
- if match = DynamicFinderMatch.match(method_id)
- return true if all_attributes_exists?(match.attribute_names)
- elsif match = DynamicScopeMatch.match(method_id)
- return true if all_attributes_exists?(match.attribute_names)
- end
-
- super
- end
-
- def sti_name
- store_full_sti_class ? name : name.demodulize
- end
-
- def arel_table
- @arel_table ||= Arel::Table.new(table_name, arel_engine)
- end
-
- def arel_engine
- @arel_engine ||= begin
- if self == ActiveRecord::Base
- ActiveRecord::Base
- else
- connection_handler.connection_pools[name] ? self : superclass.arel_engine
- end
- end
- end
-
- # Returns a scope for this class without taking into account the default_scope.
- #
- # class Post < ActiveRecord::Base
- # def self.default_scope
- # where :published => true
- # end
- # end
- #
- # Post.all # Fires "SELECT * FROM posts WHERE published = true"
- # Post.unscoped.all # Fires "SELECT * FROM posts"
- #
- # This method also accepts a block meaning that all queries inside the block will
- # not use the default_scope:
- #
- # Post.unscoped {
- # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
- # }
- #
- # It is recommended to use block form of unscoped because chaining unscoped with <tt>scope</tt>
- # does not work. Assuming that <tt>published</tt> is a <tt>scope</tt> following two statements are same.
- #
- # Post.unscoped.published
- # Post.published
- def unscoped #:nodoc:
- block_given? ? relation.scoping { yield } : relation
- end
-
- def before_remove_const #:nodoc:
- self.current_scope = nil
- end
-
- # Finder methods must instantiate through this method to work with the
- # single-table inheritance model that makes it possible to create
- # objects of different types from the same table.
- def instantiate(record)
- sti_class = find_sti_class(record[inheritance_column])
- record_id = sti_class.primary_key && record[sti_class.primary_key]
-
- if ActiveRecord::IdentityMap.enabled? && record_id
- if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
- record_id = record_id.to_i
- end
- if instance = IdentityMap.get(sti_class, record_id)
- instance.reinit_with('attributes' => record)
- else
- instance = sti_class.allocate.init_with('attributes' => record)
- IdentityMap.add(instance)
- end
- else
- instance = sti_class.allocate.init_with('attributes' => record)
- end
-
- instance
- end
-
- private
-
- def relation #:nodoc:
- @relation ||= Relation.new(self, arel_table)
-
- if finder_needs_type_condition?
- @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
- else
- @relation
- end
- end
-
- def find_sti_class(type_name)
- if type_name.blank? || !columns_hash.include?(inheritance_column)
- self
- else
- begin
- if store_full_sti_class
- ActiveSupport::Dependencies.constantize(type_name)
- else
- compute_type(type_name)
- end
- rescue NameError
- raise SubclassNotFound,
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
- "or overwrite #{name}.inheritance_column to use another column for that information."
- end
- end
- end
-
- def construct_finder_arel(options = {}, scope = nil)
- relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
- relation = scope.merge(relation) if scope
- relation
- end
-
- def type_condition(table = arel_table)
- sti_column = table[inheritance_column.to_sym]
- sti_names = ([self] + descendants).map { |model| model.sti_name }
-
- sti_column.in(sti_names)
- end
-
- # Guesses the table name, but does not decorate it with prefix and suffix information.
- def undecorated_table_name(class_name = base_class.name)
- table_name = class_name.to_s.demodulize.underscore
- table_name = table_name.pluralize if pluralize_table_names
- table_name
- end
-
- # Computes and returns a table name according to default conventions.
- def compute_table_name
- base = base_class
- if self == base
- # Nested classes are prefixed with singular parent table name.
- if parent < ActiveRecord::Base && !parent.abstract_class?
- contained = parent.table_name
- contained = contained.singularize if parent.pluralize_table_names
- contained += '_'
- end
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
- else
- # STI subclasses always use their superclass' table.
- base.table_name
- end
- end
-
- # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
- # <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
- # section at the top of this file for more detailed information.
- #
- # It's even possible to use all the additional parameters to +find+. For example, the
- # full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
- #
- # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
- # is first invoked, so that future attempts to use it do not run through method_missing.
- def method_missing(method_id, *arguments, &block)
- if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id))
- attribute_names = match.attribute_names
- super unless all_attributes_exists?(attribute_names)
- if arguments.size < attribute_names.size
- method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
- backtrace = [method_trace] + caller
- raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace
- end
- if match.respond_to?(:scope?) && match.scope?
- self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
- def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
- attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
- #
- scoped(:conditions => attributes) # scoped(:conditions => attributes)
- end # end
- METHOD
- send(method_id, *arguments)
- elsif match.finder?
- options = arguments.extract_options!
- relation = options.any? ? scoped(options) : scoped
- relation.send :find_by_attributes, match, attribute_names, *arguments, &block
- elsif match.instantiator?
- scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
- end
- else
- super
- end
- end
-
- # Similar in purpose to +expand_hash_conditions_for_aggregates+.
- def expand_attribute_names_for_aggregates(attribute_names)
- attribute_names.map { |attribute_name|
- unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
- aggregate_mapping(aggregation).map do |field_attr, _|
- field_attr.to_sym
- end
- else
- attribute_name.to_sym
- end
- }.flatten
- end
-
- def all_attributes_exists?(attribute_names)
- (expand_attribute_names_for_aggregates(attribute_names) -
- column_methods_hash.keys).empty?
- end
-
- protected
- # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
- # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
- # <tt>:create</tt> parameters are an attributes hash.
- #
- # class Article < ActiveRecord::Base
- # def self.create_with_scope
- # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
- # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
- # a = create(1)
- # a.blog_id # => 1
- # end
- # end
- # end
- #
- # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
- # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
- #
- # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
- # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
- # array of strings format for your joins.
- #
- # class Article < ActiveRecord::Base
- # def self.find_with_scope
- # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
- # with_scope(:find => limit(10)) do
- # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
- # end
- # with_scope(:find => where(:author_id => 3)) do
- # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
- # end
- # end
- # end
- # end
- #
- # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
- #
- # class Article < ActiveRecord::Base
- # def self.find_with_exclusive_scope
- # with_scope(:find => where(:blog_id => 1).limit(1)) do
- # with_exclusive_scope(:find => limit(10)) do
- # all # => SELECT * from articles LIMIT 10
- # end
- # end
- # end
- # end
- #
- # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
- def with_scope(scope = {}, action = :merge, &block)
- # If another Active Record class has been passed in, get its current scope
- scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
-
- previous_scope = self.current_scope
-
- if scope.is_a?(Hash)
- # Dup first and second level of hash (method and params).
- scope = scope.dup
- scope.each do |method, params|
- scope[method] = params.dup unless params == true
- end
-
- scope.assert_valid_keys([ :find, :create ])
- relation = construct_finder_arel(scope[:find] || {})
- relation.default_scoped = true unless action == :overwrite
-
- if previous_scope && previous_scope.create_with_value && scope[:create]
- scope_for_create = if action == :merge
- previous_scope.create_with_value.merge(scope[:create])
- else
- scope[:create]
- end
-
- relation = relation.create_with(scope_for_create)
- else
- scope_for_create = scope[:create]
- scope_for_create ||= previous_scope.create_with_value if previous_scope
- relation = relation.create_with(scope_for_create) if scope_for_create
- end
-
- scope = relation
- end
-
- scope = previous_scope.merge(scope) if previous_scope && action == :merge
-
- self.current_scope = scope
- begin
- yield
- ensure
- self.current_scope = previous_scope
- end
- end
-
- # Works like with_scope, but discards any nested properties.
- def with_exclusive_scope(method_scoping = {}, &block)
- if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
- raise ArgumentError, <<-MSG
-New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
-
- User.unscoped.where(:active => true)
-
-Or call unscoped with a block:
-
- User.unscoped do
- User.where(:active => true).all
- end
-
-MSG
- end
- with_scope(method_scoping, :overwrite, &block)
- end
-
- def current_scope #:nodoc:
- Thread.current["#{self}_current_scope"]
- end
-
- def current_scope=(scope) #:nodoc:
- Thread.current["#{self}_current_scope"] = scope
- end
-
- # Use this macro in your model to set a default scope for all operations on
- # the model.
- #
- # class Article < ActiveRecord::Base
- # default_scope where(:published => true)
- # end
- #
- # Article.all # => SELECT * FROM articles WHERE published = true
- #
- # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
- # applied while updating a record.
- #
- # Article.new.published # => true
- # Article.create.published # => true
- #
- # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
- #
- # class Article < ActiveRecord::Base
- # default_scope { where(:published_at => Time.now - 1.week) }
- # end
- #
- # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
- # macro, and it will be called when building the default scope.)
- #
- # If you use multiple <tt>default_scope</tt> declarations in your model then they will
- # be merged together:
- #
- # class Article < ActiveRecord::Base
- # default_scope where(:published => true)
- # default_scope where(:rating => 'G')
- # end
- #
- # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
- #
- # This is also the case with inheritance and module includes where the parent or module
- # defines a <tt>default_scope</tt> and the child or including class defines a second one.
- #
- # If you need to do more complex things with a default scope, you can alternatively
- # define it as a class method:
- #
- # class Article < ActiveRecord::Base
- # def self.default_scope
- # # Should return a scope, you can call 'super' here etc.
- # end
- # end
- def default_scope(scope = {})
- scope = Proc.new if block_given?
- self.default_scopes = default_scopes + [scope]
- end
-
- def build_default_scope #:nodoc:
- if method(:default_scope).owner != Base.singleton_class
- evaluate_default_scope { default_scope }
- elsif default_scopes.any?
- evaluate_default_scope do
- default_scopes.inject(relation) do |default_scope, scope|
- if scope.is_a?(Hash)
- default_scope.apply_finder_options(scope)
- elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
- default_scope.merge(scope.call)
- else
- default_scope.merge(scope)
- end
- end
- end
- end
- end
-
- def ignore_default_scope? #:nodoc:
- Thread.current["#{self}_ignore_default_scope"]
- end
-
- def ignore_default_scope=(ignore) #:nodoc:
- Thread.current["#{self}_ignore_default_scope"] = ignore
- end
-
- # The ignore_default_scope flag is used to prevent an infinite recursion situation where
- # a default scope references a scope which has a default scope which references a scope...
- def evaluate_default_scope
- return if ignore_default_scope?
-
- begin
- self.ignore_default_scope = true
- yield
- ensure
- self.ignore_default_scope = false
- end
- end
-
- # Returns the class type of the record using the current module as a prefix. So descendants of
- # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
- def compute_type(type_name)
- if type_name.match(/^::/)
- # If the type is prefixed with a scope operator then we assume that
- # the type_name is an absolute reference.
- ActiveSupport::Dependencies.constantize(type_name)
- else
- # Build a list of candidates to search for
- candidates = []
- name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
- candidates << type_name
-
- candidates.each do |candidate|
- begin
- constant = ActiveSupport::Dependencies.constantize(candidate)
- return constant if candidate == constant.to_s
- rescue NameError => e
- # We don't want to swallow NoMethodError < NameError errors
- raise e unless e.instance_of?(NameError)
- end
- end
-
- raise NameError, "uninitialized constant #{candidates.first}"
- end
- end
-
- # Returns the class descending directly from ActiveRecord::Base or an
- # abstract class, if any, in the inheritance hierarchy.
- def class_of_active_record_descendant(klass)
- if klass == Base || klass.superclass == Base || klass.superclass.abstract_class?
- klass
- elsif klass.superclass.nil?
- raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
- else
- class_of_active_record_descendant(klass.superclass)
- end
- end
-
- # Accepts an array, hash, or string of SQL conditions and sanitizes
- # them into a valid SQL fragment for a WHERE clause.
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
- # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
- # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
- def sanitize_sql_for_conditions(condition, table_name = self.table_name)
- return nil if condition.blank?
-
- case condition
- when Array; sanitize_sql_array(condition)
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
- else condition
- end
- end
- alias_method :sanitize_sql, :sanitize_sql_for_conditions
-
- # Accepts an array, hash, or string of SQL conditions and sanitizes
- # them into a valid SQL fragment for a SET clause.
- # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
- def sanitize_sql_for_assignment(assignments)
- case assignments
- when Array; sanitize_sql_array(assignments)
- when Hash; sanitize_sql_hash_for_assignment(assignments)
- else assignments
- end
- end
-
- def aggregate_mapping(reflection)
- mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
- mapping.first.is_a?(Array) ? mapping : [mapping]
- end
-
- # Accepts a hash of SQL conditions and replaces those attributes
- # that correspond to a +composed_of+ relationship with their expanded
- # aggregate attribute values.
- # Given:
- # class Person < ActiveRecord::Base
- # composed_of :address, :class_name => "Address",
- # :mapping => [%w(address_street street), %w(address_city city)]
- # end
- # Then:
- # { :address => Address.new("813 abc st.", "chicago") }
- # # => { :address_street => "813 abc st.", :address_city => "chicago" }
- def expand_hash_conditions_for_aggregates(attrs)
- expanded_attrs = {}
- attrs.each do |attr, value|
- unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
- mapping = aggregate_mapping(aggregation)
- mapping.each do |field_attr, aggregate_attr|
- if mapping.size == 1 && !value.respond_to?(aggregate_attr)
- expanded_attrs[field_attr] = value
- else
- expanded_attrs[field_attr] = value.send(aggregate_attr)
- end
- end
- else
- expanded_attrs[attr] = value
- end
- end
- expanded_attrs
- end
-
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
- # { :name => "foo'bar", :group_id => 4 }
- # # => "name='foo''bar' and group_id= 4"
- # { :status => nil, :group_id => [1,2,3] }
- # # => "status IS NULL and group_id IN (1,2,3)"
- # { :age => 13..18 }
- # # => "age BETWEEN 13 AND 18"
- # { 'other_records.id' => 7 }
- # # => "`other_records`.`id` = 7"
- # { :other_records => { :id => 7 } }
- # # => "`other_records`.`id` = 7"
- # And for value objects on a composed_of relationship:
- # { :address => Address.new("123 abc st.", "chicago") }
- # # => "address_street='123 abc st.' and address_city='chicago'"
- def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
- attrs = expand_hash_conditions_for_aggregates(attrs)
-
- table = Arel::Table.new(table_name).alias(default_table_name)
- PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
- connection.visitor.accept b
- }.join(' AND ')
- end
- alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
-
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
- # { :status => nil, :group_id => 1 }
- # # => "status = NULL , group_id = 1"
- def sanitize_sql_hash_for_assignment(attrs)
- attrs.map do |attr, value|
- "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
- end.join(', ')
- end
-
- # Accepts an array of conditions. The array has each value
- # sanitized and interpolated into the SQL statement.
- # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
- def sanitize_sql_array(ary)
- statement, *values = ary
- if values.first.is_a?(Hash) && statement =~ /:\w+/
- replace_named_bind_variables(statement, values.first)
- elsif statement.include?('?')
- replace_bind_variables(statement, values)
- elsif statement.blank?
- statement
- else
- statement % values.collect { |value| connection.quote_string(value.to_s) }
- end
- end
-
- alias_method :sanitize_conditions, :sanitize_sql
-
- def replace_bind_variables(statement, values) #:nodoc:
- raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
- bound = values.dup
- c = connection
- statement.gsub('?') { quote_bound_value(bound.shift, c) }
- end
-
- def replace_named_bind_variables(statement, bind_vars) #:nodoc:
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
- if $1 == ':' # skip postgresql casts
- $& # return the whole match
- elsif bind_vars.include?(match = $2.to_sym)
- quote_bound_value(bind_vars[match])
- else
- raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
- end
- end
- end
-
- def expand_range_bind_variables(bind_vars) #:nodoc:
- expanded = []
-
- bind_vars.each do |var|
- next if var.is_a?(Hash)
-
- if var.is_a?(Range)
- expanded << var.first
- expanded << var.last
- else
- expanded << var
- end
- end
-
- expanded
- end
-
- def quote_bound_value(value, c = connection) #:nodoc:
- if value.respond_to?(:map) && !value.acts_like?(:string)
- if value.respond_to?(:empty?) && value.empty?
- c.quote(nil)
- else
- value.map { |v| c.quote(v) }.join(',')
- end
- else
- c.quote(value)
- end
- end
-
- def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
- unless expected == provided
- raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
- end
- end
-
- def encode_quoted_value(value) #:nodoc:
- quoted_value = connection.quote(value)
- quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
- quoted_value
- end
- end
-
- public
- # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
- # attributes but not yet saved (pass a hash with key names matching the associated table column names).
- # In both instances, valid attribute keys are determined by the column names of the associated table --
- # hence you can't have attributes that aren't part of the table columns.
- #
- # +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
- # in the +options+ parameter.
- #
- # ==== Examples
- # # Instantiates a single new object
- # User.new(:first_name => 'Jamie')
- #
- # # Instantiates a single new object using the :admin mass-assignment security role
- # User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
- #
- # # Instantiates a single new object bypassing mass-assignment security
- # User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
- def initialize(attributes = nil, options = {})
- @attributes = attributes_from_column_definition
- @association_cache = {}
- @aggregation_cache = {}
- @attributes_cache = {}
- @new_record = true
- @readonly = false
- @destroyed = false
- @marked_for_destruction = false
- @previously_changed = {}
- @changed_attributes = {}
- @relation = nil
-
- ensure_proper_type
- set_serialized_attributes
-
- populate_with_current_scope_attributes
-
- assign_attributes(attributes, options) if attributes
-
- yield self if block_given?
- run_callbacks :initialize
- end
-
- # Populate +coder+ with attributes about this record that should be
- # serialized. The structure of +coder+ defined in this method is
- # guaranteed to match the structure of +coder+ passed to the +init_with+
- # method.
- #
- # Example:
- #
- # class Post < ActiveRecord::Base
- # end
- # coder = {}
- # Post.new.encode_with(coder)
- # coder # => { 'id' => nil, ... }
- def encode_with(coder)
- coder['attributes'] = attributes
- end
-
- # Initialize an empty model object from +coder+. +coder+ must contain
- # the attributes necessary for initializing an empty model object. For
- # example:
- #
- # class Post < ActiveRecord::Base
- # end
- #
- # post = Post.allocate
- # post.init_with('attributes' => { 'title' => 'hello world' })
- # post.title # => 'hello world'
- def init_with(coder)
- @attributes = coder['attributes']
- @relation = nil
-
- set_serialized_attributes
-
- @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
- @association_cache = {}
- @aggregation_cache = {}
- @readonly = @destroyed = @marked_for_destruction = false
- @new_record = false
- run_callbacks :find
- run_callbacks :initialize
-
- self
- end
-
- # Returns a String, which Action Pack uses for constructing an URL to this
- # object. The default implementation returns this record's id as a String,
- # or nil if this record's unsaved.
- #
- # For example, suppose that you have a User model, and that you have a
- # <tt>resources :users</tt> route. Normally, +user_path+ will
- # construct a path with the user object's 'id' in it:
- #
- # user = User.find_by_name('Phusion')
- # user_path(user) # => "/users/1"
- #
- # You can override +to_param+ in your model to make +user_path+ construct
- # a path using the user's name instead of the user's id:
- #
- # class User < ActiveRecord::Base
- # def to_param # overridden
- # name
- # end
- # end
- #
- # user = User.find_by_name('Phusion')
- # user_path(user) # => "/users/Phusion"
- def to_param
- # We can't use alias_method here, because method 'id' optimizes itself on the fly.
- id && id.to_s # Be sure to stringify the id for routes
- end
-
- # Returns a cache key that can be used to identify this record.
- #
- # ==== Examples
- #
- # Product.new.cache_key # => "products/new"
- # Product.find(5).cache_key # => "products/5" (updated_at not available)
- # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
- def cache_key
- case
- when new_record?
- "#{self.class.model_name.cache_key}/new"
- when timestamp = self[:updated_at]
- timestamp = timestamp.utc.to_s(:number)
- "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
- else
- "#{self.class.model_name.cache_key}/#{id}"
- end
- end
-
- def quoted_id #:nodoc:
- quote_value(id, column_for_attribute(self.class.primary_key))
- end
-
- # Returns true if the given attribute is in the attributes hash
- def has_attribute?(attr_name)
- @attributes.has_key?(attr_name.to_s)
- end
-
- # Returns an array of names for the attributes available on this object.
- def attribute_names
- @attributes.keys
- end
-
- # Allows you to set all the attributes at once by passing in a hash with keys
- # matching the attribute names (which again matches the column names).
- #
- # If any attributes are protected by either +attr_protected+ or
- # +attr_accessible+ then only settable attributes will be assigned.
- #
- # class User < ActiveRecord::Base
- # attr_protected :is_admin
- # end
- #
- # user = User.new
- # user.attributes = { :username => 'Phusion', :is_admin => true }
- # user.username # => "Phusion"
- # user.is_admin? # => false
- def attributes=(new_attributes)
- return unless new_attributes.is_a?(Hash)
-
- assign_attributes(new_attributes)
- end
-
- # Allows you to set all the attributes for a particular mass-assignment
- # security role by passing in a hash of attributes with keys matching
- # the attribute names (which again matches the column names) and the role
- # name using the :as option.
- #
- # To bypass mass-assignment security you can use the :without_protection => true
- # option.
- #
- # class User < ActiveRecord::Base
- # attr_accessible :name
- # attr_accessible :name, :is_admin, :as => :admin
- # end
- #
- # user = User.new
- # user.assign_attributes({ :name => 'Josh', :is_admin => true })
- # user.name # => "Josh"
- # user.is_admin? # => false
- #
- # user = User.new
- # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
- # user.name # => "Josh"
- # user.is_admin? # => true
- #
- # user = User.new
- # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
- # user.name # => "Josh"
- # user.is_admin? # => true
- def assign_attributes(new_attributes, options = {})
- return unless new_attributes
-
- attributes = new_attributes.stringify_keys
- multi_parameter_attributes = []
- @mass_assignment_options = options
-
- unless options[:without_protection]
- attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
- end
-
- attributes.each do |k, v|
- if k.include?("(")
- multi_parameter_attributes << [ k, v ]
- elsif respond_to?("#{k}=")
- send("#{k}=", v)
- else
- raise(UnknownAttributeError, "unknown attribute: #{k}")
- end
- end
-
- @mass_assignment_options = nil
- assign_multiparameter_attributes(multi_parameter_attributes)
- end
-
- # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
- def attributes
- Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
- end
-
- # Returns an <tt>#inspect</tt>-like string for the value of the
- # attribute +attr_name+. String attributes are truncated upto 50
- # characters, and Date and Time attributes are returned in the
- # <tt>:db</tt> format. Other attributes return the value of
- # <tt>#inspect</tt> without modification.
- #
- # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
- #
- # person.attribute_for_inspect(:name)
- # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
- #
- # person.attribute_for_inspect(:created_at)
- # # => '"2009-01-12 04:48:57"'
- def attribute_for_inspect(attr_name)
- value = read_attribute(attr_name)
-
- if value.is_a?(String) && value.length > 50
- "#{value[0..50]}...".inspect
- elsif value.is_a?(Date) || value.is_a?(Time)
- %("#{value.to_s(:db)}")
- else
- value.inspect
- end
- end
-
- # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
- # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
- def attribute_present?(attribute)
- !_read_attribute(attribute).blank?
- end
-
- # Returns the column object for the named attribute.
- def column_for_attribute(name)
- self.class.columns_hash[name.to_s]
- end
-
- # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
- # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
- #
- # Note that new records are different from any other record by definition, unless the
- # other record is the receiver itself. Besides, if you fetch existing records with
- # +select+ and leave the ID out, you're on your own, this predicate will return false.
- #
- # Note also that destroying a record preserves its ID in the model instance, so deleted
- # models are still comparable.
- def ==(comparison_object)
- super ||
- comparison_object.instance_of?(self.class) &&
- id.present? &&
- comparison_object.id == id
- end
- alias :eql? :==
-
- # Delegates to id in order to allow two records of the same type and id to work with something like:
- # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
- def hash
- id.hash
- end
-
- # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
- def freeze
- @attributes.freeze; self
- end
-
- # Returns +true+ if the attributes hash has been frozen.
- def frozen?
- @attributes.frozen?
- end
-
- # Allows sort on objects
- def <=>(other_object)
- if other_object.is_a?(self.class)
- self.to_key <=> other_object.to_key
- else
- nil
- end
- end
-
- # Backport dup from 1.9 so that initialize_dup() gets called
- unless Object.respond_to?(:initialize_dup)
- def dup # :nodoc:
- copy = super
- copy.initialize_dup(self)
- copy
- end
- end
-
- # Duped objects have no id assigned and are treated as new records. Note
- # that this is a "shallow" copy as it copies the object's attributes
- # only, not its associations. The extent of a "deep" copy is application
- # specific and is therefore left to the application to implement according
- # to its need.
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
- def initialize_dup(other)
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
- cloned_attributes.delete(self.class.primary_key)
-
- @attributes = cloned_attributes
-
- _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
-
- @changed_attributes = {}
- attributes_from_column_definition.each do |attr, orig_value|
- @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
- end
-
- @aggregation_cache = {}
- @association_cache = {}
- @attributes_cache = {}
- @new_record = true
-
- ensure_proper_type
- populate_with_current_scope_attributes
- super
- end
-
- # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
- # attributes will be marked as read only since they cannot be saved.
- def readonly?
- @readonly
- end
-
- # Marks this record as read only.
- def readonly!
- @readonly = true
- end
-
- # Returns the contents of the record as a nicely formatted string.
- def inspect
- inspection = if @attributes
- self.class.column_names.collect { |name|
- if has_attribute?(name)
- "#{name}: #{attribute_for_inspect(name)}"
- end
- }.compact.join(", ")
- else
- "not initialized"
- end
- "#<#{self.class} #{inspection}>"
- end
-
- protected
- def clone_attributes(reader_method = :read_attribute, attributes = {})
- attribute_names.each do |name|
- attributes[name] = clone_attribute_value(reader_method, name)
- end
- attributes
- end
-
- def clone_attribute_value(reader_method, attribute_name)
- value = send(reader_method, attribute_name)
- value.duplicable? ? value.clone : value
- rescue TypeError, NoMethodError
- value
- end
-
- def mass_assignment_options
- @mass_assignment_options ||= {}
- end
-
- def mass_assignment_role
- mass_assignment_options[:as] || :default
- end
-
- private
-
- # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
- # of the array, and then rescues from the possible NoMethodError. If those elements are
- # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
- # which significantly impacts upon performance.
- #
- # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
- #
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
- def to_ary # :nodoc:
- nil
- end
-
- def set_serialized_attributes
- sattrs = self.class.serialized_attributes
-
- sattrs.each do |key, coder|
- @attributes[key] = coder.load @attributes[key] if @attributes.key?(key)
- end
- end
-
- # Sets the attribute used for single table inheritance to this class name if this is not the
- # ActiveRecord::Base descendant.
- # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
- # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
- # No such attribute would be set for objects of the Message class in that example.
- def ensure_proper_type
- klass = self.class
- if klass.finder_needs_type_condition?
- write_attribute(klass.inheritance_column, klass.sti_name)
- end
- end
-
- # The primary key and inheritance column can never be set by mass-assignment for security reasons.
- def self.attributes_protected_by_default
- default = [ primary_key, inheritance_column ]
- default << 'id' unless primary_key.eql? 'id'
- default
- end
-
- # Returns a copy of the attributes hash where all the values have been safely quoted for use in
- # an Arel insert/update method.
- def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
- attrs = {}
- klass = self.class
- arel_table = klass.arel_table
-
- attribute_names.each do |name|
- if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
-
- if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
-
- value = if coder = klass.serialized_attributes[name]
- coder.dump @attributes[name]
- else
- # FIXME: we need @attributes to be used consistently.
- # If the values stored in @attributes were already type
- # casted, this code could be simplified
- read_attribute(name)
- end
-
- attrs[arel_table[name]] = value
- end
- end
- end
- attrs
- end
-
- # Quote strings appropriately for SQL statements.
- def quote_value(value, column = nil)
- self.class.connection.quote(value, column)
- end
-
- # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
- # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
- # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
- # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
- # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
- # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
- # attribute will be set to nil.
- def assign_multiparameter_attributes(pairs)
- execute_callstack_for_multiparameter_attributes(
- extract_callstack_for_multiparameter_attributes(pairs)
- )
- end
-
- def instantiate_time_object(name, values)
- if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
- Time.zone.local(*values)
- else
- Time.time_with_datetime_fallback(@@default_timezone, *values)
- end
- end
-
- def execute_callstack_for_multiparameter_attributes(callstack)
- errors = []
- callstack.each do |name, values_with_empty_parameters|
- begin
- send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
- rescue => ex
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
- end
- end
- unless errors.empty?
- raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
- end
- end
-
- def read_value_from_parameter(name, values_hash_from_param)
- klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
- if values_hash_from_param.values.all?{|v|v.nil?}
- nil
- elsif klass == Time
- read_time_parameter_value(name, values_hash_from_param)
- elsif klass == Date
- read_date_parameter_value(name, values_hash_from_param)
- else
- read_other_parameter_value(klass, name, values_hash_from_param)
- end
- end
-
- def read_time_parameter_value(name, values_hash_from_param)
- # If Date bits were not provided, error
- raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
- # If Date bits were provided but blank, then return nil
- return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
-
- set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
- # If Time bits are not there, then default to 0
- (3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
- instantiate_time_object(name, set_values)
- end
-
- def read_date_parameter_value(name, values_hash_from_param)
- return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
- set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
- begin
- Date.new(*set_values)
- rescue ArgumentError # if Date.new raises an exception on an invalid date
- instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
- end
- end
-
- def read_other_parameter_value(klass, name, values_hash_from_param)
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
- values = (1..max_position).collect do |position|
- raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
- values_hash_from_param[position]
- end
- klass.new(*values)
- end
-
- def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
- [values_hash_from_param.keys.max,upper_cap].min
- end
-
- def extract_callstack_for_multiparameter_attributes(pairs)
- attributes = { }
-
- pairs.each do |pair|
- multiparameter_name, value = pair
- attribute_name = multiparameter_name.split("(").first
- attributes[attribute_name] = {} unless attributes.include?(attribute_name)
-
- parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
- attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
- end
-
- attributes
- end
-
- def type_cast_attribute_value(multiparameter_name, value)
- multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
- end
-
- def find_parameter_position(multiparameter_name)
- multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
- end
-
- # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
- def comma_pair_list(hash)
- hash.map { |k,v| "#{k} = #{v}" }.join(", ")
- end
-
- def quote_columns(quoter, hash)
- Hash[hash.map { |name, value| [quoter.quote_column_name(name), value] }]
- end
-
- def quoted_comma_pair_list(quoter, hash)
- comma_pair_list(quote_columns(quoter, hash))
- end
-
- def convert_number_column_value(value)
- if value == false
- 0
- elsif value == true
- 1
- elsif value.is_a?(String) && value.blank?
- nil
- else
- value
- end
- end
-
- def populate_with_current_scope_attributes
- return unless self.class.scope_attributes?
-
- self.class.scope_attributes.each do |att,value|
- send("#{att}=", value) if respond_to?("#{att}=")
- end
- end
- end
-
- Base.class_eval do
- include ActiveRecord::Persistence
- extend ActiveModel::Naming
- extend QueryCache::ClassMethods
- extend ActiveSupport::Benchmarkable
- extend ActiveSupport::DescendantsTracker
-
- include ActiveModel::Conversion
- include Validations
- extend CounterCache
- include Locking::Optimistic, Locking::Pessimistic
- include AttributeMethods
- include AttributeMethods::Read, AttributeMethods::Write, AttributeMethods::BeforeTypeCast, AttributeMethods::Query
- include AttributeMethods::PrimaryKey
- include AttributeMethods::TimeZoneConversion
- include AttributeMethods::Dirty
- include ActiveModel::MassAssignmentSecurity
- include Callbacks, ActiveModel::Observing, Timestamp
- include Associations, NamedScope
- include IdentityMap
- include ActiveModel::SecurePassword
-
- # AutosaveAssociation needs to be included before Transactions, because we want
- # #save_with_autosave_associations to be wrapped inside a transaction.
- include AutosaveAssociation, NestedAttributes
- include Aggregations, Transactions, Reflection, Serialization, Store
-
- NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
-
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
- # (Alias for the protected read_attribute method).
- alias [] read_attribute
-
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
- # (Alias for the protected write_attribute method).
- alias []= write_attribute
-
- public :[], :[]=
+ include ActiveRecord::Model
end
end
-require 'active_record/connection_adapters/abstract/connection_specification'
-ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
+ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Model::DeprecationProxy)
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index a175bf003c..a050fabf35 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/array/wrap'
-
module ActiveRecord
# = Active Record Callbacks
#
@@ -25,7 +23,7 @@ module ActiveRecord
# Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
# <tt>after_rollback</tt>.
#
- # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
+ # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
# are instantiated as well.
#
@@ -215,24 +213,48 @@ module ActiveRecord
# instead of quietly returning +false+.
#
# == Debugging callbacks
- #
- # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
+ #
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
# <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
# defines what part of the chain the callback runs in.
- #
- # To find all callbacks in the before_save callback chain:
- #
+ #
+ # To find all callbacks in the before_save callback chain:
+ #
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
- #
+ #
# Returns an array of callback objects that form the before_save chain.
- #
+ #
# To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
- #
+ #
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
- #
+ #
# Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
- #
+ #
module Callbacks
+ # We can't define callbacks directly on ActiveRecord::Model because
+ # it is a module. So we queue up the definitions and execute them
+ # when ActiveRecord::Model is included.
+ module Register #:nodoc:
+ def self.extended(base)
+ base.config_attribute :_callbacks_register
+ base._callbacks_register = []
+ end
+
+ def self.setup(base)
+ base._callbacks_register.each do |item|
+ base.send(*item)
+ end
+ end
+
+ def define_callbacks(*args)
+ self._callbacks_register << [:define_callbacks, *args]
+ end
+
+ def define_model_callbacks(*args)
+ self._callbacks_register << [:define_model_callbacks, *args]
+ end
+ end
+
extend ActiveSupport::Concern
CALLBACKS = [
@@ -242,8 +264,11 @@ module ActiveRecord
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
]
+ module ClassMethods
+ include ActiveModel::Callbacks
+ end
+
included do
- extend ActiveModel::Callbacks
include ActiveModel::Validations::Callbacks
define_model_callbacks :initialize, :find, :touch, :only => :after
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index fb59d9fb07..77af540c3e 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -15,7 +15,7 @@ module ActiveRecord
end
def dump(obj)
- YAML.dump obj
+ YAML.dump(obj) unless obj.nil?
end
def load(yaml)
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 77a5fe1efb..06b9bc5765 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -1,7 +1,7 @@
require 'thread'
require 'monitor'
require 'set'
-require 'active_support/core_ext/module/synchronization'
+require 'active_support/core_ext/module/deprecation'
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -9,6 +9,13 @@ module ActiveRecord
class ConnectionTimeoutError < ConnectionNotEstablished
end
+ # Raised when a connection pool is full and another connection is requested
+ class PoolFullError < ConnectionNotEstablished
+ def initialize size, timeout
+ super("Connection pool of size #{size} and timeout #{timeout}s is full")
+ end
+ end
+
module ConnectionAdapters
# Connection pool base class for managing Active Record database
# connections.
@@ -57,10 +64,35 @@ module ActiveRecord
# * +wait_timeout+: number of seconds to block and wait for a connection
# before giving up and raising a timeout error (default 5 seconds).
class ConnectionPool
- attr_accessor :automatic_reconnect
- attr_reader :spec, :connections
- attr_reader :columns, :columns_hash, :primary_keys, :tables
- attr_reader :column_defaults
+ # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
+ # A reaper instantiated with a nil frequency will never reap the
+ # connection pool.
+ #
+ # Configure the frequency by setting "reaping_frequency" in your
+ # database yaml file.
+ class Reaper
+ attr_reader :pool, :frequency
+
+ def initialize(pool, frequency)
+ @pool = pool
+ @frequency = frequency
+ end
+
+ def run
+ return unless frequency
+ Thread.new(frequency, pool) { |t, p|
+ while true
+ sleep t
+ p.reap
+ end
+ }
+ end
+ end
+
+ include MonitorMixin
+
+ attr_accessor :automatic_reconnect, :timeout
+ attr_reader :spec, :connections, :size, :reaper
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
# object which describes database connection information (e.g. adapter,
@@ -69,88 +101,22 @@ module ActiveRecord
#
# The default ConnectionPool maximum size is 5.
def initialize(spec)
+ super()
+
@spec = spec
# The cache of reserved connections mapped to threads
@reserved_connections = {}
- # The mutex used to synchronize pool access
- @connection_mutex = Monitor.new
- @queue = @connection_mutex.new_cond
@timeout = spec.config[:wait_timeout] || 5
+ @reaper = Reaper.new self, spec.config[:reaping_frequency]
+ @reaper.run
# default max pool size to 5
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@connections = []
- @checked_out = []
@automatic_reconnect = true
- @tables = {}
- @visitor = nil
-
- @columns = Hash.new do |h, table_name|
- h[table_name] = with_connection do |conn|
-
- # Fetch a list of columns
- conn.columns(table_name, "#{table_name} Columns").tap do |columns|
-
- # set primary key information
- columns.each do |column|
- column.primary = column.name == primary_keys[table_name]
- end
- end
- end
- end
-
- @columns_hash = Hash.new do |h, table_name|
- h[table_name] = Hash[columns[table_name].map { |col|
- [col.name, col]
- }]
- end
-
- @column_defaults = Hash.new do |h, table_name|
- h[table_name] = Hash[columns[table_name].map { |col|
- [col.name, col.default]
- }]
- end
-
- @primary_keys = Hash.new do |h, table_name|
- h[table_name] = with_connection do |conn|
- table_exists?(table_name) ? conn.primary_key(table_name) : 'id'
- end
- end
- end
-
- # A cached lookup for table existence.
- def table_exists?(name)
- return true if @tables.key? name
-
- with_connection do |conn|
- conn.tables.each { |table| @tables[table] = true }
- @tables[name] = true if !@tables.key?(name) && conn.table_exists?(name)
- end
-
- @tables.key? name
- end
-
- # Clears out internal caches:
- #
- # * columns
- # * columns_hash
- # * tables
- def clear_cache!
- @columns.clear
- @columns_hash.clear
- @column_defaults.clear
- @tables.clear
- end
-
- # Clear out internal caches for table with +table_name+.
- def clear_table_cache!(table_name)
- @columns.delete table_name
- @columns_hash.delete table_name
- @column_defaults.delete table_name
- @primary_keys.delete table_name
end
# Retrieve the connection associated with the current thread, or call
@@ -165,7 +131,7 @@ module ActiveRecord
# Check to see if there is an active connection in this connection
# pool.
def active_connection?
- @reserved_connections.key? current_connection_id
+ active_connections.any?
end
# Signal that the thread is finished with the current connection.
@@ -181,7 +147,7 @@ module ActiveRecord
# connection when finished.
def with_connection
connection_id = current_connection_id
- fresh_connection = true unless @reserved_connections[connection_id]
+ fresh_connection = true unless active_connection?
yield connection
ensure
release_connection(connection_id) if fresh_connection
@@ -189,94 +155,80 @@ module ActiveRecord
# Returns true if a connection has already been opened.
def connected?
- !@connections.empty?
+ synchronize { @connections.any? }
end
# Disconnects all connections in the pool, and clears the pool.
def disconnect!
- @reserved_connections.each do |name,conn|
- checkin conn
- end
- @reserved_connections = {}
- @connections.each do |conn|
- conn.disconnect!
+ synchronize do
+ @reserved_connections = {}
+ @connections.each do |conn|
+ checkin conn
+ conn.disconnect!
+ end
+ @connections = []
end
- @connections = []
end
# Clears the cache which maps classes.
def clear_reloadable_connections!
- @reserved_connections.each do |name, conn|
- checkin conn
- end
- @reserved_connections = {}
- @connections.each do |conn|
- conn.disconnect! if conn.requires_reloading?
- end
- @connections.delete_if do |conn|
- conn.requires_reloading?
+ synchronize do
+ @reserved_connections = {}
+ @connections.each do |conn|
+ checkin conn
+ conn.disconnect! if conn.requires_reloading?
+ end
+ @connections.delete_if do |conn|
+ conn.requires_reloading?
+ end
end
end
# Verify active connections and remove and disconnect connections
# associated with stale threads.
def verify_active_connections! #:nodoc:
- clear_stale_cached_connections!
- @connections.each do |connection|
- connection.verify!
+ synchronize do
+ @connections.each do |connection|
+ connection.verify!
+ end
end
end
- # Return any checked-out connections back to the pool by threads that
- # are no longer alive.
- def clear_stale_cached_connections!
- keys = @reserved_connections.keys - Thread.list.find_all { |t|
- t.alive?
- }.map { |thread| thread.object_id }
- keys.each do |key|
- checkin @reserved_connections[key]
- @reserved_connections.delete(key)
- end
+ def clear_stale_cached_connections! # :nodoc:
end
+ deprecate :clear_stale_cached_connections!
# Check-out a database connection from the pool, indicating that you want
# to use it. You should call #checkin when you no longer need this.
#
- # This is done by either returning an existing connection, or by creating
- # a new connection. If the maximum number of connections for this pool has
- # already been reached, but the pool is empty (i.e. they're all being used),
- # then this method will wait until a thread has checked in a connection.
- # The wait time is bounded however: if no connection can be checked out
- # within the timeout specified for this pool, then a ConnectionTimeoutError
- # exception will be raised.
+ # This is done by either returning and leasing existing connection, or by
+ # creating a new connection and leasing it.
+ #
+ # If all connections are leased and the pool is at capacity (meaning the
+ # number of currently leased connections is greater than or equal to the
+ # size limit set), an ActiveRecord::PoolFullError exception will be raised.
#
# Returns: an AbstractAdapter object.
#
# Raises:
- # - ConnectionTimeoutError: no connection can be obtained from the pool
- # within the timeout period.
+ # - PoolFullError: no connection can be obtained from the pool.
def checkout
# Checkout an available connection
- @connection_mutex.synchronize do
- loop do
- conn = if @checked_out.size < @connections.size
- checkout_existing_connection
- elsif @connections.size < @size
- checkout_new_connection
- end
- return conn if conn
-
- @queue.wait(@timeout)
-
- if(@checked_out.size < @connections.size)
- next
- else
- clear_stale_cached_connections!
- if @size == @checked_out.size
- raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
- end
- end
+ synchronize do
+ # Try to find a connection that hasn't been leased, and lease it
+ conn = connections.find { |c| c.lease }
+
+ # If all connections were leased, and we have room to expand,
+ # create a new connection and lease it.
+ if !conn && connections.size < size
+ conn = checkout_new_connection
+ conn.lease
+ end
+ if conn
+ checkout_and_verify conn
+ else
+ raise PoolFullError.new(size, timeout)
end
end
end
@@ -287,30 +239,44 @@ module ActiveRecord
# +conn+: an AbstractAdapter object, which was obtained by earlier by
# calling +checkout+ on this pool.
def checkin(conn)
- @connection_mutex.synchronize do
+ synchronize do
conn.run_callbacks :checkin do
- @checked_out.delete conn
- @queue.signal
+ conn.expire
end
end
end
- synchronize :clear_reloadable_connections!, :verify_active_connections!,
- :connected?, :disconnect!, :with => :@connection_mutex
+ # Remove a connection from the connection pool. The connection will
+ # remain open and active but will no longer be managed by this pool.
+ def remove(conn)
+ synchronize do
+ @connections.delete conn
- private
+ # FIXME: we might want to store the key on the connection so that removing
+ # from the reserved hash will be a little easier.
+ thread_id = @reserved_connections.keys.find { |k|
+ @reserved_connections[k] == conn
+ }
+ @reserved_connections.delete thread_id if thread_id
+ end
+ end
- def new_connection
- connection = ActiveRecord::Base.send(spec.adapter_method, spec.config)
+ # Removes dead connections from the pool. A dead connection can occur
+ # if a programmer forgets to close a connection at the end of a thread
+ # or a thread dies unexpectedly.
+ def reap
+ synchronize do
+ stale = Time.now - @timeout
+ connections.dup.each do |conn|
+ remove conn if conn.in_use? && stale > conn.last_use && !conn.active?
+ end
+ end
+ end
- # TODO: This is a bit icky, and in the long term we may want to change the method
- # signature for connections. Also, if we switch to have one visitor per
- # connection (and therefore per thread), we can get rid of the thread-local
- # variable in Arel::Visitors::ToSql.
- @visitor ||= connection.class.visitor_for(self)
- connection.visitor = @visitor
+ private
- connection
+ def new_connection
+ ActiveRecord::Base.send(spec.adapter_method, spec.config)
end
def current_connection_id #:nodoc:
@@ -321,22 +287,21 @@ module ActiveRecord
raise ConnectionNotEstablished unless @automatic_reconnect
c = new_connection
+ c.pool = self
@connections << c
- checkout_and_verify(c)
- end
-
- def checkout_existing_connection
- c = (@connections - @checked_out).first
- checkout_and_verify(c)
+ c
end
def checkout_and_verify(c)
c.run_callbacks :checkout do
c.verify!
- @checked_out << c
end
c
end
+
+ def active_connections
+ @connections.find_all { |c| c.in_use? }
+ end
end
# ConnectionHandler is a collection of ConnectionPool objects. It is used
@@ -363,14 +328,18 @@ module ActiveRecord
# ActiveRecord::Base.connection_handler. Active Record models use this to
# determine that connection pool that they should use.
class ConnectionHandler
- attr_reader :connection_pools
-
- def initialize(pools = {})
+ def initialize(pools = Hash.new { |h,k| h[k] = {} })
@connection_pools = pools
+ @class_to_pool = Hash.new { |h,k| h[k] = {} }
+ end
+
+ def connection_pools
+ @connection_pools[Process.pid]
end
def establish_connection(name, spec)
- @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
+ set_pool_for_spec spec, ConnectionAdapters::ConnectionPool.new(spec)
+ set_class_to_pool name, connection_pools[spec]
end
# Returns true if there are any active connections among the connection
@@ -383,21 +352,21 @@ module ActiveRecord
# and also returns connections to the pool cached by threads that are no
# longer alive.
def clear_active_connections!
- @connection_pools.each_value {|pool| pool.release_connection }
+ connection_pools.each_value {|pool| pool.release_connection }
end
# Clears the cache which maps classes.
def clear_reloadable_connections!
- @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
+ connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
end
def clear_all_connections!
- @connection_pools.each_value {|pool| pool.disconnect! }
+ connection_pools.each_value {|pool| pool.disconnect! }
end
# Verify active connections.
def verify_active_connections! #:nodoc:
- @connection_pools.each_value {|pool| pool.verify_active_connections! }
+ connection_pools.each_value {|pool| pool.verify_active_connections! }
end
# Locate the connection of the nearest super class. This can be an
@@ -421,52 +390,56 @@ module ActiveRecord
# can be used as an argument for establish_connection, for easily
# re-establishing the connection.
def remove_connection(klass)
- pool = @connection_pools.delete(klass.name)
+ pool = class_to_pool.delete(klass.name)
return nil unless pool
+ connection_pools.delete pool.spec
pool.automatic_reconnect = false
pool.disconnect!
pool.spec.config
end
def retrieve_connection_pool(klass)
- pool = @connection_pools[klass.name]
+ pool = get_pool_for_class klass.name
return pool if pool
- return nil if ActiveRecord::Base == klass
- retrieve_connection_pool klass.superclass
+ return nil if ActiveRecord::Model == klass
+ retrieve_connection_pool klass.active_record_super
end
- end
-
- class ConnectionManagement
- class Proxy # :nodoc:
- attr_reader :body, :testing
- def initialize(body, testing = false)
- @body = body
- @testing = testing
- end
+ private
- def method_missing(method_sym, *arguments, &block)
- @body.send(method_sym, *arguments, &block)
- end
+ def class_to_pool
+ @class_to_pool[Process.pid]
+ end
- def respond_to?(method_sym, include_private = false)
- super || @body.respond_to?(method_sym)
- end
+ def set_pool_for_spec(spec, pool)
+ @connection_pools[Process.pid][spec] = pool
+ end
- def each(&block)
- body.each(&block)
- end
+ def set_class_to_pool(name, pool)
+ @class_to_pool[Process.pid][name] = pool
+ pool
+ end
- def close
- body.close if body.respond_to?(:close)
+ def get_pool_for_class(klass)
+ @class_to_pool[Process.pid].fetch(klass) {
+ c_to_p = @class_to_pool.values.find { |class_to_pool|
+ class_to_pool[klass]
+ }
- # Don't return connection (and perform implicit rollback) if
- # this request is a part of integration test
- ActiveRecord::Base.clear_active_connections! unless testing
- end
+ if c_to_p
+ pool = c_to_p[klass]
+ pool = ConnectionAdapters::ConnectionPool.new pool.spec
+ set_pool_for_spec pool.spec, pool
+ set_class_to_pool klass, pool
+ else
+ set_class_to_pool klass, nil
+ end
+ }
end
+ end
+ class ConnectionManagement
def initialize(app)
@app = app
end
@@ -474,9 +447,12 @@ module ActiveRecord
def call(env)
testing = env.key?('rack.test')
- status, headers, body = @app.call(env)
+ response = @app.call(env)
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
+ ActiveRecord::Base.clear_active_connections! unless testing
+ end
- [status, headers, Proxy.new(body, testing)]
+ response
rescue
ActiveRecord::Base.clear_active_connections! unless testing
raise
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
deleted file mode 100644
index 3d0f146fed..0000000000
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ /dev/null
@@ -1,161 +0,0 @@
-module ActiveRecord
- class Base
- class ConnectionSpecification #:nodoc:
- attr_reader :config, :adapter_method
- def initialize (config, adapter_method)
- @config, @adapter_method = config, adapter_method
- end
- end
-
- ##
- # :singleton-method:
- # The connection handler
- class_attribute :connection_handler, :instance_writer => false
- self.connection_handler = ConnectionAdapters::ConnectionHandler.new
-
- # Returns the connection currently associated with the class. This can
- # also be used to "borrow" the connection to do database work that isn't
- # easily done without going straight to SQL.
- def connection
- self.class.connection
- end
-
- # Establishes the connection to the database. Accepts a hash as input where
- # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
- # example for regular databases (MySQL, Postgresql, etc):
- #
- # ActiveRecord::Base.establish_connection(
- # :adapter => "mysql",
- # :host => "localhost",
- # :username => "myuser",
- # :password => "mypass",
- # :database => "somedatabase"
- # )
- #
- # Example for SQLite database:
- #
- # ActiveRecord::Base.establish_connection(
- # :adapter => "sqlite",
- # :database => "path/to/dbfile"
- # )
- #
- # Also accepts keys as strings (for parsing from YAML for example):
- #
- # ActiveRecord::Base.establish_connection(
- # "adapter" => "sqlite",
- # "database" => "path/to/dbfile"
- # )
- #
- # Or a URL:
- #
- # ActiveRecord::Base.establish_connection(
- # "postgres://myuser:mypass@localhost/somedatabase"
- # )
- #
- # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
- # may be returned on an error.
- def self.establish_connection(spec = ENV["DATABASE_URL"])
- case spec
- when nil
- raise AdapterNotSpecified unless defined?(Rails.env)
- establish_connection(Rails.env)
- when ConnectionSpecification
- self.connection_handler.establish_connection(name, spec)
- when Symbol, String
- if configuration = configurations[spec.to_s]
- establish_connection(configuration)
- elsif spec.is_a?(String) && hash = connection_url_to_hash(spec)
- establish_connection(hash)
- else
- raise AdapterNotSpecified, "#{spec} database is not configured"
- end
- else
- spec = spec.symbolize_keys
- unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
-
- begin
- require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
- rescue LoadError => e
- raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e})"
- end
-
- adapter_method = "#{spec[:adapter]}_connection"
- unless respond_to?(adapter_method)
- raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
- end
-
- remove_connection
- establish_connection(ConnectionSpecification.new(spec, adapter_method))
- end
- end
-
- def self.connection_url_to_hash(url) # :nodoc:
- config = URI.parse url
- adapter = config.scheme
- adapter = "postgresql" if adapter == "postgres"
- spec = { :adapter => adapter,
- :username => config.user,
- :password => config.password,
- :port => config.port,
- :database => config.path.sub(%r{^/},""),
- :host => config.host }
- spec.reject!{ |_,value| !value }
- if config.query
- options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
- spec.merge!(options)
- end
- spec
- end
-
- class << self
- # Returns the connection currently associated with the class. This can
- # also be used to "borrow" the connection to do database work unrelated
- # to any of the specific Active Records.
- def connection
- retrieve_connection
- end
-
- def connection_id
- Thread.current['ActiveRecord::Base.connection_id']
- end
-
- def connection_id=(connection_id)
- Thread.current['ActiveRecord::Base.connection_id'] = connection_id
- end
-
- # Returns the configuration of the associated connection as a hash:
- #
- # ActiveRecord::Base.connection_config
- # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"}
- #
- # Please use only for reading.
- def connection_config
- connection_pool.spec.config
- end
-
- def connection_pool
- connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
- end
-
- def retrieve_connection
- connection_handler.retrieve_connection(self)
- end
-
- # Returns true if Active Record is connected.
- def connected?
- connection_handler.connected?(self)
- end
-
- def remove_connection(klass = self)
- connection_handler.remove_connection(klass)
- end
-
- def clear_active_connections!
- connection_handler.clear_active_connections!
- end
-
- delegate :clear_reloadable_connections!,
- :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index dc4a53034b..eb8cff9610 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -130,7 +130,7 @@ module ActiveRecord
#
# In order to get around this problem, #transaction will emulate the effect
# of nested transactions, by using savepoints:
- # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
+ # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
# Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
#
# It is safe to call this method if a database transaction is already open,
@@ -341,7 +341,7 @@ module ActiveRecord
# Send a rollback message to all records after they have been rolled back. If rollback
# is false, only rollback records since the last save point.
- def rollback_transaction_records(rollback) #:nodoc
+ def rollback_transaction_records(rollback)
if rollback
records = @_current_transaction_records.flatten
@_current_transaction_records.clear
@@ -361,7 +361,7 @@ module ActiveRecord
end
# Send a commit message to all records after they have been committed.
- def commit_transaction_records #:nodoc
+ def commit_transaction_records
records = @_current_transaction_records.flatten
@_current_transaction_records.clear
unless records.blank?
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 27ff13ad89..6ba64bb88f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -65,18 +65,24 @@ module ActiveRecord
end
private
- def cache_sql(sql, binds)
- result =
- if @query_cache[sql].key?(binds)
- ActiveSupport::Notifications.instrument("sql.active_record",
- :sql => sql, :name => "CACHE", :connection_id => object_id)
- @query_cache[sql][binds]
- else
- @query_cache[sql][binds] = yield
- end
+ def cache_sql(sql, binds)
+ result =
+ if @query_cache[sql].key?(binds)
+ ActiveSupport::Notifications.instrument("sql.active_record",
+ :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
+ @query_cache[sql][binds]
+ else
+ @query_cache[sql][binds] = yield
+ end
+ # FIXME: we should guarantee that all cached items are Result
+ # objects. Then we can avoid this conditional
+ if ActiveRecord::Result === result
+ result.dup
+ else
result.collect { |row| row.dup }
end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index f93c7cd74a..44ac37c498 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -71,7 +71,8 @@ module ActiveRecord
when Date, Time then quoted_date(value)
when Symbol then value.to_s
else
- YAML.dump(value)
+ to_type = column ? " to #{column.type}" : ""
+ raise TypeError, "can't cast #{value.class}#{to_type}"
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 989a4fcbca..ad2e8634eb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -6,7 +6,7 @@ require 'bigdecimal/util'
module ActiveRecord
module ConnectionAdapters #:nodoc:
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where) #:nodoc:
end
# Abstract representation of a column definition. Instances of this type
@@ -46,13 +46,13 @@ module ActiveRecord
# +change_table+ is actually of this type:
#
# class SomeMigration < ActiveRecord::Migration
- # def self.up
+ # def up
# create_table :foo do |t|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
# end
# end
#
- # def self.down
+ # def down
# ...
# end
# end
@@ -66,6 +66,7 @@ module ActiveRecord
def initialize(base)
@columns = []
+ @columns_hash = {}
@base = base
end
@@ -86,7 +87,7 @@ module ActiveRecord
# Returns a ColumnDefinition for the column with name +name+.
def [](name)
- @columns.find {|column| column.name.to_s == name.to_s}
+ @columns_hash[name.to_s]
end
# Instantiates a new column for the table.
@@ -224,28 +225,31 @@ module ActiveRecord
# t.references :taggable, :polymorphic => { :default => 'Photo' }
# end
def column(name, type, options = {})
- column = self[name] || ColumnDefinition.new(@base, name, type)
- if options[:limit]
- column.limit = options[:limit]
- elsif native[type.to_sym].is_a?(Hash)
- column.limit = native[type.to_sym][:limit]
+ name = name.to_s
+ type = type.to_sym
+
+ column = self[name] || new_column_definition(@base, name, type)
+
+ limit = options.fetch(:limit) do
+ native[type][:limit] if native[type].is_a?(Hash)
end
+
+ column.limit = limit
column.precision = options[:precision]
- column.scale = options[:scale]
- column.default = options[:default]
- column.null = options[:null]
- @columns << column unless @columns.include? column
+ column.scale = options[:scale]
+ column.default = options[:default]
+ column.null = options[:null]
self
end
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
class_eval <<-EOV, __FILE__, __LINE__ + 1
- def #{column_type}(*args) # def string(*args)
- options = args.extract_options! # options = args.extract_options!
- column_names = args # column_names = args
- #
- column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
- end # end
+ def #{column_type}(*args) # def string(*args)
+ options = args.extract_options! # options = args.extract_options!
+ column_names = args # column_names = args
+ type = :'#{column_type}' # type = :string
+ column_names.each { |name| column(name, type, options) } # column_names.each { |name| column(name, type, options) }
+ end # end
EOV
end
@@ -275,9 +279,16 @@ module ActiveRecord
end
private
- def native
- @base.native_database_types
- end
+ def new_column_definition(base, name, type)
+ definition = ColumnDefinition.new base, name, type
+ @columns << definition
+ @columns_hash[name] = definition
+ definition
+ end
+
+ def native
+ @base.native_database_types
+ end
end
# Represents an SQL table in an abstract way for updating a table.
@@ -453,13 +464,13 @@ module ActiveRecord
def #{column_type}(*args) # def string(*args)
options = args.extract_options! # options = args.extract_options!
column_names = args # column_names = args
- #
+ type = :'#{column_type}' # type = :string
column_names.each do |name| # column_names.each do |name|
- column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
+ column = ColumnDefinition.new(@base, name.to_s, type) # column = ColumnDefinition.new(@base, name, type)
if options[:limit] # if options[:limit]
column.limit = options[:limit] # column.limit = options[:limit]
- elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
- column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
+ elsif native[type].is_a?(Hash) # elsif native[type].is_a?(Hash)
+ column.limit = native[type][:limit] # column.limit = native[type][:limit]
end # end
column.precision = options[:precision] # column.precision = options[:precision]
column.scale = options[:scale] # column.scale = options[:scale]
@@ -479,4 +490,3 @@ module ActiveRecord
end
end
-
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 b4a9e29ef1..6bac05191d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1,8 +1,12 @@
-require 'active_support/core_ext/array/wrap'
+require 'active_support/deprecation/reporting'
+require 'active_record/schema_migration'
+require 'active_record/migration/join_table'
module ActiveRecord
module ConnectionAdapters # :nodoc:
module SchemaStatements
+ include ActiveRecord::Migration::JoinTable
+
# Returns a Hash of mappings from the abstract data types to the native
# database types. See TableDefinition#column for details on the recognized
# abstract data types.
@@ -15,8 +19,6 @@ module ActiveRecord
table_name[0...table_alias_length].gsub(/\./, '_')
end
- # def tables(name = nil) end
-
# Checks to see if the table +table_name+ exists on the database.
#
# === Example
@@ -43,7 +45,7 @@ module ActiveRecord
# # Check an index with a custom name exists
# index_exists?(:suppliers, :company_id, :name => "idx_company_id"
def index_exists?(table_name, column_name, options = {})
- column_names = Array.wrap(column_name)
+ column_names = Array(column_name)
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
if options[:unique]
indexes(table_name).any?{ |i| i.unique && i.name == index_name }
@@ -54,7 +56,7 @@ module ActiveRecord
# Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values.
- def columns(table_name, name = nil) end
+ def columns(table_name) end
# Checks to see if a column exists in a given table.
#
@@ -113,7 +115,7 @@ module ActiveRecord
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
#
# Also note that this just sets the primary key in the table. You additionally
- # need to configure the primary key in the model via the +set_primary_key+ macro.
+ # need to configure the primary key in the model via +self.primary_key=+.
# Models do NOT auto-detect the primary key from their table definition.
#
# [<tt>:options</tt>]
@@ -161,7 +163,7 @@ module ActiveRecord
yield td if block_given?
if options[:force] && table_exists?(table_name)
- drop_table(table_name)
+ drop_table(table_name, options)
end
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
@@ -171,6 +173,45 @@ module ActiveRecord
execute create_sql
end
+ # Creates a new join table with the name created using the lexical order of the first two
+ # arguments. These arguments can be be a String or a Symbol.
+ #
+ # # Creates a table called 'assemblies_parts' with no id.
+ # create_join_table(:assemblies, :parts)
+ #
+ # You can pass a +options+ hash can include the following keys:
+ # [<tt>:table_name</tt>]
+ # Sets the table name overriding the default
+ # [<tt>:column_options</tt>]
+ # Any extra options you want appended to the columns definition.
+ # [<tt>:options</tt>]
+ # Any extra options you want appended to the table definition.
+ # [<tt>:temporary</tt>]
+ # Make a temporary table.
+ # [<tt>:force</tt>]
+ # Set to true to drop the table before creating it.
+ # Defaults to false.
+ #
+ # ===== Examples
+ # ====== Add a backend specific option to the generated SQL (MySQL)
+ # create_join_table(:assemblies, :parts, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
+ # generates:
+ # CREATE TABLE assemblies_parts (
+ # assembly_id int NOT NULL,
+ # part_id int NOT NULL,
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
+ def create_join_table(table_1, table_2, options = {})
+ join_table_name = find_join_table_name(table_1, table_2, options)
+
+ column_options = options.delete(:column_options) || {}
+ column_options.reverse_merge!({:null => false})
+
+ create_table(join_table_name, options.merge!(:id => false)) do |td|
+ td.integer :"#{table_1.to_s.singularize}_id", column_options
+ td.integer :"#{table_2.to_s.singularize}_id", column_options
+ end
+ end
+
# A block for changing columns in +table+.
#
# === Example
@@ -253,7 +294,7 @@ module ActiveRecord
end
# Drops a table from the database.
- def drop_table(table_name)
+ def drop_table(table_name, options = {})
execute "DROP TABLE #{quote_table_name(table_name)}"
end
@@ -302,15 +343,8 @@ module ActiveRecord
# Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols.
#
- # The index will be named after the table and the first column name,
- # unless you pass <tt>:name</tt> as an option.
- #
- # When creating an index on multiple columns, the first column is used as a name
- # for the index. For example, when you specify an index on two columns
- # [<tt>:first</tt>, <tt>:last</tt>], the DBMS creates an index for both columns as well as an
- # index for the first column <tt>:first</tt>. Using just the first name for this index
- # makes sense, because you will never have to create a singular index with this
- # name.
+ # The index will be named after the table and the column name(s), unless
+ # you pass <tt>:name</tt> as an option.
#
# ===== Examples
#
@@ -339,9 +373,24 @@ module ActiveRecord
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
#
# Note: SQLite doesn't support index length
+ #
+ # ====== Creating an index with a sort order (desc or asc, asc is the default)
+ # add_index(:accounts, [:branch_id, :party_id, :surname], :order => {:branch_id => :desc, :part_id => :asc})
+ # generates
+ # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
+ #
+ # Note: mysql doesn't yet support index order (it accepts the syntax but ignores it)
+ #
+ # ====== Creating a partial index
+ # add_index(:accounts, [:branch_id, :party_id], :unique => true, :where => "active")
+ # generates
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
+ #
+ # Note: only supported by PostgreSQL
+ #
def add_index(table_name, column_name, options = {})
- index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
end
# Remove the given index from the table.
@@ -377,7 +426,7 @@ module ActiveRecord
def index_name(table_name, options) #:nodoc:
if Hash === options # legacy support
if options[:column]
- "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
elsif options[:name]
options[:name]
else
@@ -405,37 +454,20 @@ module ActiveRecord
def dump_schema_information #:nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- migrated = select_values("SELECT version FROM #{sm_table}")
- migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
+
+ ActiveRecord::SchemaMigration.order('version').all.map { |sm|
+ "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
+ }.join "\n\n"
end
# Should not be called normally, but this operation is non-destructive.
# The migrations module handles this automatically.
def initialize_schema_migrations_table
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
-
- unless table_exists?(sm_table)
- create_table(sm_table, :id => false) do |schema_migrations_table|
- schema_migrations_table.column :version, :string, :null => false
- end
- add_index sm_table, :version, :unique => true,
- :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
-
- # Backwards-compatibility: if we find schema_info, assume we've
- # migrated up to that point:
- si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
-
- 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)
- drop_table(si_table)
- end
- end
+ ActiveRecord::SchemaMigration.create_table
end
def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
- migrations_paths = Array.wrap(migrations_paths)
+ migrations_paths = Array(migrations_paths)
version = version.to_i
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
@@ -520,9 +552,29 @@ module ActiveRecord
end
protected
+ def add_index_sort_order(option_strings, column_names, options = {})
+ if options.is_a?(Hash) && order = options[:order]
+ case order
+ when Hash
+ column_names.each {|name| option_strings[name] += " #{order[name].to_s.upcase}" if order.has_key?(name)}
+ when String
+ column_names.each {|name| option_strings[name] += " #{order.upcase}"}
+ end
+ end
+
+ return option_strings
+ end
+
# Overridden by the mysql adapter for supporting index lengths
def quoted_columns_for_index(column_names, options = {})
- column_names.map {|name| quote_column_name(name) }
+ option_strings = Hash[column_names.map {|name| [name, '']}]
+
+ # add index sort order if supported
+ if supports_index_sort_order?
+ option_strings = add_index_sort_order(option_strings, column_names, options)
+ end
+
+ column_names.map {|name| quote_column_name(name) + option_strings[name]}
end
def options_include_default?(options)
@@ -530,12 +582,15 @@ module ActiveRecord
end
def add_index_options(table_name, column_name, options = {})
- column_names = Array.wrap(column_name)
+ column_names = Array(column_name)
index_name = index_name(table_name, :column => column_names)
if Hash === options # legacy support, since this param was a string
index_type = options[:unique] ? "UNIQUE" : ""
index_name = options[:name].to_s if options.key?(:name)
+ if supports_partial_index?
+ index_options = options[:where] ? " WHERE #{options[:where]}" : ""
+ end
else
index_type = options
end
@@ -548,7 +603,7 @@ module ActiveRecord
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
- [index_name, index_type, index_columns]
+ [index_name, index_type, index_columns, index_options]
end
def index_name_for_remove(table_name, options = {})
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 4c3a8f7233..07c2bc44d9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -3,29 +3,34 @@ require 'bigdecimal'
require 'bigdecimal/util'
require 'active_support/core_ext/benchmark'
require 'active_support/deprecation'
+require 'active_record/connection_adapters/schema_cache'
+require 'monitor'
module ActiveRecord
module ConnectionAdapters # :nodoc:
extend ActiveSupport::Autoload
autoload :Column
+ autoload :ConnectionSpecification
- autoload_under 'abstract' do
- autoload :IndexDefinition, 'active_record/connection_adapters/abstract/schema_definitions'
- autoload :ColumnDefinition, 'active_record/connection_adapters/abstract/schema_definitions'
- autoload :TableDefinition, 'active_record/connection_adapters/abstract/schema_definitions'
- autoload :Table, 'active_record/connection_adapters/abstract/schema_definitions'
+ autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
+ autoload :IndexDefinition
+ autoload :ColumnDefinition
+ autoload :TableDefinition
+ autoload :Table
+ end
+
+ autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
+ autoload :ConnectionHandler
+ autoload :ConnectionManagement
+ end
+ autoload_under 'abstract' do
autoload :SchemaStatements
autoload :DatabaseStatements
autoload :DatabaseLimits
autoload :Quoting
-
autoload :ConnectionPool
- autoload :ConnectionHandler, 'active_record/connection_adapters/abstract/connection_pool'
- autoload :ConnectionManagement, 'active_record/connection_adapters/abstract/connection_pool'
- autoload :ConnectionSpecification
-
autoload :QueryCache
end
@@ -47,39 +52,44 @@ module ActiveRecord
include DatabaseLimits
include QueryCache
include ActiveSupport::Callbacks
+ include MonitorMixin
define_callbacks :checkout, :checkin
- attr_accessor :visitor
-
- def initialize(connection, logger = nil) #:nodoc:
- @active = nil
- @connection, @logger = connection, logger
+ attr_accessor :visitor, :pool
+ attr_reader :schema_cache, :last_use, :in_use
+ alias :in_use? :in_use
+
+ def initialize(connection, logger = nil, pool = nil) #:nodoc:
+ super()
+
+ @active = nil
+ @connection = connection
+ @in_use = false
+ @instrumenter = ActiveSupport::Notifications.instrumenter
+ @last_use = false
+ @logger = logger
+ @open_transactions = 0
+ @pool = pool
+ @query_cache = Hash.new { |h,sql| h[sql] = {} }
@query_cache_enabled = false
- @query_cache = Hash.new { |h,sql| h[sql] = {} }
- @open_transactions = 0
- @instrumenter = ActiveSupport::Notifications.instrumenter
- @visitor = nil
- end
-
- # Returns a visitor instance for this adaptor, which conforms to the Arel::ToSql interface
- def self.visitor_for(pool) # :nodoc:
- adapter = pool.spec.config[:adapter]
-
- if Arel::Visitors::VISITORS[adapter]
- ActiveSupport::Deprecation.warn(
- "Arel::Visitors::VISITORS is deprecated and will be removed. Database adapters " \
- "should define a visitor_for method which returns the appropriate visitor for " \
- "the database. For example, MysqlAdapter.visitor_for(pool) returns " \
- "Arel::Visitors::MySQL.new(pool)."
- )
-
- Arel::Visitors::VISITORS[adapter].new(pool)
- else
- Arel::Visitors::ToSql.new(pool)
+ @schema_cache = SchemaCache.new self
+ @visitor = nil
+ end
+
+ def lease
+ synchronize do
+ unless in_use
+ @in_use = true
+ @last_use = Time.now
+ end
end
end
+ def expire
+ @in_use = false
+ end
+
# Returns the human-readable name of the adapter. Use mixed case - one
# can always use downcase if needed.
def adapter_name
@@ -130,6 +140,22 @@ module ActiveRecord
false
end
+ # Does this adapter support index sort order?
+ def supports_index_sort_order?
+ false
+ end
+
+ # Does this adapter support partial indices?
+ def supports_partial_index?
+ false
+ end
+
+ # Does this adapter support explain? As of this writing sqlite3,
+ # mysql2, and postgresql are the only ones that do.
+ def supports_explain?
+ false
+ end
+
# QUOTING ==================================================
# Override to return the quoted table name. Defaults to column quoting.
@@ -246,6 +272,11 @@ module ActiveRecord
"active_record_#{open_transactions}"
end
+ # Check the connection back in to the connection pool
+ def close
+ pool.checkin self
+ end
+
protected
def log(sql, name = "SQL", binds = [])
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index dd573ba569..e1dad5b166 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -127,10 +127,7 @@ module ActiveRecord
super(connection, logger)
@connection_options, @config = connection_options, config
@quoted_column_names, @quoted_table_names = {}, {}
- end
-
- def self.visitor_for(pool) # :nodoc:
- Arel::Visitors::MySQL.new(pool)
+ @visitor = Arel::Visitors::MySQL.new self
end
def adapter_name #:nodoc:
@@ -155,6 +152,12 @@ module ActiveRecord
true
end
+ # Technically MySQL allows to create indexes with the sort order syntax
+ # but at the moment (5.5) it doesn't yet implement them
+ def supports_index_sort_order?
+ true
+ end
+
def native_database_types
NATIVE_DATABASE_TYPES
end
@@ -309,11 +312,11 @@ module ActiveRecord
sql = "SHOW TABLES"
end
- select_all(sql).map do |table|
+ select_all(sql).map { |table|
table.delete('Table_type')
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
exec_without_stmt(sql).first['Create Table'] + ";\n\n"
- end.join("")
+ }.join
end
# Drops the database specified on the +name+ attribute
@@ -360,8 +363,10 @@ module ActiveRecord
show_variable 'collation_database'
end
- def tables(name = nil, database = nil) #:nodoc:
- sql = ["SHOW TABLES", database].compact.join(' IN ')
+ def tables(name = nil, database = nil, like = nil) #:nodoc:
+ sql = "SHOW TABLES "
+ sql << "IN #{database} " if database
+ sql << "LIKE #{quote(like)}" if like
execute_and_free(sql, 'SCHEMA') do |result|
result.collect { |field| field.first }
@@ -369,7 +374,8 @@ module ActiveRecord
end
def table_exists?(name)
- return true if super
+ return false unless name
+ return true if tables(nil, nil, name).any?
name = name.to_s
schema, table = name.split('.', 2)
@@ -379,7 +385,7 @@ module ActiveRecord
schema = nil
end
- tables(nil, schema).include? table
+ tables(nil, schema, table).any?
end
# Returns an array of indexes for the given table.
@@ -403,7 +409,7 @@ module ActiveRecord
end
# Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name, name = nil)#:nodoc:
+ def columns(table_name)#:nodoc:
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
execute_and_free(sql, 'SCHEMA') do |result|
each_hash(result).map do |field|
@@ -496,9 +502,14 @@ module ActiveRecord
# Returns a table's primary key and belonging sequence.
def pk_and_sequence_for(table)
- execute_and_free("DESCRIBE #{quote_table_name(table)}", 'SCHEMA') do |result|
- keys = each_hash(result).select { |row| row[:Key] == 'PRI' }.map { |row| row[:Field] }
- keys.length == 1 ? [keys.first, nil] : nil
+ execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
+ create_table = each_hash(result).first[:"Create Table"]
+ if create_table.to_s =~ /PRIMARY KEY\s+\((.+)\)/
+ keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
+ keys.length == 1 ? [keys.first, nil] : nil
+ else
+ nil
+ end
end
end
@@ -526,17 +537,29 @@ module ActiveRecord
protected
+ def add_index_length(option_strings, column_names, options = {})
+ if options.is_a?(Hash) && length = options[:length]
+ case length
+ when Hash
+ column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
+ when Fixnum
+ column_names.each {|name| option_strings[name] += "(#{length})"}
+ end
+ end
+
+ return option_strings
+ end
+
def quoted_columns_for_index(column_names, options = {})
- length = options[:length] if options.is_a?(Hash)
+ option_strings = Hash[column_names.map {|name| [name, '']}]
- case length
- when Hash
- column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
- when Fixnum
- column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
- else
- column_names.map {|name| quote_column_name(name) }
- end
+ # add index length
+ option_strings = add_index_length(option_strings, column_names, options)
+
+ # add index sort order
+ option_strings = add_index_sort_order(option_strings, column_names, options)
+
+ column_names.map {|name| quote_column_name(name) + option_strings[name]}
end
def translate_exception(exception, message)
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index a7856539b7..78e54c4c9b 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -5,8 +5,8 @@ module ActiveRecord
module ConnectionAdapters
# An abstract definition of a column in a table.
class Column
- TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
- FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
module Format
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
@@ -66,6 +66,25 @@ module ActiveRecord
end
end
+ def binary?
+ type == :binary
+ end
+
+ # Casts a Ruby value to something appropriate for writing to the database.
+ def type_cast_for_write(value)
+ return value unless number?
+
+ if value == false
+ 0
+ elsif value == true
+ 1
+ elsif value.is_a?(String) && value.blank?
+ nil
+ else
+ value
+ end
+ end
+
# Casts value (which is a String) to an appropriate instance.
def type_cast(value)
return nil if value.nil?
@@ -80,7 +99,7 @@ module ActiveRecord
when :decimal then klass.value_to_decimal(value)
when :datetime, :timestamp then klass.string_to_time(value)
when :time then klass.string_to_dummy_time(value)
- when :date then klass.string_to_date(value)
+ when :date then klass.value_to_date(value)
when :binary then klass.binary_to_string(value)
when :boolean then klass.value_to_boolean(value)
else value
@@ -97,9 +116,10 @@ module ActiveRecord
when :decimal then "#{klass}.value_to_decimal(#{var_name})"
when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
when :time then "#{klass}.string_to_dummy_time(#{var_name})"
- when :date then "#{klass}.string_to_date(#{var_name})"
+ when :date then "#{klass}.value_to_date(#{var_name})"
when :binary then "#{klass}.binary_to_string(#{var_name})"
when :boolean then "#{klass}.value_to_boolean(#{var_name})"
+ when :hstore then "#{klass}.string_to_hstore(#{var_name})"
else var_name
end
end
@@ -132,11 +152,15 @@ module ActiveRecord
value
end
- def string_to_date(string)
- return string unless string.is_a?(String)
- return nil if string.empty?
-
- fast_string_to_date(string) || fallback_string_to_date(string)
+ def value_to_date(value)
+ if value.is_a?(String)
+ return nil if value.empty?
+ fast_string_to_date(value) || fallback_string_to_date(value)
+ elsif value.respond_to?(:to_date)
+ value.to_date
+ else
+ value
+ end
end
def string_to_time(string)
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
new file mode 100644
index 0000000000..8491d42b86
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -0,0 +1,83 @@
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionSpecification #:nodoc:
+ attr_reader :config, :adapter_method
+
+ def initialize(config, adapter_method)
+ @config, @adapter_method = config, adapter_method
+ end
+
+ def initialize_dup(original)
+ @config = original.config.dup
+ end
+
+ ##
+ # Builds a ConnectionSpecification from user input
+ class Resolver # :nodoc:
+ attr_reader :config, :klass, :configurations
+
+ def initialize(config, configurations)
+ @config = config
+ @configurations = configurations
+ end
+
+ def spec
+ case config
+ when nil
+ raise AdapterNotSpecified unless defined?(Rails.env)
+ resolve_string_connection Rails.env
+ when Symbol, String
+ resolve_string_connection config.to_s
+ when Hash
+ resolve_hash_connection config
+ end
+ end
+
+ private
+ def resolve_string_connection(spec) # :nodoc:
+ hash = configurations.fetch(spec) do |k|
+ connection_url_to_hash(k)
+ end
+
+ raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
+
+ resolve_hash_connection hash
+ end
+
+ def resolve_hash_connection(spec) # :nodoc:
+ spec = spec.symbolize_keys
+
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
+
+ begin
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
+ rescue LoadError => e
+ raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace
+ end
+
+ adapter_method = "#{spec[:adapter]}_connection"
+
+ ConnectionSpecification.new(spec, adapter_method)
+ end
+
+ def connection_url_to_hash(url) # :nodoc:
+ config = URI.parse url
+ adapter = config.scheme
+ adapter = "postgresql" if adapter == "postgres"
+ spec = { :adapter => adapter,
+ :username => config.user,
+ :password => config.password,
+ :port => config.port,
+ :database => config.path.sub(%r{^/},""),
+ :host => config.host }
+ spec.reject!{ |_,value| !value }
+ if config.query
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
+ spec.merge!(options)
+ end
+ spec
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 971f3c35f3..321d500da2 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -1,12 +1,12 @@
require 'active_record/connection_adapters/abstract_mysql_adapter'
-gem 'mysql2', '~> 0.3.6'
+gem 'mysql2', '~> 0.3.10'
require 'mysql2'
module ActiveRecord
- class Base
+ module ConnectionHandling
# Establishes a connection to the database that's used by all Active Record objects.
- def self.mysql2_connection(config)
+ def mysql2_connection(config)
config[:username] = 'root' if config[:username].nil?
if Mysql2::Client.const_defined? :FOUND_ROWS
@@ -35,6 +35,10 @@ module ActiveRecord
configure_connection
end
+ def supports_explain?
+ true
+ end
+
# HELPER METHODS ===========================================
def each_hash(result) # :nodoc:
@@ -76,6 +80,7 @@ module ActiveRecord
disconnect!
connect
end
+ alias :reset! :reconnect!
# Disconnects from the database if already connected.
# Otherwise, this method does nothing.
@@ -86,12 +91,81 @@ module ActiveRecord
end
end
- def reset!
- disconnect!
- connect
+ # DATABASE STATEMENTS ======================================
+
+ def explain(arel, binds = [])
+ sql = "EXPLAIN #{to_sql(arel)}"
+ start = Time.now
+ result = exec_query(sql, 'EXPLAIN', binds)
+ elapsed = Time.now - start
+
+ ExplainPrettyPrinter.new.pp(result, elapsed)
end
- # DATABASE STATEMENTS ======================================
+ class ExplainPrettyPrinter # :nodoc:
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
+ # MySQL shell:
+ #
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
+ # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
+ # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+ # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
+ # 2 rows in set (0.00 sec)
+ #
+ # This is an exercise in Ruby hyperrealism :).
+ def pp(result, elapsed)
+ widths = compute_column_widths(result)
+ separator = build_separator(widths)
+
+ pp = []
+
+ pp << separator
+ pp << build_cells(result.columns, widths)
+ pp << separator
+
+ result.rows.each do |row|
+ pp << build_cells(row, widths)
+ end
+
+ pp << separator
+ pp << build_footer(result.rows.length, elapsed)
+
+ pp.join("\n") + "\n"
+ end
+
+ private
+
+ def compute_column_widths(result)
+ [].tap do |widths|
+ result.columns.each_with_index do |column, i|
+ cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
+ widths << cells_in_column.map(&:length).max
+ end
+ end
+ end
+
+ def build_separator(widths)
+ padding = 1
+ '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
+ end
+
+ def build_cells(items, widths)
+ cells = []
+ items.each_with_index do |item, i|
+ item = 'NULL' if item.nil?
+ justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
+ cells << item.to_s.send(justifier, widths[i])
+ end
+ '| ' + cells.join(' | ') + ' |'
+ end
+
+ def build_footer(nrows, elapsed)
+ rows_label = nrows == 1 ? 'row' : 'rows'
+ "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
+ end
+ end
# FIXME: re-enable the following once a "better" query_cache solution is in core
#
@@ -147,7 +221,7 @@ module ActiveRecord
# column values as values.
def select(sql, name = nil, binds = [])
binds = binds.dup
- exec_query(sql.gsub("\0") { quote(*binds.shift.reverse) }, name).to_a
+ exec_query(sql.gsub("\0") { quote(*binds.shift.reverse) }, name)
end
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index f092edecda..724dbff1f0 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -18,9 +18,9 @@ class Mysql
end
module ActiveRecord
- class Base
+ module ConnectionHandling
# Establishes a connection to the database that's used by all Active Record objects.
- def self.mysql_connection(config) # :nodoc:
+ def mysql_connection(config) # :nodoc:
config = config.symbolize_keys
host = config[:host]
port = config[:port]
@@ -119,7 +119,7 @@ module ActiveRecord
private
def cache
- @cache[$$]
+ @cache[Process.pid]
end
end
@@ -224,52 +224,48 @@ module ActiveRecord
@statements.clear
end
- if "<3".respond_to?(:encode)
- # Taken from here:
- # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
- # Author: TOMITA Masahiro <tommy@tmtm.org>
- ENCODINGS = {
- "armscii8" => nil,
- "ascii" => Encoding::US_ASCII,
- "big5" => Encoding::Big5,
- "binary" => Encoding::ASCII_8BIT,
- "cp1250" => Encoding::Windows_1250,
- "cp1251" => Encoding::Windows_1251,
- "cp1256" => Encoding::Windows_1256,
- "cp1257" => Encoding::Windows_1257,
- "cp850" => Encoding::CP850,
- "cp852" => Encoding::CP852,
- "cp866" => Encoding::IBM866,
- "cp932" => Encoding::Windows_31J,
- "dec8" => nil,
- "eucjpms" => Encoding::EucJP_ms,
- "euckr" => Encoding::EUC_KR,
- "gb2312" => Encoding::EUC_CN,
- "gbk" => Encoding::GBK,
- "geostd8" => nil,
- "greek" => Encoding::ISO_8859_7,
- "hebrew" => Encoding::ISO_8859_8,
- "hp8" => nil,
- "keybcs2" => nil,
- "koi8r" => Encoding::KOI8_R,
- "koi8u" => Encoding::KOI8_U,
- "latin1" => Encoding::ISO_8859_1,
- "latin2" => Encoding::ISO_8859_2,
- "latin5" => Encoding::ISO_8859_9,
- "latin7" => Encoding::ISO_8859_13,
- "macce" => Encoding::MacCentEuro,
- "macroman" => Encoding::MacRoman,
- "sjis" => Encoding::SHIFT_JIS,
- "swe7" => nil,
- "tis620" => Encoding::TIS_620,
- "ucs2" => Encoding::UTF_16BE,
- "ujis" => Encoding::EucJP_ms,
- "utf8" => Encoding::UTF_8,
- "utf8mb4" => Encoding::UTF_8,
- }
- else
- ENCODINGS = Hash.new { |h,k| h[k] = k }
- end
+ # Taken from here:
+ # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
+ # Author: TOMITA Masahiro <tommy@tmtm.org>
+ ENCODINGS = {
+ "armscii8" => nil,
+ "ascii" => Encoding::US_ASCII,
+ "big5" => Encoding::Big5,
+ "binary" => Encoding::ASCII_8BIT,
+ "cp1250" => Encoding::Windows_1250,
+ "cp1251" => Encoding::Windows_1251,
+ "cp1256" => Encoding::Windows_1256,
+ "cp1257" => Encoding::Windows_1257,
+ "cp850" => Encoding::CP850,
+ "cp852" => Encoding::CP852,
+ "cp866" => Encoding::IBM866,
+ "cp932" => Encoding::Windows_31J,
+ "dec8" => nil,
+ "eucjpms" => Encoding::EucJP_ms,
+ "euckr" => Encoding::EUC_KR,
+ "gb2312" => Encoding::EUC_CN,
+ "gbk" => Encoding::GBK,
+ "geostd8" => nil,
+ "greek" => Encoding::ISO_8859_7,
+ "hebrew" => Encoding::ISO_8859_8,
+ "hp8" => nil,
+ "keybcs2" => nil,
+ "koi8r" => Encoding::KOI8_R,
+ "koi8u" => Encoding::KOI8_U,
+ "latin1" => Encoding::ISO_8859_1,
+ "latin2" => Encoding::ISO_8859_2,
+ "latin5" => Encoding::ISO_8859_9,
+ "latin7" => Encoding::ISO_8859_13,
+ "macce" => Encoding::MacCentEuro,
+ "macroman" => Encoding::MacRoman,
+ "sjis" => Encoding::SHIFT_JIS,
+ "swe7" => nil,
+ "tis620" => Encoding::TIS_620,
+ "ucs2" => Encoding::UTF_16BE,
+ "ujis" => Encoding::EucJP_ms,
+ "utf8" => Encoding::UTF_8,
+ "utf8mb4" => Encoding::UTF_8,
+ }
# Get the client encoding for this database
def client_encoding
@@ -412,7 +408,7 @@ module ActiveRecord
def select(sql, name = nil, binds = [])
@connection.query_with_result = true
- rows = exec_query(sql, name, binds).to_a
+ rows = exec_query(sql, name, binds)
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
new file mode 100644
index 0000000000..c82afc232c
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -0,0 +1,243 @@
+require 'active_record/connection_adapters/abstract_adapter'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLAdapter < AbstractAdapter
+ module OID
+ class Type
+ def type; end
+
+ def type_cast_for_write(value)
+ value
+ end
+ end
+
+ class Identity < Type
+ def type_cast(value)
+ value
+ end
+ end
+
+ class Bytea < Type
+ def type_cast(value)
+ PGconn.unescape_bytea value
+ end
+ end
+
+ class Money < Type
+ def type_cast(value)
+ return if value.nil?
+
+ # Because money output is formatted according to the locale, there are two
+ # cases to consider (note the decimal separators):
+ # (1) $12,345,678.12
+ # (2) $12.345.678,12
+
+ case value
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
+ value.gsub!(/[^-\d.]/, '')
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
+ value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
+ end
+
+ ConnectionAdapters::Column.value_to_decimal value
+ end
+ end
+
+ class Vector < Type
+ attr_reader :delim, :subtype
+
+ # +delim+ corresponds to the `typdelim` column in the pg_types
+ # table. +subtype+ is derived from the `typelem` column in the
+ # pg_types table.
+ def initialize(delim, subtype)
+ @delim = delim
+ @subtype = subtype
+ end
+
+ # FIXME: this should probably split on +delim+ and use +subtype+
+ # to cast the values. Unfortunately, the current Rails behavior
+ # is to just return the string.
+ def type_cast(value)
+ value
+ end
+ end
+
+ class Integer < Type
+ def type_cast(value)
+ return if value.nil?
+
+ value.to_i rescue value ? 1 : 0
+ end
+ end
+
+ class Boolean < Type
+ def type_cast(value)
+ return if value.nil?
+
+ ConnectionAdapters::Column.value_to_boolean value
+ end
+ end
+
+ class Timestamp < Type
+ def type; :timestamp; end
+
+ def type_cast(value)
+ return if value.nil?
+
+ # FIXME: probably we can improve this since we know it is PG
+ # specific
+ ConnectionAdapters::PostgreSQLColumn.string_to_time value
+ end
+ end
+
+ class Date < Type
+ def type; :datetime; end
+
+ def type_cast(value)
+ return if value.nil?
+
+ # FIXME: probably we can improve this since we know it is PG
+ # specific
+ ConnectionAdapters::Column.value_to_date value
+ end
+ end
+
+ class Time < Type
+ def type_cast(value)
+ return if value.nil?
+
+ # FIXME: probably we can improve this since we know it is PG
+ # specific
+ ConnectionAdapters::Column.string_to_dummy_time value
+ end
+ end
+
+ class Float < Type
+ def type_cast(value)
+ return if value.nil?
+
+ value.to_f
+ end
+ end
+
+ class Decimal < Type
+ def type_cast(value)
+ return if value.nil?
+
+ ConnectionAdapters::Column.value_to_decimal value
+ end
+ end
+
+ class Hstore < Type
+ def type_cast(value)
+ return if value.nil?
+
+ ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
+ end
+ end
+
+ class TypeMap
+ def initialize
+ @mapping = {}
+ end
+
+ def []=(oid, type)
+ @mapping[oid] = type
+ end
+
+ def [](oid)
+ @mapping[oid]
+ end
+
+ def key?(oid)
+ @mapping.key? oid
+ end
+
+ def fetch(ftype, fmod)
+ # The type for the numeric depends on the width of the field,
+ # so we'll do something special here.
+ #
+ # When dealing with decimal columns:
+ #
+ # places after decimal = fmod - 4 & 0xffff
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
+ if ftype == 1700 && (fmod - 4 & 0xffff).zero?
+ ftype = 23
+ end
+
+ @mapping.fetch(ftype) { |oid| yield oid, fmod }
+ end
+ end
+
+ TYPE_MAP = TypeMap.new # :nodoc:
+
+ # When the PG adapter connects, the pg_type table is queried. The
+ # key of this hash maps to the `typname` column from the table.
+ # TYPE_MAP is then dynamically built with oids as the key and type
+ # objects as values.
+ NAMES = Hash.new { |h,k| # :nodoc:
+ h[k] = OID::Identity.new
+ }
+
+ # Register an OID type named +name+ with a typcasting object in
+ # +type+. +name+ should correspond to the `typname` column in
+ # the `pg_type` table.
+ def self.register_type(name, type)
+ NAMES[name] = type
+ end
+
+ # Alias the +old+ type to the +new+ type.
+ def self.alias_type(new, old)
+ NAMES[new] = NAMES[old]
+ end
+
+ # Is +name+ a registered type?
+ def self.registered_type?(name)
+ NAMES.key? name
+ end
+
+ register_type 'int2', OID::Integer.new
+ alias_type 'int4', 'int2'
+ alias_type 'int8', 'int2'
+ alias_type 'oid', 'int2'
+
+ register_type 'numeric', OID::Decimal.new
+ register_type 'text', OID::Identity.new
+ alias_type 'varchar', 'text'
+ alias_type 'char', 'text'
+ alias_type 'bpchar', 'text'
+ alias_type 'xml', 'text'
+
+ # FIXME: why are we keeping these types as strings?
+ alias_type 'tsvector', 'text'
+ alias_type 'interval', 'text'
+ alias_type 'cidr', 'text'
+ alias_type 'inet', 'text'
+ alias_type 'macaddr', 'text'
+ alias_type 'bit', 'text'
+ alias_type 'varbit', 'text'
+
+ # FIXME: I don't think this is correct. We should probably be returning a parsed date,
+ # but the tests pass with a string returned.
+ register_type 'timestamptz', OID::Identity.new
+
+ register_type 'money', OID::Money.new
+ register_type 'bytea', OID::Bytea.new
+ register_type 'bool', OID::Boolean.new
+
+ register_type 'float4', OID::Float.new
+ alias_type 'float8', 'float4'
+
+ register_type 'timestamp', OID::Timestamp.new
+ register_type 'date', OID::Date.new
+ register_type 'time', OID::Time.new
+
+ register_type 'path', OID::Identity.new
+ register_type 'polygon', OID::Identity.new
+ register_type 'circle', OID::Identity.new
+ register_type 'hstore', OID::Hstore.new
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index e8a43e7bce..d04f04b201 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,30 +1,33 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_support/core_ext/object/blank'
require 'active_record/connection_adapters/statement_pool'
+require 'active_record/connection_adapters/postgresql/oid'
# Make sure we're using pg high enough for PGResult#values
gem 'pg', '~> 0.11'
require 'pg'
module ActiveRecord
- class Base
+ module ConnectionHandling
# Establishes a connection to the database that's used by all Active Record objects
- def self.postgresql_connection(config) # :nodoc:
- config = config.symbolize_keys
- host = config[:host]
- port = config[:port] || 5432
- username = config[:username].to_s if config[:username]
- password = config[:password].to_s if config[:password]
+ def postgresql_connection(config) # :nodoc:
+ conn_params = config.symbolize_keys
- if config.key?(:database)
- database = config[:database]
- else
- raise ArgumentError, "No database specified. Missing argument: database."
+ # Forward any unused config params to PGconn.connect.
+ [:statement_limit, :encoding, :min_messages, :schema_search_path,
+ :schema_order, :adapter, :pool, :wait_timeout, :template,
+ :reaping_frequency].each do |key|
+ conn_params.delete key
end
+ conn_params.delete_if { |k,v| v.nil? }
+
+ # Map ActiveRecords param names to PGs.
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
# The postgres drivers don't allow the creation of an unconnected PGconn object,
# so just pass a nil connection object for the time being.
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
end
end
@@ -32,7 +35,8 @@ module ActiveRecord
# PostgreSQL-specific extensions to column definitions in a table.
class PostgreSQLColumn < Column #:nodoc:
# Instantiates a new PostgreSQL column definition in a table.
- def initialize(name, default, sql_type = nil, null = true)
+ def initialize(name, default, oid_type, sql_type = nil, null = true)
+ @oid_type = oid_type
super(name, self.class.extract_value_from_default(default), sql_type, null)
end
@@ -49,161 +53,218 @@ module ActiveRecord
super
end
end
- end
- # :startdoc:
- private
- def extract_limit(sql_type)
- case sql_type
- when /^bigint/i; 8
- when /^smallint/i; 2
- else super
+ def hstore_to_string(object)
+ if Hash === object
+ object.map { |k,v|
+ "#{escape_hstore(k)}=>#{escape_hstore(v)}"
+ }.join ','
+ else
+ object
end
end
- # Extracts the scale from PostgreSQL-specific data types.
- def extract_scale(sql_type)
- # Money type has a fixed scale of 2.
- sql_type =~ /^money/ ? 2 : super
+ def string_to_hstore(string)
+ if string.nil?
+ nil
+ elsif String === string
+ Hash[string.scan(HstorePair).map { |k,v|
+ v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
+ k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
+ [k,v]
+ }]
+ else
+ string
+ end
+ end
+
+ private
+ HstorePair = begin
+ quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
+ unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
+ /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
end
- # Extracts the precision from PostgreSQL-specific data types.
- def extract_precision(sql_type)
- if sql_type == 'money'
- self.class.money_precision
+ def escape_hstore(value)
+ value.nil? ? 'NULL'
+ : value =~ /[=\s,>]/ ? '"%s"' % value.gsub(/(["\\])/, '\\\\\1')
+ : value == "" ? '""'
+ : value.to_s.gsub(/(["\\])/, '\\\\\1')
+ end
+ end
+ # :startdoc:
+
+ # Extracts the value from a PostgreSQL column default definition.
+ def self.extract_value_from_default(default)
+ # This is a performance optimization for Ruby 1.9.2 in development.
+ # If the value is nil, we return nil straight away without checking
+ # the regular expressions. If we check each regular expression,
+ # Regexp#=== will call NilClass#to_str, which will trigger
+ # method_missing (defined by whiny nil in ActiveSupport) which
+ # makes this method very very slow.
+ return default unless default
+
+ case default
+ # Numeric types
+ when /\A\(?(-?\d+(\.\d*)?\)?)\z/
+ $1
+ # Character types
+ when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
+ $1
+ # Character types (8.1 formatting)
+ when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
+ $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
+ # Binary data types
+ when /\A'(.*)'::bytea\z/m
+ $1
+ # Date/time types
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
+ $1
+ when /\A'(.*)'::interval\z/
+ $1
+ # Boolean type
+ when 'true'
+ true
+ when 'false'
+ false
+ # Geometric types
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
+ $1
+ # Network address types
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
+ $1
+ # Bit string types
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
+ $1
+ # XML type
+ when /\A'(.*)'::xml\z/m
+ $1
+ # Arrays
+ when /\A'(.*)'::"?\D+"?\[\]\z/
+ $1
+ # Hstore
+ when /\A'(.*)'::hstore\z/
+ $1
+ # Object identifier types
+ when /\A-?\d+\z/
+ $1
else
- super
- end
+ # Anything else is blank, some user type, or some function
+ # and we can't know the value of that, so return nil.
+ nil
end
+ end
- # Maps PostgreSQL-specific data types to logical Rails types.
- def simplified_type(field_type)
- case field_type
- # Numeric and monetary types
- when /^(?:real|double precision)$/
- :float
- # Monetary types
- when 'money'
- :decimal
- # Character types
- when /^(?:character varying|bpchar)(?:\(\d+\))?$/
- :string
- # Binary data types
- when 'bytea'
- :binary
- # Date/time types
- when /^timestamp with(?:out)? time zone$/
- :datetime
- when 'interval'
- :string
- # Geometric types
- when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
- :string
- # Network address types
- when /^(?:cidr|inet|macaddr)$/
- :string
- # Bit strings
- when /^bit(?: varying)?(?:\(\d+\))?$/
- :string
- # XML type
- when 'xml'
- :xml
- # tsvector type
- when 'tsvector'
- :tsvector
- # Arrays
- when /^\D+\[\]$/
- :string
- # Object identifier types
- when 'oid'
- :integer
- # UUID type
- when 'uuid'
- :string
- # Small and big integer types
- when /^(?:small|big)int$/
- :integer
- # Pass through all types that are not specific to PostgreSQL.
- else
- super
- end
+ def type_cast(value)
+ return if value.nil?
+ return super if encoded?
+
+ @oid_type.type_cast value
+ end
+
+ private
+ def extract_limit(sql_type)
+ case sql_type
+ when /^bigint/i; 8
+ when /^smallint/i; 2
+ else super
end
+ end
- # Extracts the value from a PostgreSQL column default definition.
- def self.extract_value_from_default(default)
- case default
- # This is a performance optimization for Ruby 1.9.2 in development.
- # If the value is nil, we return nil straight away without checking
- # the regular expressions. If we check each regular expression,
- # Regexp#=== will call NilClass#to_str, which will trigger
- # method_missing (defined by whiny nil in ActiveSupport) which
- # makes this method very very slow.
- when NilClass
- nil
- # Numeric types
- when /\A\(?(-?\d+(\.\d*)?\)?)\z/
- $1
- # Character types
- when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
- $1
- # Character types (8.1 formatting)
- when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
- $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
- # Binary data types
- when /\A'(.*)'::bytea\z/m
- $1
- # Date/time types
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
- $1
- when /\A'(.*)'::interval\z/
- $1
- # Boolean type
- when 'true'
- true
- when 'false'
- false
- # Geometric types
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
- $1
- # Network address types
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
- $1
- # Bit string types
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
- $1
- # XML type
- when /\A'(.*)'::xml\z/m
- $1
- # Arrays
- when /\A'(.*)'::"?\D+"?\[\]\z/
- $1
- # Object identifier types
- when /\A-?\d+\z/
- $1
- else
- # Anything else is blank, some user type, or some function
- # and we can't know the value of that, so return nil.
- nil
- end
+ # Extracts the scale from PostgreSQL-specific data types.
+ def extract_scale(sql_type)
+ # Money type has a fixed scale of 2.
+ sql_type =~ /^money/ ? 2 : super
+ end
+
+ # Extracts the precision from PostgreSQL-specific data types.
+ def extract_precision(sql_type)
+ if sql_type == 'money'
+ self.class.money_precision
+ else
+ super
end
+ end
+
+ # Maps PostgreSQL-specific data types to logical Rails types.
+ def simplified_type(field_type)
+ case field_type
+ # Numeric and monetary types
+ when /^(?:real|double precision)$/
+ :float
+ # Monetary types
+ when 'money'
+ :decimal
+ when 'hstore'
+ :hstore
+ # Character types
+ when /^(?:character varying|bpchar)(?:\(\d+\))?$/
+ :string
+ # Binary data types
+ when 'bytea'
+ :binary
+ # Date/time types
+ when /^timestamp with(?:out)? time zone$/
+ :datetime
+ when 'interval'
+ :string
+ # Geometric types
+ when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
+ :string
+ # Network address types
+ when /^(?:cidr|inet|macaddr)$/
+ :string
+ # Bit strings
+ when /^bit(?: varying)?(?:\(\d+\))?$/
+ :string
+ # XML type
+ when 'xml'
+ :xml
+ # tsvector type
+ when 'tsvector'
+ :tsvector
+ # Arrays
+ when /^\D+\[\]$/
+ :string
+ # Object identifier types
+ when 'oid'
+ :integer
+ # UUID type
+ when 'uuid'
+ :string
+ # Small and big integer types
+ when /^(?:small|big)int$/
+ :integer
+ # Pass through all types that are not specific to PostgreSQL.
+ else
+ super
+ end
+ end
end
- # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
- # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
#
# Options:
#
- # * <tt>:host</tt> - Defaults to "localhost".
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
+ # the default is to connect to localhost.
# * <tt>:port</tt> - Defaults to 5432.
- # * <tt>:username</tt> - Defaults to nothing.
- # * <tt>:password</tt> - Defaults to nothing.
- # * <tt>:database</tt> - The name of the database. No default, must be provided.
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
# <encoding></tt> call on the connection.
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
+ #
+ # Any further options are used as connection parameters to libpq. See
+ # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
+ # list of parameters.
+ #
+ # In addition, default connection parameters of libpq can be set per environment variables.
+ # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
def xml(*args)
@@ -215,6 +276,10 @@ module ActiveRecord
options = args.extract_options!
column(args[0], 'tsvector', options)
end
+
+ def hstore(name, options = {})
+ column(name, 'hstore', options)
+ end
end
ADAPTER_NAME = 'PostgreSQL'
@@ -233,7 +298,8 @@ module ActiveRecord
:binary => { :name => "bytea" },
:boolean => { :name => "boolean" },
:xml => { :name => "xml" },
- :tsvector => { :name => "tsvector" }
+ :tsvector => { :name => "tsvector" },
+ :hstore => { :name => "hstore" }
}
# Returns 'PostgreSQL' as adapter name for identification purposes.
@@ -247,6 +313,14 @@ module ActiveRecord
true
end
+ def supports_index_sort_order?
+ true
+ end
+
+ def supports_partial_index?
+ true
+ end
+
class StatementPool < ConnectionAdapters::StatementPool
def initialize(connection, max)
super
@@ -285,7 +359,7 @@ module ActiveRecord
private
def cache
- @cache[$$]
+ @cache[Process.pid]
end
def dealloc(key)
@@ -303,6 +377,7 @@ module ActiveRecord
def initialize(connection, logger, connection_parameters, config)
super(connection, logger)
@connection_parameters, @config = connection_parameters, config
+ @visitor = Arel::Visitors::PostgreSQL.new self
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@local_tz = nil
@@ -316,13 +391,10 @@ module ActiveRecord
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
end
+ initialize_type_map
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
end
- def self.visitor_for(pool) # :nodoc:
- Arel::Visitors::PostgreSQL.new(pool)
- end
-
# Clears the prepared statements cache.
def clear_cache!
@statements.clear
@@ -389,6 +461,11 @@ module ActiveRecord
true
end
+ # Returns true.
+ def supports_explain?
+ true
+ end
+
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
@table_alias_length ||= query('SHOW max_identifier_length')[0][0].to_i
@@ -398,14 +475,14 @@ module ActiveRecord
# Escapes binary strings for bytea input to the database.
def escape_bytea(value)
- @connection.escape_bytea(value) if value
+ PGconn.escape_bytea(value) if value
end
# Unescapes bytea output from a database to the binary string it represents.
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
# on escaped binary output from database drive.
def unescape_bytea(value)
- @connection.unescape_bytea(value) if value
+ PGconn.unescape_bytea(value) if value
end
# Quotes PostgreSQL-specific data types for SQL input.
@@ -413,6 +490,11 @@ module ActiveRecord
return super unless column
case value
+ when Hash
+ case column.sql_type
+ when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
+ else super
+ end
when Float
return super unless value.infinite? && column.type == :datetime
"'#{value.to_s.downcase}'"
@@ -444,6 +526,9 @@ module ActiveRecord
when String
return super unless 'bytea' == column.sql_type
{ :value => value, :format => 1 }
+ when Hash
+ return super unless 'hstore' == column.sql_type
+ PostgreSQLColumn.hstore_to_string(value)
else
super
end
@@ -513,6 +598,48 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
+ def explain(arel, binds = [])
+ sql = "EXPLAIN #{to_sql(arel)}"
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
+ end
+
+ class ExplainPrettyPrinter # :nodoc:
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
+ # PostgreSQL shell:
+ #
+ # QUERY PLAN
+ # ------------------------------------------------------------------------------
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
+ # Join Filter: (posts.user_id = users.id)
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
+ # Index Cond: (id = 1)
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
+ # Filter: (posts.user_id = 1)
+ # (6 rows)
+ #
+ def pp(result)
+ header = result.columns.first
+ lines = result.rows.map(&:first)
+
+ # We add 2 because there's one char of padding at both sides, note
+ # the extra hyphens in the example above.
+ width = [header, *lines].map(&:length).max + 2
+
+ pp = []
+
+ pp << header.center(width).rstrip
+ pp << '-' * width
+
+ pp += lines.map {|line| " #{line}"}
+
+ nrows = result.rows.length
+ rows_label = nrows == 1 ? 'row' : 'rows'
+ pp << "(#{nrows} #{rows_label})"
+
+ pp.join("\n") + "\n"
+ end
+ end
+
# Executes a SELECT query and returns an array of rows. Each row is an
# array of field values.
def select_rows(sql, name = nil)
@@ -597,12 +724,29 @@ module ActiveRecord
Arel.sql("$#{index + 1}")
end
+ class Result < ActiveRecord::Result
+ def initialize(columns, rows, column_types)
+ super(columns, rows)
+ @column_types = column_types
+ end
+ end
+
def exec_query(sql, name = 'SQL', binds = [])
log(sql, name, binds) do
result = binds.empty? ? exec_no_cache(sql, binds) :
exec_cache(sql, binds)
- ret = ActiveRecord::Result.new(result.fields, result_as_array(result))
+ types = {}
+ result.fields.each_with_index do |fname, i|
+ ftype = result.ftype i
+ fmod = result.fmod i
+ types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
+ warn "unknown OID: #{fname}(#{oid}) (#{sql})"
+ OID::Identity.new
+ }
+ end
+
+ ret = Result.new(result.fields, result.values, types)
result.clear
return ret
end
@@ -754,16 +898,15 @@ module ActiveRecord
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)
- schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
result = query(<<-SQL, name)
- SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
INNER JOIN pg_class i ON d.indexrelid = i.oid
WHERE i.relkind = 'i'
AND d.indisprimary = 'f'
AND t.relname = '#{table_name}'
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
ORDER BY i.relname
SQL
@@ -772,7 +915,8 @@ module ActiveRecord
index_name = row[0]
unique = row[1] == 't'
indkey = row[2].split(" ")
- oid = row[3]
+ inddef = row[3]
+ oid = row[4]
columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
SELECT a.attnum, a.attname
@@ -782,15 +926,24 @@ module ActiveRecord
SQL
column_names = columns.values_at(*indkey).compact
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names)
+
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
+ desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
+ orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
+ where = inddef.scan(/WHERE (.+)$/).flatten[0]
+
+ column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where)
end.compact
end
# Returns the list of all column definitions for a table.
- def columns(table_name, name = nil)
+ def columns(table_name)
# Limit, precision, and scale are all handled by the superclass.
- column_definitions(table_name).collect do |column_name, type, default, notnull|
- PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
+ column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
+ oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) {
+ OID::Identity.new
+ }
+ PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
end
end
@@ -826,7 +979,7 @@ module ActiveRecord
# Returns the active schema search path.
def schema_search_path
- @schema_search_path ||= query('SHOW search_path')[0][0]
+ @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
end
# Returns the current client message level.
@@ -1047,6 +1200,22 @@ module ActiveRecord
end
private
+ def initialize_type_map
+ result = execute('SELECT oid, typname, typelem, typdelim FROM pg_type', 'SCHEMA')
+ leaves, nodes = result.partition { |row| row['typelem'] == '0' }
+
+ # populate the leaf nodes
+ leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
+ OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
+ end
+
+ # populate composite types
+ nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
+ vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
+ OID::TYPE_MAP[row['oid'].to_i] = vector
+ end
+ end
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
def exec_no_cache(sql, binds)
@@ -1069,7 +1238,11 @@ module ActiveRecord
# prepared statements whose return value may have changed is
# FEATURE_NOT_SUPPORTED. Check here for more details:
# http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
- code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
+ begin
+ code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
+ rescue
+ raise e
+ end
if FEATURE_NOT_SUPPORTED == code
@statements.delete sql_key(sql)
retry
@@ -1105,7 +1278,7 @@ module ActiveRecord
# Connects to a PostgreSQL server and sets up the adapter depending on the
# connected server's characteristics.
def connect
- @connection = PGconn.connect(*@connection_parameters)
+ @connection = PGconn.connect(@connection_parameters)
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
@@ -1145,7 +1318,7 @@ module ActiveRecord
# Executes a SELECT query and returns the results, performing any data type
# conversions that are required to be performed here instead of in PostgreSQLColumn.
def select(sql, name = nil, binds = [])
- exec_query(sql, name, binds).to_a
+ exec_query(sql, name, binds)
end
def select_raw(sql, name = nil)
@@ -1176,7 +1349,7 @@ module ActiveRecord
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
exec_query(<<-end_sql, 'SCHEMA').rows
- SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
new file mode 100644
index 0000000000..962718da56
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -0,0 +1,50 @@
+module ActiveRecord
+ module ConnectionAdapters
+ class SchemaCache
+ attr_reader :columns, :columns_hash, :primary_keys, :tables
+ attr_reader :connection
+
+ def initialize(conn)
+ @connection = conn
+ @tables = {}
+
+ @columns = Hash.new do |h, table_name|
+ h[table_name] = conn.columns(table_name)
+ end
+
+ @columns_hash = Hash.new do |h, table_name|
+ h[table_name] = Hash[columns[table_name].map { |col|
+ [col.name, col]
+ }]
+ end
+
+ @primary_keys = Hash.new do |h, table_name|
+ h[table_name] = table_exists?(table_name) ? conn.primary_key(table_name) : nil
+ end
+ end
+
+ # A cached lookup for table existence.
+ def table_exists?(name)
+ return @tables[name] if @tables.key? name
+
+ @tables[name] = connection.table_exists?(name)
+ end
+
+ # Clears out internal caches
+ def clear!
+ @columns.clear
+ @columns_hash.clear
+ @primary_keys.clear
+ @tables.clear
+ end
+
+ # Clear out internal caches for table with +table_name+.
+ def clear_table_cache!(table_name)
+ @columns.delete table_name
+ @columns_hash.delete table_name
+ @primary_keys.delete table_name
+ @tables.delete table_name
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 0a0da0b5d3..ee5d10859c 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -1,12 +1,12 @@
require 'active_record/connection_adapters/sqlite_adapter'
-gem 'sqlite3', '~> 1.3.4'
+gem 'sqlite3', '~> 1.3.5'
require 'sqlite3'
module ActiveRecord
- class Base
+ module ConnectionHandling
# sqlite3 adapter reuses sqlite_connection.
- def self.sqlite3_connection(config) # :nodoc:
+ def sqlite3_connection(config) # :nodoc:
# Require database.
unless config[:database]
raise ArgumentError, "No database file specified. Missing argument: database"
@@ -47,11 +47,7 @@ module ActiveRecord
# Returns the current database encoding format as a string, eg: 'UTF-8'
def encoding
- if @connection.respond_to?(:encoding)
- @connection.encoding.to_s
- else
- @connection.execute('PRAGMA encoding')[0]['encoding']
- end
+ @connection.encoding.to_s
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 e0e957a12c..b73f7ae876 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,31 +1,15 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/statement_pool'
-require 'active_support/core_ext/string/encoding'
module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLiteColumn < Column #:nodoc:
class << self
- def string_to_binary(value)
- value.gsub(/\0|\%/n) do |b|
- case b
- when "\0" then "%00"
- when "%" then "%25"
- end
- end
- end
-
def binary_to_string(value)
- if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
+ if value.encoding != Encoding::ASCII_8BIT
value = value.force_encoding(Encoding::ASCII_8BIT)
end
-
- value.gsub(/%00|%25/n) do |b|
- case b
- when "%00" then "\0"
- when "%25" then "%"
- end
- end
+ value
end
end
end
@@ -89,10 +73,7 @@ module ActiveRecord
@statements = StatementPool.new(@connection,
config.fetch(:statement_limit) { 1000 })
@config = config
- end
-
- def self.visitor_for(pool) # :nodoc:
- Arel::Visitors::SQLite.new(pool)
+ @visitor = Arel::Visitors::SQLite.new self
end
def adapter_name #:nodoc:
@@ -125,6 +106,11 @@ module ActiveRecord
true
end
+ # Returns true.
+ def supports_explain?
+ true
+ end
+
def requires_reloading?
true
end
@@ -157,6 +143,10 @@ module ActiveRecord
sqlite_version >= '3.1.0'
end
+ def supports_index_sort_order?
+ sqlite_version >= '3.3.0'
+ end
+
def native_database_types #:nodoc:
{
:primary_key => default_primary_key_type,
@@ -195,29 +185,40 @@ module ActiveRecord
end
end
- if "<3".encoding_aware?
- def type_cast(value, column) # :nodoc:
- return value.to_f if BigDecimal === value
- return super unless String === value
- return super unless column && value
+ def type_cast(value, column) # :nodoc:
+ return value.to_f if BigDecimal === value
+ return super unless String === value
+ return super unless column && value
- value = super
- if column.type == :string && value.encoding == Encoding::ASCII_8BIT
- @logger.error "Binary data inserted for `string` type on column `#{column.name}`"
- value.encode! 'utf-8'
- end
- value
- end
- else
- def type_cast(value, column) # :nodoc:
- return super unless BigDecimal === value
-
- value.to_f
+ value = super
+ if column.type == :string && value.encoding == Encoding::ASCII_8BIT
+ @logger.error "Binary data inserted for `string` type on column `#{column.name}`"
+ value.encode! 'utf-8'
end
+ value
end
# DATABASE STATEMENTS ======================================
+ def explain(arel, binds = [])
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel)}"
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
+ end
+
+ class ExplainPrettyPrinter
+ # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
+ # the output of the SQLite shell:
+ #
+ # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
+ # 0|1|1|SCAN TABLE posts (~100000 rows)
+ #
+ def pp(result) # :nodoc:
+ result.rows.map do |row|
+ row.join('|')
+ end.join("\n") + "\n"
+ end
+ end
+
def exec_query(sql, name = nil, binds = [])
log(sql, name, binds) do
@@ -304,20 +305,25 @@ module ActiveRecord
# SCHEMA STATEMENTS ========================================
- def tables(name = 'SCHEMA') #:nodoc:
+ def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
sql = <<-SQL
SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL
+ sql << " AND name = #{quote_table_name(table_name)}" if table_name
exec_query(sql, name).map do |row|
row['name']
end
end
+ def table_exists?(name)
+ name && tables('SCHEMA', name).any?
+ end
+
# Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
- def columns(table_name, name = nil) #:nodoc:
+ def columns(table_name) #:nodoc:
table_structure(table_name).map do |field|
case field["dflt_value"]
when /^null$/i
@@ -432,7 +438,7 @@ module ActiveRecord
protected
def select(sql, name = nil, binds = []) #:nodoc:
- exec_query(sql, name, binds).to_a
+ exec_query(sql, name, binds)
end
def table_structure(table_name)
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
new file mode 100644
index 0000000000..d7746826a9
--- /dev/null
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -0,0 +1,96 @@
+require 'active_support/core_ext/module/delegation'
+
+module ActiveRecord
+ module ConnectionHandling
+ # Establishes the connection to the database. Accepts a hash as input where
+ # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
+ # example for regular databases (MySQL, Postgresql, etc):
+ #
+ # ActiveRecord::Base.establish_connection(
+ # :adapter => "mysql",
+ # :host => "localhost",
+ # :username => "myuser",
+ # :password => "mypass",
+ # :database => "somedatabase"
+ # )
+ #
+ # Example for SQLite database:
+ #
+ # ActiveRecord::Base.establish_connection(
+ # :adapter => "sqlite",
+ # :database => "path/to/dbfile"
+ # )
+ #
+ # Also accepts keys as strings (for parsing from YAML for example):
+ #
+ # ActiveRecord::Base.establish_connection(
+ # "adapter" => "sqlite",
+ # "database" => "path/to/dbfile"
+ # )
+ #
+ # Or a URL:
+ #
+ # ActiveRecord::Base.establish_connection(
+ # "postgres://myuser:mypass@localhost/somedatabase"
+ # )
+ #
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
+ # may be returned on an error.
+ def establish_connection(spec = ENV["DATABASE_URL"])
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations
+ spec = resolver.spec
+
+ unless respond_to?(spec.adapter_method)
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
+ end
+
+ remove_connection
+ connection_handler.establish_connection name, spec
+ end
+
+ # Returns the connection currently associated with the class. This can
+ # also be used to "borrow" the connection to do database work unrelated
+ # to any of the specific Active Records.
+ def connection
+ retrieve_connection
+ end
+
+ def connection_id
+ Thread.current['ActiveRecord::Base.connection_id']
+ end
+
+ def connection_id=(connection_id)
+ Thread.current['ActiveRecord::Base.connection_id'] = connection_id
+ end
+
+ # Returns the configuration of the associated connection as a hash:
+ #
+ # ActiveRecord::Base.connection_config
+ # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"}
+ #
+ # Please use only for reading.
+ def connection_config
+ connection_pool.spec.config
+ end
+
+ def connection_pool
+ connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
+ end
+
+ def retrieve_connection
+ connection_handler.retrieve_connection(self)
+ end
+
+ # Returns true if Active Record is connected.
+ def connected?
+ connection_handler.connected?(self)
+ end
+
+ def remove_connection(klass = self)
+ connection_handler.remove_connection(klass)
+ end
+
+ delegate :clear_active_connections!, :clear_reloadable_connections!,
+ :clear_all_connections!, :verify_active_connections!, :to => :connection_handler
+ end
+end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
new file mode 100644
index 0000000000..3f90d25962
--- /dev/null
+++ b/activerecord/lib/active_record/core.rb
@@ -0,0 +1,361 @@
+require 'active_support/concern'
+require 'thread'
+
+module ActiveRecord
+ module Core
+ extend ActiveSupport::Concern
+
+ included do
+ ##
+ # :singleton-method:
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
+ # which is then passed on to any new database connections made and which can be retrieved on both
+ # a class and instance level by calling +logger+.
+ config_attribute :logger, :global => true
+
+ ##
+ # :singleton-method:
+ # Contains the database configuration - as is typically stored in config/database.yml -
+ # as a Hash.
+ #
+ # For example, the following database.yml...
+ #
+ # development:
+ # adapter: sqlite3
+ # database: db/development.sqlite3
+ #
+ # production:
+ # adapter: sqlite3
+ # database: db/production.sqlite3
+ #
+ # ...would result in ActiveRecord::Base.configurations to look like this:
+ #
+ # {
+ # 'development' => {
+ # 'adapter' => 'sqlite3',
+ # 'database' => 'db/development.sqlite3'
+ # },
+ # 'production' => {
+ # 'adapter' => 'sqlite3',
+ # 'database' => 'db/production.sqlite3'
+ # }
+ # }
+ config_attribute :configurations, :global => true
+ self.configurations = {}
+
+ ##
+ # :singleton-method:
+ # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
+ # dates and times from the database. This is set to :utc by default.
+ config_attribute :default_timezone, :global => true
+ self.default_timezone = :utc
+
+ ##
+ # :singleton-method:
+ # Specifies the format to use when dumping the database schema with Rails'
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
+ # specific) SQL statements. If :ruby, the schema is dumped as an
+ # ActiveRecord::Schema file which can be loaded into any database that
+ # supports migrations. Use :ruby if you want to have different database
+ # adapters for, e.g., your development and test environments.
+ config_attribute :schema_format, :global => true
+ self.schema_format = :ruby
+
+ ##
+ # :singleton-method:
+ # Specify whether or not to use timestamps for migration versions
+ config_attribute :timestamped_migrations, :global => true
+ self.timestamped_migrations = true
+
+ ##
+ # :singleton-method:
+ # The connection handler
+ config_attribute :connection_handler
+ self.connection_handler = ConnectionAdapters::ConnectionHandler.new
+
+ ##
+ # :singleton-method:
+ # Specifies wether or not has_many or has_one association option
+ # :dependent => :restrict raises an exception. If set to true, the
+ # ActiveRecord::DeleteRestrictionError exception will be raised
+ # along with a DEPRECATION WARNING. If set to false, an error would
+ # be added to the model instead.
+ config_attribute :dependent_restrict_raises, :global => true
+ self.dependent_restrict_raises = true
+ end
+
+ module ClassMethods
+ def inherited(child_class) #:nodoc:
+ child_class.initialize_generated_modules
+ super
+ end
+
+ def initialize_generated_modules
+ @attribute_methods_mutex = Mutex.new
+
+ # force attribute methods to be higher in inheritance hierarchy than other generated methods
+ generated_attribute_methods
+ generated_feature_methods
+ end
+
+ def generated_feature_methods
+ @generated_feature_methods ||= begin
+ mod = const_set(:GeneratedFeatureMethods, Module.new)
+ include mod
+ mod
+ end
+ end
+
+ # Returns a string like 'Post(id:integer, title:string, body:text)'
+ def inspect
+ if self == Base
+ super
+ elsif abstract_class?
+ "#{super}(abstract)"
+ elsif table_exists?
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
+ "#{super}(#{attr_list})"
+ else
+ "#{super}(Table doesn't exist)"
+ end
+ end
+
+ # Overwrite the default class equality method to provide support for association proxies.
+ def ===(object)
+ object.is_a?(self)
+ end
+
+ def arel_table
+ @arel_table ||= Arel::Table.new(table_name, arel_engine)
+ end
+
+ def arel_engine
+ @arel_engine ||= connection_handler.connection_pools[name] ? self : active_record_super.arel_engine
+ end
+
+ private
+
+ def relation #:nodoc:
+ @relation ||= Relation.new(self, arel_table)
+
+ if finder_needs_type_condition?
+ @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
+ else
+ @relation
+ end
+ end
+ end
+
+ # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
+ # attributes but not yet saved (pass a hash with key names matching the associated table column names).
+ # In both instances, valid attribute keys are determined by the column names of the associated table --
+ # hence you can't have attributes that aren't part of the table columns.
+ #
+ # +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
+ # in the +options+ parameter.
+ #
+ # ==== Examples
+ # # Instantiates a single new object
+ # User.new(:first_name => 'Jamie')
+ #
+ # # Instantiates a single new object using the :admin mass-assignment security role
+ # User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
+ #
+ # # Instantiates a single new object bypassing mass-assignment security
+ # User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
+ def initialize(attributes = nil, options = {})
+ @attributes = self.class.initialize_attributes(self.class.column_defaults.dup)
+ @columns_hash = self.class.column_types.dup
+
+ init_internals
+
+ ensure_proper_type
+
+ populate_with_current_scope_attributes
+
+ assign_attributes(attributes, options) if attributes
+
+ yield self if block_given?
+ run_callbacks :initialize
+ end
+
+ # Initialize an empty model object from +coder+. +coder+ must contain
+ # the attributes necessary for initializing an empty model object. For
+ # example:
+ #
+ # class Post < ActiveRecord::Base
+ # end
+ #
+ # post = Post.allocate
+ # post.init_with('attributes' => { 'title' => 'hello world' })
+ # post.title # => 'hello world'
+ def init_with(coder)
+ @attributes = self.class.initialize_attributes(coder['attributes'])
+ @columns_hash = self.class.column_types.merge(coder['column_types'] || {})
+
+
+ init_internals
+
+ @new_record = false
+
+ run_callbacks :find
+ run_callbacks :initialize
+
+ self
+ end
+
+ # Duped objects have no id assigned and are treated as new records. Note
+ # that this is a "shallow" copy as it copies the object's attributes
+ # only, not its associations. The extent of a "deep" copy is application
+ # specific and is therefore left to the application to implement according
+ # to its need.
+ # The dup method does not preserve the timestamps (created|updated)_(at|on).
+ def initialize_dup(other)
+ cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
+ self.class.initialize_attributes(cloned_attributes)
+
+ cloned_attributes.delete(self.class.primary_key)
+
+ @attributes = cloned_attributes
+ @attributes[self.class.primary_key] = nil
+
+ run_callbacks(:initialize) if _initialize_callbacks.any?
+
+ @changed_attributes = {}
+ self.class.column_defaults.each do |attr, orig_value|
+ @changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
+ end
+
+ @aggregation_cache = {}
+ @association_cache = {}
+ @attributes_cache = {}
+
+ @new_record = true
+
+ ensure_proper_type
+ populate_with_current_scope_attributes
+ super
+ end
+
+ # Populate +coder+ with attributes about this record that should be
+ # serialized. The structure of +coder+ defined in this method is
+ # guaranteed to match the structure of +coder+ passed to the +init_with+
+ # method.
+ #
+ # Example:
+ #
+ # class Post < ActiveRecord::Base
+ # end
+ # coder = {}
+ # Post.new.encode_with(coder)
+ # coder # => {"attributes" => {"id" => nil, ... }}
+ def encode_with(coder)
+ coder['attributes'] = attributes
+ end
+
+ # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
+ # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
+ #
+ # Note that new records are different from any other record by definition, unless the
+ # other record is the receiver itself. Besides, if you fetch existing records with
+ # +select+ and leave the ID out, you're on your own, this predicate will return false.
+ #
+ # Note also that destroying a record preserves its ID in the model instance, so deleted
+ # models are still comparable.
+ def ==(comparison_object)
+ super ||
+ comparison_object.instance_of?(self.class) &&
+ id.present? &&
+ comparison_object.id == id
+ end
+ alias :eql? :==
+
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
+ def hash
+ id.hash
+ end
+
+ # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
+ def freeze
+ @attributes.freeze; self
+ end
+
+ # Returns +true+ if the attributes hash has been frozen.
+ def frozen?
+ @attributes.frozen?
+ end
+
+ # Allows sort on objects
+ def <=>(other_object)
+ if other_object.is_a?(self.class)
+ self.to_key <=> other_object.to_key
+ else
+ nil
+ end
+ end
+
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
+ # attributes will be marked as read only since they cannot be saved.
+ def readonly?
+ @readonly
+ end
+
+ # Marks this record as read only.
+ def readonly!
+ @readonly = true
+ end
+
+ # Returns the connection currently associated with the class. This can
+ # also be used to "borrow" the connection to do database work that isn't
+ # easily done without going straight to SQL.
+ def connection
+ self.class.connection
+ end
+
+ # Returns the contents of the record as a nicely formatted string.
+ def inspect
+ inspection = if @attributes
+ self.class.column_names.collect { |name|
+ if has_attribute?(name)
+ "#{name}: #{attribute_for_inspect(name)}"
+ end
+ }.compact.join(", ")
+ else
+ "not initialized"
+ end
+ "#<#{self.class} #{inspection}>"
+ end
+
+ private
+
+ # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
+ # of the array, and then rescues from the possible NoMethodError. If those elements are
+ # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
+ # which significantly impacts upon performance.
+ #
+ # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
+ #
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
+ def to_ary # :nodoc:
+ nil
+ end
+
+ def init_internals
+ pk = self.class.primary_key
+
+ @attributes[pk] = nil unless @attributes.key?(pk)
+
+ @relation = nil
+ @aggregation_cache = {}
+ @association_cache = {}
+ @attributes_cache = {}
+ @previously_changed = {}
+ @changed_attributes = {}
+ @readonly = false
+ @destroyed = false
+ @marked_for_destruction = false
+ @new_record = true
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 3c7defedac..224f5276eb 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -19,15 +19,10 @@ module ActiveRecord
counters.each do |association|
has_many_association = reflect_on_association(association.to_sym)
- expected_name = if has_many_association.options[:as]
- has_many_association.options[:as].to_s.classify
- else
- self.name
- end
-
+ foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
- reflection = belongs_to.find { |e| e.class_name == expected_name }
+ reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
counter_name = reflection.counter_cache_column
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
@@ -65,7 +60,7 @@ module ActiveRecord
# Post.update_counters [10, 15], :comment_count => 1
# # Executes the following SQL:
# # UPDATE posts
- # # SET comment_count = COALESCE(comment_count, 0) + 1,
+ # # SET comment_count = COALESCE(comment_count, 0) + 1
# # WHERE id IN (10, 15)
def update_counters(id, counters)
updates = counters.map do |counter_name, value|
diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb
index b309df9b1b..38dbbef5fc 100644
--- a/activerecord/lib/active_record/dynamic_finder_match.rb
+++ b/activerecord/lib/active_record/dynamic_finder_match.rb
@@ -6,33 +6,23 @@ module ActiveRecord
#
class DynamicFinderMatch
def self.match(method)
- finder = :first
- bang = false
- instantiator = nil
-
- case method.to_s
- when /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
- finder = :last if $1 == 'last_'
- finder = :all if $1 == 'all_'
- names = $2
- when /^find_by_([_a-zA-Z]\w*)\!$/
- bang = true
- names = $1
- when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
- instantiator = $1 == 'initialize' ? :new : :create
- names = $2
- else
- return nil
+ method = method.to_s
+ klass = [FindBy, FindByBang, FindOrInitializeCreateBy].find do |_klass|
+ _klass.matches?(method)
end
+ klass.new(method) if klass
+ end
- new(finder, instantiator, bang, names.split('_and_'))
+ def self.matches?(method)
+ method =~ self::METHOD_PATTERN
end
- def initialize(finder, instantiator, bang, attribute_names)
- @finder = finder
- @instantiator = instantiator
- @bang = bang
- @attribute_names = attribute_names
+ def initialize(method)
+ @finder = :first
+ @instantiator = nil
+ match_data = method.match(self.class::METHOD_PATTERN)
+ @attribute_names = match_data[-1].split("_and_")
+ initialize_from_match_data(match_data)
end
attr_reader :finder, :attribute_names, :instantiator
@@ -41,16 +31,54 @@ module ActiveRecord
@finder && !@instantiator
end
+ def creator?
+ @finder == :first && @instantiator == :create
+ end
+
def instantiator?
- @finder == :first && @instantiator
+ @instantiator
end
- def creator?
- @finder == :first && @instantiator == :create
+ def bang?
+ false
+ end
+
+ def valid_arguments?(arguments)
+ arguments.size >= @attribute_names.size
end
+ private
+
+ def initialize_from_match_data(match_data)
+ end
+ end
+
+ class FindBy < DynamicFinderMatch
+ METHOD_PATTERN = /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
+
+ def initialize_from_match_data(match_data)
+ @finder = :last if match_data[1] == 'last_'
+ @finder = :all if match_data[1] == 'all_'
+ end
+ end
+
+ class FindByBang < DynamicFinderMatch
+ METHOD_PATTERN = /^find_by_([_a-zA-Z]\w*)\!$/
+
def bang?
- @bang
+ true
+ end
+ end
+
+ class FindOrInitializeCreateBy < DynamicFinderMatch
+ METHOD_PATTERN = /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
+
+ def initialize_from_match_data(match_data)
+ @instantiator = match_data[1] == 'initialize' ? :new : :create
+ end
+
+ def valid_arguments?(arguments)
+ arguments.size == 1 && arguments.first.is_a?(Hash) || super
end
end
end
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
new file mode 100644
index 0000000000..60ce3dd4f1
--- /dev/null
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -0,0 +1,79 @@
+module ActiveRecord
+ module DynamicMatchers
+ def respond_to?(method_id, include_private = false)
+ if match = DynamicFinderMatch.match(method_id)
+ return true if all_attributes_exists?(match.attribute_names)
+ elsif match = DynamicScopeMatch.match(method_id)
+ return true if all_attributes_exists?(match.attribute_names)
+ end
+
+ super
+ end
+
+ private
+
+ # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
+ # <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
+ # section at the top of this file for more detailed information.
+ #
+ # It's even possible to use all the additional parameters to +find+. For example, the
+ # full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
+ #
+ # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
+ # is first invoked, so that future attempts to use it do not run through method_missing.
+ def method_missing(method_id, *arguments, &block)
+ if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id))
+ attribute_names = match.attribute_names
+ super unless all_attributes_exists?(attribute_names)
+ unless match.valid_arguments?(arguments)
+ method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
+ backtrace = [method_trace] + caller
+ raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace
+ end
+ if match.respond_to?(:scope?) && match.scope?
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
+ attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
+ #
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
+ end # end
+ METHOD
+ send(method_id, *arguments)
+ elsif match.finder?
+ options = arguments.extract_options!
+ relation = options.any? ? scoped(options) : scoped
+ relation.send :find_by_attributes, match, attribute_names, *arguments, &block
+ elsif match.instantiator?
+ scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
+ end
+ else
+ super
+ end
+ end
+
+ # Similar in purpose to +expand_hash_conditions_for_aggregates+.
+ def expand_attribute_names_for_aggregates(attribute_names)
+ attribute_names.map { |attribute_name|
+ unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
+ aggregate_mapping(aggregation).map do |field_attr, _|
+ field_attr.to_sym
+ end
+ else
+ attribute_name.to_sym
+ end
+ }.flatten
+ end
+
+ def all_attributes_exists?(attribute_names)
+ (expand_attribute_names_for_aggregates(attribute_names) -
+ column_methods_hash.keys).empty?
+ end
+
+ def aggregate_mapping(reflection)
+ mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
+ mapping.first.is_a?(Array) ? mapping : [mapping]
+ end
+
+
+ end
+end
diff --git a/activerecord/lib/active_record/dynamic_scope_match.rb b/activerecord/lib/active_record/dynamic_scope_match.rb
index c832e927d6..a502155aac 100644
--- a/activerecord/lib/active_record/dynamic_scope_match.rb
+++ b/activerecord/lib/active_record/dynamic_scope_match.rb
@@ -19,5 +19,9 @@ module ActiveRecord
attr_reader :scope, :attribute_names
alias :scope? :scope
+
+ def valid_arguments?(arguments)
+ arguments.size >= @attribute_names.size
+ end
end
end
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
new file mode 100644
index 0000000000..01cacf6153
--- /dev/null
+++ b/activerecord/lib/active_record/explain.rb
@@ -0,0 +1,82 @@
+require 'active_support/core_ext/class/attribute'
+
+module ActiveRecord
+ module Explain
+ def self.extended(base)
+ # If a query takes longer than these many seconds we log its query plan
+ # automatically. nil disables this feature.
+ base.config_attribute :auto_explain_threshold_in_seconds, :global => true
+ end
+
+ # If auto explain is enabled, this method triggers EXPLAIN logging for the
+ # queries triggered by the block if it takes more than the threshold as a
+ # whole. That is, the threshold is not checked against each individual
+ # query, but against the duration of the entire block. This approach is
+ # convenient for relations.
+ #
+ # The available_queries_for_explain thread variable collects the queries
+ # to be explained. If the value is nil, it means queries are not being
+ # currently collected. A false value indicates collecting is turned
+ # off. Otherwise it is an array of queries.
+ def logging_query_plan # :nodoc:
+ return yield unless logger
+
+ threshold = auto_explain_threshold_in_seconds
+ current = Thread.current
+ if threshold && current[:available_queries_for_explain].nil?
+ begin
+ queries = current[:available_queries_for_explain] = []
+ start = Time.now
+ result = yield
+ logger.warn(exec_explain(queries)) if Time.now - start > threshold
+ result
+ ensure
+ current[:available_queries_for_explain] = nil
+ end
+ else
+ yield
+ end
+ end
+
+ # Relation#explain needs to be able to collect the queries regardless of
+ # whether auto explain is enabled. This method serves that purpose.
+ def collecting_queries_for_explain # :nodoc:
+ current = Thread.current
+ original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
+ return yield, current[:available_queries_for_explain]
+ ensure
+ # Note that the return value above does not depend on this assigment.
+ current[:available_queries_for_explain] = original
+ end
+
+ # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
+ # Returns a formatted string ready to be logged.
+ def exec_explain(queries) # :nodoc:
+ queries && queries.map do |sql, bind|
+ [].tap do |msg|
+ msg << "EXPLAIN for: #{sql}"
+ unless bind.empty?
+ bind_msg = bind.map {|col, val| [col.name, val]}.inspect
+ msg.last << " #{bind_msg}"
+ end
+ msg << connection.explain(sql, bind)
+ end.join("\n")
+ end.join("\n")
+ end
+
+ # Silences automatic EXPLAIN logging for the duration of the block.
+ #
+ # This has high priority, no EXPLAINs will be run even if downwards
+ # the threshold is set to 0.
+ #
+ # As the name of the method suggests this only applies to automatic
+ # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run.
+ def silence_auto_explain
+ current = Thread.current
+ original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
+ yield
+ ensure
+ current[:available_queries_for_explain] = original
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
new file mode 100644
index 0000000000..1f8c4fc203
--- /dev/null
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -0,0 +1,24 @@
+require 'active_support/notifications'
+
+module ActiveRecord
+ class ExplainSubscriber # :nodoc:
+ def call(*args)
+ if queries = Thread.current[:available_queries_for_explain]
+ payload = args.last
+ queries << payload.values_at(:sql, :binds) unless ignore_payload?(payload)
+ end
+ end
+
+ # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
+ # our own EXPLAINs now matter how loopingly beautiful that would be.
+ #
+ # On the other hand, we want to monitor the performance of our real database
+ # queries, not the performance of the access to the query cache.
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
+ def ignore_payload?(payload)
+ payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name])
+ end
+
+ ActiveSupport::Notifications.subscribe("sql.active_record", new)
+ end
+end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index cad9417216..b82d5b5621 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -1,17 +1,8 @@
require 'erb'
-
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
require 'zlib'
require 'active_support/dependencies'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/logger'
-require 'active_support/ordered_hash'
require 'active_record/fixtures/file'
if defined? ActiveRecord
@@ -22,8 +13,6 @@ else
end
end
-class FixturesFileNotFound < StandardError; end
-
module ActiveRecord
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
#
@@ -391,10 +380,15 @@ module ActiveRecord
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
- def self.find_table_name(table_name) # :nodoc:
+ def self.default_fixture_model_name(fixture_name) # :nodoc:
ActiveRecord::Base.pluralize_table_names ?
- table_name.to_s.singularize.camelize :
- table_name.to_s.camelize
+ fixture_name.singularize.camelize :
+ fixture_name.camelize
+ end
+
+ def self.default_fixture_table_name(fixture_name) # :nodoc:
+ "#{ActiveRecord::Base.table_name_prefix}"\
+ "#{fixture_name.tr('/', '_')}#{ActiveRecord::Base.table_name_suffix}".to_sym
end
def self.reset_cache
@@ -443,10 +437,8 @@ module ActiveRecord
self.all_loaded_fixtures = {}
def self.create_fixtures(fixtures_directory, table_names, class_names = {})
- table_names = [table_names].flatten.map { |n| n.to_s }
- table_names.each { |n|
- class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
- }
+ table_names = Array(table_names).map(&:to_s)
+ class_names = class_names.stringify_keys
# FIXME: Apparently JK uses this.
connection = block_given? ? yield : ActiveRecord::Base.connection
@@ -460,12 +452,12 @@ module ActiveRecord
fixtures_map = {}
fixture_files = files_to_read.map do |path|
- table_name = path.tr '/', '_'
+ fixture_name = path
- fixtures_map[path] = ActiveRecord::Fixtures.new(
+ fixtures_map[fixture_name] = new( # ActiveRecord::Fixtures.new
connection,
- table_name,
- class_names[table_name.to_sym] || table_name.classify,
+ fixture_name,
+ class_names[fixture_name.to_s] || default_fixture_model_name(fixture_name),
::File.join(fixtures_directory, path))
end
@@ -489,8 +481,8 @@ module ActiveRecord
# Cap primary key sequences to max(pk).
if connection.respond_to?(:reset_pk_sequence!)
- table_names.each do |table_name|
- connection.reset_pk_sequence!(table_name.tr('/', '_'))
+ fixture_files.each do |ff|
+ connection.reset_pk_sequence!(ff.table_name)
end
end
end
@@ -509,25 +501,27 @@ module ActiveRecord
attr_reader :table_name, :name, :fixtures, :model_class
- def initialize(connection, table_name, class_name, fixture_path)
+ def initialize(connection, fixture_name, class_name, fixture_path)
@connection = connection
- @table_name = table_name
@fixture_path = fixture_path
- @name = table_name # preserve fixture base name
+ @name = fixture_name
@class_name = class_name
- @fixtures = ActiveSupport::OrderedHash.new
- @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
+ @fixtures = {}
# Should be an AR::Base type class
if class_name.is_a?(Class)
- @table_name = class_name.table_name
- @connection = class_name.connection
- @model_class = class_name
+ @model_class = class_name
else
- @model_class = class_name.constantize rescue nil
+ @model_class = class_name.constantize rescue nil
end
+ @connection = model_class.connection if model_class && model_class.respond_to?(:connection)
+
+ @table_name = ( model_class.respond_to?(:table_name) ?
+ model_class.table_name :
+ self.class.default_fixture_table_name(fixture_name) )
+
read_fixture_files
end
@@ -562,7 +556,7 @@ module ActiveRecord
rows[table_name] = fixtures.map do |label, fixture|
row = fixture.to_hash
- if model_class && model_class < ActiveRecord::Base
+ if model_class && model_class < ActiveRecord::Model
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
if model_class.record_timestamps
timestamp_column_names.each do |name|
@@ -644,14 +638,6 @@ module ActiveRecord
end
def read_fixture_files
- if ::File.file?(yaml_file_path)
- read_yaml_fixture_files
- else
- raise FixturesFileNotFound, "Could not find #{yaml_file_path}"
- end
- end
-
- def read_yaml_fixture_files
yaml_files = Dir["#{@fixture_path}/**/*.yml"].select { |f|
::File.file?(f)
} + [yaml_file_path]
@@ -734,20 +720,37 @@ module ActiveRecord
self.use_instantiated_fixtures = false
self.pre_loaded_fixtures = false
- self.fixture_class_names = Hash.new do |h, table_name|
- h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name)
+ self.fixture_class_names = Hash.new do |h, fixture_name|
+ fixture_name = fixture_name.to_s
+ h[fixture_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_name)
end
end
module ClassMethods
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
+ #
+ # Examples:
+ #
+ # set_fixture_class :some_fixture => SomeModel,
+ # 'namespaced/fixture' => Another::Model
+ #
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
+ #--
+ # It is also possible to pass the class name instead of the class:
+ # set_fixture_class 'some_fixture' => 'SomeModel'
+ # I think this option is redundant, i propose to deprecate it.
+ # Isn't it easier to always pass the class itself?
+ # (2011-12-20 alexeymuranov)
+ #++
def set_fixture_class(class_names = {})
- self.fixture_class_names = self.fixture_class_names.merge(class_names)
+ self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
end
def fixtures(*fixture_names)
if fixture_names.first == :all
- fixture_names = Dir["#{fixture_path}/**/*.{yml}"]
- fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] }
+ fixture_names = Dir["#{fixture_path}/**/*.yml"].map { |f|
+ File.basename f, '.yml'
+ }
else
fixture_names = fixture_names.flatten.map { |n| n.to_s }
end
@@ -778,12 +781,13 @@ module ActiveRecord
end
def setup_fixture_accessors(fixture_names = nil)
- fixture_names = Array.wrap(fixture_names || fixture_table_names)
+ fixture_names = Array(fixture_names || fixture_table_names)
methods = Module.new do
fixture_names.each do |fixture_name|
- fixture_name = fixture_name.to_s.tr('./', '_')
+ fixture_name = fixture_name.to_s
+ accessor_name = fixture_name.tr('/', '_').to_sym
- define_method(fixture_name) do |*fixtures|
+ define_method(accessor_name) do |*fixtures|
force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
@fixture_cache[fixture_name] ||= {}
@@ -796,13 +800,13 @@ module ActiveRecord
@fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
end
else
- raise StandardError, "No fixture with name '#{fixture}' found for table '#{fixture_name}'"
+ raise StandardError, "No entry named '#{fixture}' found for fixture collection '#{fixture_name}'"
end
end
instances.size == 1 ? instances.first : instances
end
- private fixture_name
+ private accessor_name
end
end
include methods
@@ -832,6 +836,7 @@ module ActiveRecord
end
@fixture_cache = {}
+ @fixture_connections = []
@@already_loaded_fixtures ||= {}
# Load fixtures once and begin transaction.
diff --git a/activerecord/lib/active_record/fixtures/file.rb b/activerecord/lib/active_record/fixtures/file.rb
index 6bad36abb9..6547791144 100644
--- a/activerecord/lib/active_record/fixtures/file.rb
+++ b/activerecord/lib/active_record/fixtures/file.rb
@@ -1,8 +1,3 @@
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'erb'
require 'yaml'
diff --git a/activerecord/lib/active_record/identity_map.rb b/activerecord/lib/active_record/identity_map.rb
index b15b5a8133..d9777bb2f6 100644
--- a/activerecord/lib/active_record/identity_map.rb
+++ b/activerecord/lib/active_record/identity_map.rb
@@ -111,37 +111,18 @@ module ActiveRecord
# model object.
def reinit_with(coder)
@attributes_cache = {}
- dirty = @changed_attributes.keys
- @attributes.update(coder['attributes'].except(*dirty))
+ dirty = @changed_attributes.keys
+ attributes = self.class.initialize_attributes(coder['attributes'].except(*dirty))
+ @attributes.update(attributes)
@changed_attributes.update(coder['attributes'].slice(*dirty))
@changed_attributes.delete_if{|k,v| v.eql? @attributes[k]}
- set_serialized_attributes
-
run_callbacks :find
self
end
class Middleware
- class Body #:nodoc:
- def initialize(target, original)
- @target = target
- @original = original
- end
-
- def each(&block)
- @target.each(&block)
- end
-
- def close
- @target.close if @target.respond_to?(:close)
- ensure
- IdentityMap.enabled = @original
- IdentityMap.clear
- end
- end
-
def initialize(app)
@app = app
end
@@ -149,8 +130,14 @@ module ActiveRecord
def call(env)
enabled = IdentityMap.enabled
IdentityMap.enabled = true
- status, headers, body = @app.call(env)
- [status, headers, Body.new(body, enabled)]
+
+ response = @app.call(env)
+ response[2] = Rack::BodyProxy.new(response[2]) do
+ IdentityMap.enabled = enabled
+ IdentityMap.clear
+ end
+
+ response
end
end
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
new file mode 100644
index 0000000000..2c766411a0
--- /dev/null
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -0,0 +1,184 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module Inheritance
+ extend ActiveSupport::Concern
+
+ included do
+ # Determine whether to store the full constant name including namespace when using STI
+ config_attribute :store_full_sti_class
+ self.store_full_sti_class = true
+ end
+
+ module ClassMethods
+ # True if this isn't a concrete subclass needing a STI type condition.
+ def descends_from_active_record?
+ sup = active_record_super
+
+ if sup.abstract_class?
+ sup.descends_from_active_record?
+ elsif self == Base
+ false
+ else
+ [Base, Model].include?(sup) || !columns_hash.include?(inheritance_column)
+ end
+ end
+
+ def finder_needs_type_condition? #:nodoc:
+ # This is like this because benchmarking justifies the strange :false stuff
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
+ end
+
+ def symbolized_base_class
+ @symbolized_base_class ||= base_class.to_s.to_sym
+ end
+
+ def symbolized_sti_name
+ @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
+ end
+
+ # Returns the base AR subclass that this class descends from. If A
+ # extends AR::Base, A.base_class will return A. If B descends from A
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
+ #
+ # If B < A and C < B and if A is an abstract_class then both B.base_class
+ # and C.base_class would return B as the answer since A is an abstract_class.
+ def base_class
+ class_of_active_record_descendant(self)
+ end
+
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
+ attr_accessor :abstract_class
+
+ # Returns whether this class is an abstract class or not.
+ def abstract_class?
+ defined?(@abstract_class) && @abstract_class == true
+ end
+
+ def sti_name
+ store_full_sti_class ? name : name.demodulize
+ end
+
+ # Finder methods must instantiate through this method to work with the
+ # single-table inheritance model that makes it possible to create
+ # objects of different types from the same table.
+ def instantiate(record, column_types = {})
+ sti_class = find_sti_class(record[inheritance_column])
+ record_id = sti_class.primary_key && record[sti_class.primary_key]
+
+ if ActiveRecord::IdentityMap.enabled? && record_id
+ if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
+ record_id = record_id.to_i
+ end
+ if instance = IdentityMap.get(sti_class, record_id)
+ instance.reinit_with('attributes' => record)
+ else
+ instance = sti_class.allocate.init_with('attributes' => record)
+ IdentityMap.add(instance)
+ end
+ else
+ column_types = sti_class.decorate_columns(column_types)
+ instance = sti_class.allocate.init_with('attributes' => record,
+ 'column_types' => column_types)
+ end
+
+ instance
+ end
+
+ # For internal use.
+ #
+ # If this class includes ActiveRecord::Model then it won't have a
+ # superclass. So this provides a way to get to the 'root' (ActiveRecord::Model).
+ def active_record_super #:nodoc:
+ superclass < Model ? superclass : Model
+ end
+
+ protected
+
+ # Returns the class descending directly from ActiveRecord::Base or an
+ # abstract class, if any, in the inheritance hierarchy.
+ def class_of_active_record_descendant(klass)
+ unless klass < Model
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
+ end
+
+ sup = klass.active_record_super
+ if [Base, Model].include?(klass) || [Base, Model].include?(sup) || sup.abstract_class?
+ klass
+ else
+ class_of_active_record_descendant(sup)
+ end
+ end
+
+ # Returns the class type of the record using the current module as a prefix. So descendants of
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
+ def compute_type(type_name)
+ if type_name.match(/^::/)
+ # If the type is prefixed with a scope operator then we assume that
+ # the type_name is an absolute reference.
+ ActiveSupport::Dependencies.constantize(type_name)
+ else
+ # Build a list of candidates to search for
+ candidates = []
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
+ candidates << type_name
+
+ candidates.each do |candidate|
+ begin
+ constant = ActiveSupport::Dependencies.constantize(candidate)
+ return constant if candidate == constant.to_s
+ rescue NameError => e
+ # We don't want to swallow NoMethodError < NameError errors
+ raise e unless e.instance_of?(NameError)
+ end
+ end
+
+ raise NameError, "uninitialized constant #{candidates.first}"
+ end
+ end
+
+ private
+
+ def find_sti_class(type_name)
+ if type_name.blank? || !columns_hash.include?(inheritance_column)
+ self
+ else
+ begin
+ if store_full_sti_class
+ ActiveSupport::Dependencies.constantize(type_name)
+ else
+ compute_type(type_name)
+ end
+ rescue NameError
+ raise SubclassNotFound,
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
+ "or overwrite #{name}.inheritance_column to use another column for that information."
+ end
+ end
+ end
+
+ def type_condition(table = arel_table)
+ sti_column = table[inheritance_column.to_sym]
+ sti_names = ([self] + descendants).map { |model| model.sti_name }
+
+ sti_column.in(sti_names)
+ end
+ end
+
+ private
+
+ # Sets the attribute used for single table inheritance to this class name if this is not the
+ # ActiveRecord::Base descendant.
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
+ # No such attribute would be set for objects of the Message class in that example.
+ def ensure_proper_type
+ klass = self.class
+ if klass.finder_needs_type_condition?
+ write_attribute(klass.inheritance_column, klass.sti_name)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
new file mode 100644
index 0000000000..2c42f4cca5
--- /dev/null
+++ b/activerecord/lib/active_record/integration.rb
@@ -0,0 +1,49 @@
+module ActiveRecord
+ module Integration
+ # Returns a String, which Action Pack uses for constructing an URL to this
+ # object. The default implementation returns this record's id as a String,
+ # or nil if this record's unsaved.
+ #
+ # For example, suppose that you have a User model, and that you have a
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
+ # construct a path with the user object's 'id' in it:
+ #
+ # user = User.find_by_name('Phusion')
+ # user_path(user) # => "/users/1"
+ #
+ # You can override +to_param+ in your model to make +user_path+ construct
+ # a path using the user's name instead of the user's id:
+ #
+ # class User < ActiveRecord::Base
+ # def to_param # overridden
+ # name
+ # end
+ # end
+ #
+ # user = User.find_by_name('Phusion')
+ # user_path(user) # => "/users/Phusion"
+ def to_param
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
+ id && id.to_s # Be sure to stringify the id for routes
+ end
+
+ # Returns a cache key that can be used to identify this record.
+ #
+ # ==== Examples
+ #
+ # Product.new.cache_key # => "products/new"
+ # Product.find(5).cache_key # => "products/5" (updated_at not available)
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
+ def cache_key
+ case
+ when new_record?
+ "#{self.class.model_name.cache_key}/new"
+ when timestamp = self[:updated_at]
+ timestamp = timestamp.utc.to_s(:number)
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
+ else
+ "#{self.class.model_name.cache_key}/#{id}"
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml
index 44328f63b6..896132d566 100644
--- a/activerecord/lib/active_record/locale/en.yml
+++ b/activerecord/lib/active_record/locale/en.yml
@@ -10,6 +10,9 @@ en:
messages:
taken: "has already been taken"
record_invalid: "Validation failed: %{errors}"
+ restrict_dependent_destroy:
+ one: "Cannot delete record because a dependent %{record} exists"
+ many: "Cannot delete record because dependent %{record} exist"
# Append your own errors here or at the model/attributes scope.
# You can define own errors for models or model attributes.
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 1a29ded787..4d73cdd37a 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -40,21 +40,19 @@ module ActiveRecord
# This locking mechanism will function inside a single Ruby process. To make it work across all
# web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
#
- # You must ensure that your database schema defaults the +lock_version+ column to 0.
- #
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
- # 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>
+ # To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
+ #
+ # class Person < ActiveRecord::Base
+ # self.locking_column = :lock_person
+ # end
+ #
module Optimistic
extend ActiveSupport::Concern
included do
- cattr_accessor :lock_optimistically, :instance_writer => false
+ config_attribute :lock_optimistically, :global => true
self.lock_optimistically = true
-
- class << self
- alias_method :locking_column=, :set_locking_column
- end
end
def locking_enabled? #:nodoc:
@@ -68,21 +66,6 @@ module ActiveRecord
send(lock_col + '=', previous_lock_value + 1)
end
- def attributes_from_column_definition
- result = super
-
- # If the locking column has no default value set,
- # start the lock version at zero. Note we can't use
- # <tt>locking_enabled?</tt> at this point as
- # <tt>@attributes</tt> may not have been initialized yet.
-
- if result.key?(self.class.locking_column) && lock_optimistically
- result[self.class.locking_column] ||= 0
- end
-
- result
- end
-
def update(attribute_names = @attributes.keys) #:nodoc:
return super unless locking_enabled?
return 0 if attribute_names.empty?
@@ -149,14 +132,15 @@ module ActiveRecord
end
# Set the column to use for optimistic locking. Defaults to +lock_version+.
- def set_locking_column(value = nil, &block)
- define_attr_method :locking_column, value, &block
- value
+ def locking_column=(value)
+ @original_locking_column = @locking_column if defined?(@locking_column)
+ @locking_column = value.to_s
end
# The version column used for optimistic locking. Defaults to +lock_version+.
def locking_column
- reset_locking_column
+ reset_locking_column unless defined?(@locking_column)
+ @locking_column
end
# Quote the column name used for optimistic locking.
@@ -166,7 +150,7 @@ module ActiveRecord
# Reset the column used for optimistic locking back to the +lock_version+ default.
def reset_locking_column
- set_locking_column DEFAULT_LOCKING_COLUMN
+ self.locking_column = DEFAULT_LOCKING_COLUMN
end
# Make sure the lock version column gets updated when counters are
@@ -175,6 +159,18 @@ module ActiveRecord
counters = counters.merge(locking_column => 1) if locking_enabled?
super
end
+
+ # If the locking column has no default value set,
+ # start the lock version at zero. Note we can't use
+ # <tt>locking_enabled?</tt> at this point as
+ # <tt>@attributes</tt> may not have been initialized yet.
+ def initialize_attributes(attributes) #:nodoc:
+ if attributes.key?(locking_column) && lock_optimistically
+ attributes[locking_column] ||= 0
+ end
+
+ attributes
+ end
end
end
end
diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb
index 66994e4797..58af92f0b1 100644
--- a/activerecord/lib/active_record/locking/pessimistic.rb
+++ b/activerecord/lib/active_record/locking/pessimistic.rb
@@ -38,6 +38,18 @@ module ActiveRecord
# account2.save!
# end
#
+ # You can start a transaction and acquire the lock in one go by calling
+ # <tt>with_lock</tt> with a block. The block is called from within
+ # a transaction, the object is already locked. Example:
+ #
+ # account = Account.first
+ # account.with_lock do
+ # # This block is called within a transaction,
+ # # account is already locked.
+ # account.balance -= 100
+ # account.save!
+ # end
+ #
# Database-specific information on row locking:
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
@@ -50,6 +62,16 @@ module ActiveRecord
reload(:lock => lock) if persisted?
self
end
+
+ # Wraps the passed block in a transaction, locking the object
+ # before yielding. You pass can the SQL locking clause
+ # as argument (see <tt>lock!</tt>).
+ def with_lock(lock = true)
+ transaction do
+ lock!(lock)
+ yield
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 3a015ee8c2..a25f2c7bca 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -26,9 +26,9 @@ module ActiveRecord
return if 'SCHEMA' == payload[:name]
- name = '%s (%.1fms)' % [payload[:name], event.duration]
- sql = payload[:sql].squeeze(' ')
- binds = nil
+ name = '%s (%.1fms)' % [payload[:name], event.duration]
+ sql = payload[:sql].squeeze(' ')
+ binds = nil
unless (payload[:binds] || []).empty?
binds = " " + payload[:binds].map { |col,v|
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 7166f1b82a..2a9139749d 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,6 +1,8 @@
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/class/attribute_accessors"
-require "active_support/core_ext/array/wrap"
+require 'active_support/deprecation'
+require 'active_record/schema_migration'
+require 'set'
module ActiveRecord
# Exception that can be raised to stop migrations from going backwards.
@@ -112,12 +114,13 @@ module ActiveRecord
# a column but keeps the type and content.
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
# the column to a different type using the same parameters as add_column.
- # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named
- # +column_name+ from the table called +table_name+.
+ # * <tt>remove_column(table_name, column_names)</tt>: Removes the column listed in
+ # +column_names+ from the table called +table_name+.
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
# with the name of the column. Other options include
- # <tt>:name</tt> and <tt>:unique</tt> (e.g.
- # <tt>{ :name => "users_name_index", :unique => true }</tt>).
+ # <tt>:name</tt>, <tt>:unique</tt> (e.g.
+ # <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
+ # (e.g. { :order => {:name => :desc} }</tt>).
# * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
# specified by +column_name+.
# * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
@@ -301,7 +304,7 @@ module ActiveRecord
#
# class TenderloveMigration < ActiveRecord::Migration
# def change
- # create_table(:horses) do
+ # create_table(:horses) do |t|
# t.column :content, :text
# t.column :remind_at, :datetime
# end
@@ -340,16 +343,28 @@ module ActiveRecord
attr_accessor :name, :version
- def initialize
- @name = self.class.name
- @version = nil
+ def initialize(name = self.class.name, version = nil)
+ @name = name
+ @version = version
@connection = nil
+ @reverting = false
end
# instantiate the delegate object after initialize is defined
self.verbose = true
self.delegate = new
+ def revert
+ @reverting = true
+ yield
+ ensure
+ @reverting = false
+ end
+
+ def reverting?
+ @reverting
+ end
+
def up
self.class.delegate = self
return unless self.class.respond_to?(:up)
@@ -383,9 +398,11 @@ module ActiveRecord
end
@connection = conn
time = Benchmark.measure {
- recorder.inverse.each do |cmd, args|
- send(cmd, *args)
- end
+ self.revert {
+ recorder.inverse.each do |cmd, args|
+ send(cmd, *args)
+ end
+ }
}
else
time = Benchmark.measure { change }
@@ -440,8 +457,11 @@ module ActiveRecord
arg_list = arguments.map{ |a| a.inspect } * ', '
say_with_time "#{method}(#{arg_list})" do
- unless arguments.empty? || method == :execute
- arguments[0] = Migrator.proper_table_name(arguments.first)
+ unless reverting?
+ unless arguments.empty? || method == :execute
+ arguments[0] = Migrator.proper_table_name(arguments.first)
+ arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
+ end
end
return super unless connection.respond_to?(method)
connection.send(method, *arguments, &block)
@@ -455,26 +475,28 @@ module ActiveRecord
destination_migrations = ActiveRecord::Migrator.migrations(destination)
last = destination_migrations.last
- sources.each do |name, path|
+ sources.each do |scope, path|
source_migrations = ActiveRecord::Migrator.migrations(path)
source_migrations.each do |migration|
source = File.read(migration.filename)
- source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}"
+ source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
- options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip]
+ if options[:on_skip] && duplicate.scope != scope.to_s
+ options[:on_skip].call(scope, migration)
+ end
next
end
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
- new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
old_path, migration.filename = migration.filename, new_path
last = migration
- FileUtils.cp(old_path, migration.filename)
+ File.open(migration.filename, "w") { |f| f.write source }
copied << migration
- options[:on_copy].call(name, migration, old_path) if options[:on_copy]
+ options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
destination_migrations << migration
end
end
@@ -493,9 +515,9 @@ module ActiveRecord
# MigrationProxy is used to defer loading of the actual migration classes
# until they are needed
- class MigrationProxy < Struct.new(:name, :version, :filename)
+ class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
- def initialize(name, version, filename)
+ def initialize(name, version, filename, scope)
super
@migration = nil
end
@@ -504,7 +526,7 @@ module ActiveRecord
File.basename(filename)
end
- delegate :migrate, :announce, :write, :to=>:migration
+ delegate :migrate, :announce, :write, :to => :migration
private
@@ -524,16 +546,16 @@ module ActiveRecord
attr_writer :migrations_paths
alias :migrations_path= :migrations_paths=
- def migrate(migrations_paths, target_version = nil)
+ def migrate(migrations_paths, target_version = nil, &block)
case
- when target_version.nil?
- up(migrations_paths, target_version)
- when current_version == 0 && target_version == 0
- []
- when current_version > target_version
- down(migrations_paths, target_version)
- else
- up(migrations_paths, target_version)
+ when target_version.nil?
+ up(migrations_paths, target_version, &block)
+ when current_version == 0 && target_version == 0
+ []
+ when current_version > target_version
+ down(migrations_paths, target_version, &block)
+ else
+ up(migrations_paths, target_version, &block)
end
end
@@ -546,24 +568,33 @@ module ActiveRecord
end
def up(migrations_paths, target_version = nil)
- self.new(:up, migrations_paths, target_version).migrate
+ migrations = migrations(migrations_paths)
+ migrations.select! { |m| yield m } if block_given?
+
+ self.new(:up, migrations, target_version).migrate
end
- def down(migrations_paths, target_version = nil)
- self.new(:down, migrations_paths, target_version).migrate
+ def down(migrations_paths, target_version = nil, &block)
+ migrations = migrations(migrations_paths)
+ migrations.select! { |m| yield m } if block_given?
+
+ self.new(:down, migrations, target_version).migrate
end
def run(direction, migrations_paths, target_version)
- self.new(direction, migrations_paths, target_version).run
+ self.new(direction, migrations(migrations_paths), target_version).run
+ end
+
+ def open(migrations_paths)
+ self.new(:up, migrations(migrations_paths), nil)
end
def schema_migrations_table_name
- Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
+ SchemaMigration.table_name
end
def get_all_versions
- table = Arel::Table.new(schema_migrations_table_name)
- Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
end
def current_version
@@ -583,7 +614,7 @@ module ActiveRecord
def migrations_paths
@migrations_paths ||= ['db/migrate']
# just to not break things if someone uses: migration_path = some_string
- Array.wrap(@migrations_paths)
+ Array(@migrations_paths)
end
def migrations_path
@@ -591,25 +622,18 @@ module ActiveRecord
end
def migrations(paths)
- paths = Array.wrap(paths)
-
- files = Dir[*paths.map { |p| "#{p}/[0-9]*_*.rb" }]
+ paths = Array(paths)
- seen = Hash.new false
+ files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
migrations = files.map do |file|
- version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
raise IllegalMigrationNameError.new(file) unless version
version = version.to_i
name = name.camelize
- raise DuplicateMigrationVersionError.new(version) if seen[version]
- raise DuplicateMigrationNameError.new(name) if seen[name]
-
- seen[version] = seen[name] = true
-
- MigrationProxy.new(name, version, file)
+ MigrationProxy.new(name, version, file, scope)
end
migrations.sort_by(&:version)
@@ -618,7 +642,7 @@ module ActiveRecord
private
def move(direction, migrations_paths, steps)
- migrator = self.new(direction, migrations_paths)
+ migrator = self.new(direction, migrations(migrations_paths))
start_index = migrator.migrations.index(migrator.current_migration)
if start_index
@@ -629,19 +653,33 @@ module ActiveRecord
end
end
- def initialize(direction, migrations_paths, target_version = nil)
+ def initialize(direction, migrations, target_version = nil)
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
- Base.connection.initialize_schema_migrations_table
- @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
+
+ @direction = direction
+ @target_version = target_version
+ @migrated_versions = nil
+
+ if Array(migrations).grep(String).empty?
+ @migrations = migrations
+ else
+ ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
+ @migrations = self.class.migrations(migrations)
+ end
+
+ validate(@migrations)
+
+ ActiveRecord::SchemaMigration.create_table
end
def current_version
- migrated.last || 0
+ migrated.sort.last || 0
end
def current_migration
migrations.detect { |m| m.version == current_version }
end
+ alias :current :current_migration
def run
target = migrations.detect { |m| m.version == @target_version }
@@ -653,96 +691,108 @@ module ActiveRecord
end
def migrate
- current = migrations.detect { |m| m.version == current_version }
- target = migrations.detect { |m| m.version == @target_version }
-
- if target.nil? && @target_version && @target_version > 0
+ if !target && @target_version && @target_version > 0
raise UnknownMigrationVersionError.new(@target_version)
end
- start = up? ? 0 : (migrations.index(current) || 0)
- finish = migrations.index(target) || migrations.size - 1
- runnable = migrations[start..finish]
+ running = runnable
- # skip the last migration if we're headed down, but not ALL the way down
- runnable.pop if down? && target
+ if block_given?
+ ActiveSupport::Deprecation.warn(<<-eomsg)
+block argument to migrate is deprecated, please filter migrations before constructing the migrator
+ eomsg
+ running.select! { |m| yield m }
+ end
- ran = []
- runnable.each do |migration|
+ running.each do |migration|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
- seen = migrated.include?(migration.version.to_i)
-
- # On our way up, we skip migrating the ones we've already migrated
- next if up? && seen
-
- # On our way down, we skip reverting the ones we've never migrated
- if down? && !seen
- migration.announce 'never migrated, skipping'; migration.write
- next
- end
-
begin
ddl_transaction do
migration.migrate(@direction)
record_version_state_after_migrating(migration.version)
end
- ran << migration
rescue => e
canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
end
end
- ran
end
- def migrations
- @migrations ||= begin
- migrations = self.class.migrations(@migrations_paths)
- down? ? migrations.reverse : migrations
+ def runnable
+ runnable = migrations[start..finish]
+ if up?
+ runnable.reject { |m| ran?(m) }
+ else
+ # skip the last migration if we're headed down, but not ALL the way down
+ runnable.pop if target
+ runnable.find_all { |m| ran?(m) }
end
end
+ def migrations
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
+ end
+
def pending_migrations
already_migrated = migrated
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
+ migrations.reject { |m| already_migrated.include?(m.version) }
end
def migrated
- @migrated_versions ||= self.class.get_all_versions
+ @migrated_versions ||= Set.new(self.class.get_all_versions)
end
private
- def record_version_state_after_migrating(version)
- table = Arel::Table.new(self.class.schema_migrations_table_name)
-
- @migrated_versions ||= []
- if down?
- @migrated_versions.delete(version)
- stmt = table.where(table["version"].eq(version.to_s)).compile_delete
- Base.connection.delete stmt
- else
- @migrated_versions.push(version).sort!
- stmt = table.compile_insert table["version"] => version.to_s
- Base.connection.insert stmt
- end
- end
+ def ran?(migration)
+ migrated.include?(migration.version.to_i)
+ end
- def up?
- @direction == :up
- end
+ def target
+ migrations.detect { |m| m.version == @target_version }
+ end
+
+ def finish
+ migrations.index(target) || migrations.size - 1
+ end
- def down?
- @direction == :down
+ def start
+ up? ? 0 : (migrations.index(current) || 0)
+ end
+
+ def validate(migrations)
+ name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
+ raise DuplicateMigrationNameError.new(name) if name
+
+ version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
+ raise DuplicateMigrationVersionError.new(version) if version
+ end
+
+ def record_version_state_after_migrating(version)
+ if down?
+ migrated.delete(version)
+ ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
+ else
+ migrated << version
+ ActiveRecord::SchemaMigration.create!(:version => version.to_s)
end
+ end
- # Wrap the migration in a transaction only if supported by the adapter.
- def ddl_transaction(&block)
- if Base.connection.supports_ddl_transactions?
- Base.transaction { block.call }
- else
- block.call
- end
+ def up?
+ @direction == :up
+ end
+
+ def down?
+ @direction == :down
+ end
+
+ # Wrap the migration in a transaction only if supported by the adapter.
+ def ddl_transaction
+ if Base.connection.supports_ddl_transactions?
+ Base.transaction { yield }
+ else
+ yield
end
+ end
end
end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index ffee5a081a..96b62fdd61 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -8,11 +8,14 @@ module ActiveRecord
# * add_index
# * add_timestamps
# * create_table
+ # * create_join_table
# * remove_timestamps
# * rename_column
# * rename_index
# * rename_table
class CommandRecorder
+ include JoinTable
+
attr_accessor :commands, :delegate
def initialize(delegate = nil)
@@ -48,7 +51,7 @@ module ActiveRecord
super || delegate.respond_to?(*args)
end
- [:create_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
+ [:create_table, :create_join_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method}(*args) # def create_table(*args)
record(:"#{method}", args) # record(:create_table, args)
@@ -59,7 +62,13 @@ module ActiveRecord
private
def invert_create_table(args)
- [:drop_table, args]
+ [:drop_table, [args.first]]
+ end
+
+ def invert_create_join_table(args)
+ table_name = find_join_table_name(*args)
+
+ [:drop_table, [table_name]]
end
def invert_rename_table(args)
@@ -99,7 +108,6 @@ module ActiveRecord
rescue NoMethodError => e
raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@delegate}")
end
-
end
end
end
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
new file mode 100644
index 0000000000..01a580781b
--- /dev/null
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -0,0 +1,17 @@
+module ActiveRecord
+ class Migration
+ module JoinTable #:nodoc:
+ private
+
+ def find_join_table_name(table_1, table_2, options = {})
+ options.delete(:table_name) { join_table_name(table_1, table_2) }
+ end
+
+ def join_table_name(table_1, table_2)
+ tables_names = [table_1, table_2].map(&:to_s).sort
+
+ tables_names.join("_").to_sym
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
new file mode 100644
index 0000000000..86de5ab2fa
--- /dev/null
+++ b/activerecord/lib/active_record/model.rb
@@ -0,0 +1,109 @@
+require 'active_support/deprecation'
+
+module ActiveRecord
+ # <tt>ActiveRecord::Model</tt> can be included into a class to add Active Record persistence.
+ # This is an alternative to inheriting from <tt>ActiveRecord::Base</tt>. Example:
+ #
+ # class Post
+ # include ActiveRecord::Model
+ # end
+ #
+ module Model
+ module ClassMethods #:nodoc:
+ include ActiveSupport::Callbacks::ClassMethods
+ include ActiveModel::Naming
+ include QueryCache::ClassMethods
+ include ActiveSupport::Benchmarkable
+ include ActiveSupport::DescendantsTracker
+
+ include Querying
+ include Translation
+ include DynamicMatchers
+ include CounterCache
+ include Explain
+ include ConnectionHandling
+ end
+
+ def self.included(base)
+ return if base.singleton_class < ClassMethods
+
+ base.class_eval do
+ extend ClassMethods
+ Callbacks::Register.setup(self)
+ initialize_generated_modules unless self == Base
+ end
+ end
+
+ extend ActiveModel::Configuration
+ extend ActiveModel::Callbacks
+ extend ActiveModel::MassAssignmentSecurity::ClassMethods
+ extend ActiveModel::AttributeMethods::ClassMethods
+ extend Callbacks::Register
+ extend Explain
+ extend ConnectionHandling
+
+ def self.extend(*modules)
+ ClassMethods.send(:include, *modules)
+ end
+
+ include Persistence
+ include ReadonlyAttributes
+ include ModelSchema
+ include Inheritance
+ include Scoping
+ include Sanitization
+ include Integration
+ include AttributeAssignment
+ include ActiveModel::Conversion
+ include Validations
+ include Locking::Optimistic, Locking::Pessimistic
+ include AttributeMethods
+ include Callbacks, ActiveModel::Observing, Timestamp
+ include Associations
+ include IdentityMap
+ include ActiveModel::SecurePassword
+ include AutosaveAssociation, NestedAttributes
+ include Aggregations, Transactions, Reflection, Serialization, Store
+ include Core
+
+ class << self
+ def arel_engine
+ self
+ end
+
+ def abstract_class?
+ false
+ end
+
+ def inheritance_column
+ 'type'
+ end
+ end
+
+ module DeprecationProxy #:nodoc:
+ class << self
+ instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$|^instance_eval$/ }
+
+ def method_missing(name, *args, &block)
+ if Model.respond_to?(name)
+ Model.send(name, *args, &block)
+ else
+ ActiveSupport::Deprecation.warn(
+ "The object passed to the active_record load hook was previously ActiveRecord::Base " \
+ "(a Class). Now it is ActiveRecord::Model (a Module). You have called `#{name}' which " \
+ "is only defined on ActiveRecord::Base. Please change your code so that it works with " \
+ "a module rather than a class. (Model is included in Base, so anything added to Model " \
+ "will be available on Base as well.)"
+ )
+ Base.send(name, *args, &block)
+ end
+ end
+
+ alias send method_missing
+ end
+ end
+ end
+
+ # Load Base at this point, because the active_record load hook is run in that file.
+ Base
+end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
new file mode 100644
index 0000000000..b8764217d3
--- /dev/null
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -0,0 +1,334 @@
+require 'active_support/concern'
+require 'active_support/core_ext/class/attribute_accessors'
+
+module ActiveRecord
+ module ModelSchema
+ extend ActiveSupport::Concern
+
+ included do
+ ##
+ # :singleton-method:
+ # Accessor for the prefix type that will be prepended to every primary key column name.
+ # The options are :table_name and :table_name_with_underscore. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+ config_attribute :primary_key_prefix_type, :global => true
+
+ ##
+ # :singleton-method:
+ # Accessor for the name of the prefix string to prepend to every table name. So if set
+ # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
+ # etc. This is a convenient way of creating a namespace for tables in a shared database.
+ # By default, the prefix is the empty string.
+ #
+ # If you are organising your models within modules you can add a prefix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
+ # returns your chosen prefix.
+ config_attribute :table_name_prefix
+ self.table_name_prefix = ""
+
+ ##
+ # :singleton-method:
+ # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
+ # "people_basecamp"). By default, the suffix is the empty string.
+ config_attribute :table_name_suffix
+ self.table_name_suffix = ""
+
+ ##
+ # :singleton-method:
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
+ # See table_name for the full rules on table/class naming. This is true, by default.
+ config_attribute :pluralize_table_names
+ self.pluralize_table_names = true
+ end
+
+ module ClassMethods
+ # Guesses the table name (in forced lower-case) based on the name of the class in the
+ # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
+ # looks like: Reply < Message < ActiveRecord::Base, then Message is used
+ # to guess the table name even when called on Reply. The rules used to do the guess
+ # are handled by the Inflector class in Active Support, which knows almost all common
+ # English inflections. You can add new inflections in config/initializers/inflections.rb.
+ #
+ # Nested classes are given table names prefixed by the singular form of
+ # the parent's table name. Enclosing modules are not considered.
+ #
+ # ==== Examples
+ #
+ # class Invoice < ActiveRecord::Base
+ # end
+ #
+ # file class table_name
+ # invoice.rb Invoice invoices
+ #
+ # class Invoice < ActiveRecord::Base
+ # class Lineitem < ActiveRecord::Base
+ # end
+ # end
+ #
+ # file class table_name
+ # invoice.rb Invoice::Lineitem invoice_lineitems
+ #
+ # module Invoice
+ # class Lineitem < ActiveRecord::Base
+ # end
+ # end
+ #
+ # file class table_name
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
+ #
+ # Additionally, the class-level +table_name_prefix+ is prepended and the
+ # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
+ # the table name guess for an Invoice class becomes "myapp_invoices".
+ # Invoice::Lineitem becomes "myapp_invoice_lineitems".
+ #
+ # You can also set your own table name explicitly:
+ #
+ # class Mouse < ActiveRecord::Base
+ # self.table_name = "mice"
+ # end
+ #
+ # Alternatively, you can override the table_name method to define your
+ # own computation. (Possibly using <tt>super</tt> to manipulate the default
+ # table name.) Example:
+ #
+ # class Post < ActiveRecord::Base
+ # def self.table_name
+ # "special_" + super
+ # end
+ # end
+ # Post.table_name # => "special_posts"
+ def table_name
+ reset_table_name unless defined?(@table_name)
+ @table_name
+ end
+
+ # Sets the table name explicitly. Example:
+ #
+ # class Project < ActiveRecord::Base
+ # self.table_name = "project"
+ # end
+ #
+ # You can also just define your own <tt>self.table_name</tt> method; see
+ # the documentation for ActiveRecord::Base#table_name.
+ def table_name=(value)
+ @original_table_name = @table_name if defined?(@table_name)
+ @table_name = value && value.to_s
+ @quoted_table_name = nil
+ @arel_table = nil
+ @relation = Relation.new(self, arel_table)
+ end
+
+ # Returns a quoted version of the table name, used to construct SQL statements.
+ def quoted_table_name
+ @quoted_table_name ||= connection.quote_table_name(table_name)
+ end
+
+ # Computes the table name, (re)sets it internally, and returns it.
+ def reset_table_name #:nodoc:
+ if abstract_class?
+ self.table_name = if active_record_super == Base || active_record_super.abstract_class?
+ nil
+ else
+ active_record_super.table_name
+ end
+ elsif active_record_super.abstract_class?
+ self.table_name = active_record_super.table_name || compute_table_name
+ else
+ self.table_name = compute_table_name
+ end
+ end
+
+ def full_table_name_prefix #:nodoc:
+ (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
+ end
+
+ # The name of the column containing the object's class when Single Table Inheritance is used
+ def inheritance_column
+ (@inheritance_column ||= nil) || active_record_super.inheritance_column
+ end
+
+ # Sets the value of inheritance_column
+ def inheritance_column=(value)
+ @original_inheritance_column = inheritance_column
+ @inheritance_column = value.to_s
+ end
+
+ def sequence_name
+ if base_class == self
+ @sequence_name ||= reset_sequence_name
+ else
+ (@sequence_name ||= nil) || base_class.sequence_name
+ end
+ end
+
+ def reset_sequence_name #:nodoc:
+ self.sequence_name = connection.default_sequence_name(table_name, primary_key)
+ end
+
+ # Sets the name of the sequence to use when generating ids to the given
+ # value, or (if the value is nil or false) to the value returned by the
+ # given block. This is required for Oracle and is useful for any
+ # database which relies on sequences for primary key generation.
+ #
+ # If a sequence name is not explicitly set when using Oracle or Firebird,
+ # it will default to the commonly used pattern of: #{table_name}_seq
+ #
+ # If a sequence name is not explicitly set when using PostgreSQL, it
+ # will discover the sequence corresponding to your primary key for you.
+ #
+ # class Project < ActiveRecord::Base
+ # self.sequence_name = "projectseq" # default would have been "project_seq"
+ # end
+ def sequence_name=(value)
+ @original_sequence_name = @sequence_name if defined?(@sequence_name)
+ @sequence_name = value.to_s
+ end
+
+ # Indicates whether the table associated with this class exists
+ def table_exists?
+ connection.schema_cache.table_exists?(table_name)
+ end
+
+ # Returns an array of column objects for the table associated with this class.
+ def columns
+ @columns ||= connection.schema_cache.columns[table_name].map do |col|
+ col = col.dup
+ col.primary = (col.name == primary_key)
+ col
+ end
+ end
+
+ # Returns a hash of column objects for the table associated with this class.
+ def columns_hash
+ @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
+ end
+
+ def column_types # :nodoc:
+ @column_types ||= decorate_columns(columns_hash.dup)
+ end
+
+ def decorate_columns(columns_hash) # :nodoc:
+ return if columns_hash.empty?
+
+ serialized_attributes.keys.each do |key|
+ columns_hash[key] = AttributeMethods::Serialization::Type.new(columns_hash[key])
+ end
+
+ columns_hash.each do |name, col|
+ if create_time_zone_conversion_attribute?(name, col)
+ columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(col)
+ end
+ end
+
+ columns_hash
+ end
+
+ # Returns a hash where the keys are column names and the values are
+ # default values when instantiating the AR object for this table.
+ def column_defaults
+ @column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
+ end
+
+ # Returns an array of column names as strings.
+ def column_names
+ @column_names ||= columns.map { |column| column.name }
+ end
+
+ # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
+ # and columns used for single table inheritance have been removed.
+ def content_columns
+ @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
+ end
+
+ # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
+ # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
+ # is available.
+ def column_methods_hash #:nodoc:
+ @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
+ attr_name = attr.to_s
+ methods[attr.to_sym] = attr_name
+ methods["#{attr}=".to_sym] = attr_name
+ methods["#{attr}?".to_sym] = attr_name
+ methods["#{attr}_before_type_cast".to_sym] = attr_name
+ methods
+ end
+ end
+
+ # Resets all the cached information about columns, which will cause them
+ # to be reloaded on the next request.
+ #
+ # The most common usage pattern for this method is probably in a migration,
+ # when just after creating a table you want to populate it with some default
+ # values, eg:
+ #
+ # class CreateJobLevels < ActiveRecord::Migration
+ # def up
+ # create_table :job_levels do |t|
+ # t.integer :id
+ # t.string :name
+ #
+ # t.timestamps
+ # end
+ #
+ # JobLevel.reset_column_information
+ # %w{assistant executive manager director}.each do |type|
+ # JobLevel.create(:name => type)
+ # end
+ # end
+ #
+ # def down
+ # drop_table :job_levels
+ # end
+ # end
+ def reset_column_information
+ connection.clear_cache!
+ undefine_attribute_methods
+ connection.schema_cache.clear_table_cache!(table_name) if table_exists?
+
+ @arel_engine = nil
+ @column_defaults = nil
+ @column_names = nil
+ @columns = nil
+ @columns_hash = nil
+ @column_types = nil
+ @content_columns = nil
+ @dynamic_methods_hash = nil
+ @inheritance_column = nil
+ @relation = nil
+ end
+
+ def clear_cache! # :nodoc:
+ connection.schema_cache.clear!
+ end
+
+ private
+
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
+ def undecorated_table_name(class_name = base_class.name)
+ table_name = class_name.to_s.demodulize.underscore
+ table_name = table_name.pluralize if pluralize_table_names
+ table_name
+ end
+
+ # Computes and returns a table name according to default conventions.
+ def compute_table_name
+ base = base_class
+ if self == base
+ # Nested classes are prefixed with singular parent table name.
+ if parent < ActiveRecord::Model && !parent.abstract_class?
+ contained = parent.table_name
+ contained = contained.singularize if parent.pluralize_table_names
+ contained += '_'
+ end
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
+ else
+ # STI subclasses always use their superclass' table.
+ base.table_name
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
deleted file mode 100644
index 0313abe456..0000000000
--- a/activerecord/lib/active_record/named_scope.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-require 'active_support/core_ext/array'
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/class/attribute'
-
-module ActiveRecord
- # = Active Record Named \Scopes
- module NamedScope
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Returns an anonymous \scope.
- #
- # posts = Post.scoped
- # posts.size # Fires "select count(*) from posts" and returns the count
- # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
- #
- # fruits = Fruit.scoped
- # fruits = fruits.where(:color => 'red') if options[:red_only]
- # fruits = fruits.limit(10) if limited?
- #
- # Anonymous \scopes tend to be useful when procedurally generating complex
- # queries, where passing 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 scoped(options = nil)
- if options
- scoped.apply_finder_options(options)
- else
- if current_scope
- current_scope.clone
- else
- scope = relation.clone
- scope.default_scoped = true
- scope
- end
- end
- end
-
- ##
- # Collects attributes from scopes that should be applied when creating
- # an AR instance for the particular class this is called on.
- def scope_attributes # :nodoc:
- if current_scope
- current_scope.scope_for_create
- else
- scope = relation.clone
- scope.default_scoped = true
- scope.scope_for_create
- end
- end
-
- ##
- # Are there default attributes associated with this scope?
- def scope_attributes? # :nodoc:
- current_scope || default_scopes.any?
- end
-
- # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
- # such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
- #
- # class Shirt < ActiveRecord::Base
- # scope :red, where(:color => 'red')
- # scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
- # end
- #
- # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
- # in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
- #
- # Note that this is simply 'syntactic sugar' for defining an actual class method:
- #
- # class Shirt < ActiveRecord::Base
- # def self.red
- # where(:color => 'red')
- # end
- # end
- #
- # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
- # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
- # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
- # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable;
- # <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
- # all behave as if Shirt.red really was an Array.
- #
- # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce
- # all shirts that are both red and dry clean only.
- # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
- # returns the number of garments for which these criteria obtain. Similarly with
- # <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
- #
- # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which
- # the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If,
- #
- # class Person < ActiveRecord::Base
- # has_many :shirts
- # end
- #
- # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
- # only shirts.
- #
- # Named \scopes can also be procedural:
- #
- # class Shirt < ActiveRecord::Base
- # scope :colored, lambda { |color| where(:color => color) }
- # end
- #
- # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
- #
- # On Ruby 1.9 you can use the 'stabby lambda' syntax:
- #
- # scope :colored, ->(color) { where(:color => color) }
- #
- # Note that scopes defined with \scope will be evaluated when they are defined, rather than
- # when they are used. For example, the following would be incorrect:
- #
- # class Post < ActiveRecord::Base
- # scope :recent, where('published_at >= ?', Time.now - 1.week)
- # end
- #
- # The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt>
- # class was defined, and so the resultant SQL query would always be the same. The correct
- # way to do this would be via a lambda, which will re-evaluate the scope each time
- # it is called:
- #
- # class Post < ActiveRecord::Base
- # scope :recent, lambda { where('published_at >= ?', Time.now - 1.week) }
- # end
- #
- # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
- #
- # class Shirt < ActiveRecord::Base
- # scope :red, where(:color => 'red') do
- # def dom_id
- # 'red_shirts'
- # end
- # end
- # end
- #
- # Scopes can also be used while creating/building a record.
- #
- # class Article < ActiveRecord::Base
- # scope :published, where(:published => true)
- # end
- #
- # Article.published.new.published # => true
- # Article.published.create.published # => true
- #
- # Class methods on your model are automatically available
- # on scopes. Assuming the following setup:
- #
- # class Article < ActiveRecord::Base
- # scope :published, where(:published => true)
- # scope :featured, where(:featured => true)
- #
- # def self.latest_article
- # order('published_at desc').first
- # end
- #
- # def self.titles
- # map(&:title)
- # end
- #
- # end
- #
- # We are able to call the methods like this:
- #
- # Article.published.featured.latest_article
- # Article.featured.titles
-
- def scope(name, scope_options = {})
- name = name.to_sym
- valid_scope_name?(name)
- extension = Module.new(&Proc.new) if block_given?
-
- scope_proc = lambda do |*args|
- options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options
- options = scoped.apply_finder_options(options) if options.is_a?(Hash)
-
- relation = scoped.merge(options)
-
- extension ? relation.extending(extension) : relation
- end
-
- singleton_class.send(:redefine_method, name, &scope_proc)
- end
-
- protected
-
- def valid_scope_name?(name)
- if respond_to?(name, true)
- logger.warn "Creating scope :#{name}. " \
- "Overwriting existing method #{self.name}.#{name}."
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index d2065d701f..9e21039c4f 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -12,7 +12,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :nested_attributes_options, :instance_writer => false
+ config_attribute :nested_attributes_options
self.nested_attributes_options = {}
end
@@ -382,7 +382,7 @@ module ActiveRecord
if attributes_collection.is_a? Hash
keys = attributes_collection.keys
attributes_collection = if keys.include?('id') || keys.include?(:id)
- Array.wrap(attributes_collection)
+ [attributes_collection]
else
attributes_collection.values
end
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
new file mode 100644
index 0000000000..60c37ac2b7
--- /dev/null
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+module ActiveRecord
+ # = Active Record Null Relation
+ class NullRelation < Relation
+ def exec_queries
+ @records = []
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 5e65e46a7d..9bc046c775 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -1,6 +1,53 @@
+require 'active_support/concern'
+
module ActiveRecord
# = Active Record Persistence
module Persistence
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
+ # The resulting object is returned whether the object was saved successfully to the database or not.
+ #
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
+ # attributes on the objects that are to be created.
+ #
+ # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
+ # in the +options+ parameter.
+ #
+ # ==== Examples
+ # # Create a single new object
+ # User.create(:first_name => 'Jamie')
+ #
+ # # Create a single new object using the :admin mass-assignment security role
+ # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
+ #
+ # # Create a single new object bypassing mass-assignment security
+ # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
+ #
+ # # Create an Array of new objects
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
+ #
+ # # Create a single object and pass it into a block to set other attributes.
+ # User.create(:first_name => 'Jamie') do |u|
+ # u.is_admin = false
+ # end
+ #
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
+ # u.is_admin = false
+ # end
+ def create(attributes = nil, options = {}, &block)
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create(attr, options, &block) }
+ else
+ object = new(attributes, options, &block)
+ object.save
+ object
+ end
+ end
+ end
+
# Returns true if this object hasn't been saved yet -- that is, a record
# for the object doesn't exist in the data store yet; otherwise, returns false.
def new_record?
@@ -12,8 +59,8 @@ module ActiveRecord
@destroyed
end
- # Returns if the record is persisted, i.e. it's not a new record and it was
- # not destroyed.
+ # Returns true if the record is persisted, i.e. it's not a new record and it was
+ # not destroyed, otherwise returns false.
def persisted?
!(new_record? || destroyed?)
end
@@ -114,6 +161,7 @@ module ActiveRecord
became.instance_variable_set("@attributes_cache", @attributes_cache)
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
+ became.instance_variable_set("@errors", errors)
became.type = klass.name unless self.class.descends_from_active_record?
became
end
@@ -161,7 +209,7 @@ module ActiveRecord
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
- self.assign_attributes(attributes, options)
+ assign_attributes(attributes, options)
save
end
end
@@ -172,7 +220,7 @@ module ActiveRecord
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
- self.assign_attributes(attributes, options)
+ assign_attributes(attributes, options)
save!
end
end
@@ -237,8 +285,9 @@ module ActiveRecord
clear_association_cache
IdentityMap.without do
- fresh_object = self.class.unscoped { self.class.find(self.id, options) }
+ fresh_object = self.class.unscoped { self.class.find(id, options) }
@attributes.update(fresh_object.instance_variable_get('@attributes'))
+ @columns_hash = fresh_object.instance_variable_get('@columns_hash')
end
@attributes_cache = {}
@@ -320,13 +369,5 @@ module ActiveRecord
@new_record = false
id
end
-
- # Initializes the attributes array with keys matching the columns from the linked table and
- # the values matching the corresponding default value of that column, so
- # that a new instance, or one populated from a passed-in Hash, still has all the attributes
- # that instances loaded from the database would.
- def attributes_from_column_definition
- self.class.column_defaults.dup
- end
end
end
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 466d148901..9701898415 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -27,47 +27,22 @@ module ActiveRecord
@app = app
end
- class BodyProxy # :nodoc:
- def initialize(original_cache_value, target, connection_id)
- @original_cache_value = original_cache_value
- @target = target
- @connection_id = connection_id
- end
-
- def method_missing(method_sym, *arguments, &block)
- @target.send(method_sym, *arguments, &block)
- end
-
- def respond_to?(method_sym, include_private = false)
- super || @target.respond_to?(method_sym)
- end
-
- def each(&block)
- @target.each(&block)
- end
+ def call(env)
+ enabled = ActiveRecord::Base.connection.query_cache_enabled
+ connection_id = ActiveRecord::Base.connection_id
+ ActiveRecord::Base.connection.enable_query_cache!
- def close
- @target.close if @target.respond_to?(:close)
- ensure
- ActiveRecord::Base.connection_id = @connection_id
+ response = @app.call(env)
+ response[2] = Rack::BodyProxy.new(response[2]) do
+ ActiveRecord::Base.connection_id = connection_id
ActiveRecord::Base.connection.clear_query_cache
- unless @original_cache_value
- ActiveRecord::Base.connection.disable_query_cache!
- end
+ ActiveRecord::Base.connection.disable_query_cache! unless enabled
end
- end
- def call(env)
- old = ActiveRecord::Base.connection.query_cache_enabled
- ActiveRecord::Base.connection.enable_query_cache!
-
- status, headers, body = @app.call(env)
- [status, headers, BodyProxy.new(old, body, ActiveRecord::Base.connection_id)]
+ response
rescue Exception => e
ActiveRecord::Base.connection.clear_query_cache
- unless old
- ActiveRecord::Base.connection.disable_query_cache!
- end
+ ActiveRecord::Base.connection.disable_query_cache! unless enabled
raise e
end
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
new file mode 100644
index 0000000000..0e6fecbc4b
--- /dev/null
+++ b/activerecord/lib/active_record/querying.rb
@@ -0,0 +1,68 @@
+require 'active_support/core_ext/module/delegation'
+require 'active_support/deprecation'
+
+module ActiveRecord
+ module Querying
+ delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
+ delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped
+ delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
+ delegate :find_each, :find_in_batches, :to => :scoped
+ delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
+ :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
+ :having, :create_with, :uniq, :references, :none, :to => :scoped
+ delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :to => :scoped
+
+ # Executes a custom SQL query against your database and returns all the results. The results will
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
+ # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
+ # a Product object with the attributes you specified in the SQL query.
+ #
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
+ # table.
+ #
+ # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
+ # MySQL specific terms will lock you to using that particular database engine or require you to
+ # change your call if you switch engines.
+ #
+ # ==== Examples
+ # # A simple SQL query spanning multiple tables
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
+ # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
+ #
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
+ # > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
+ def find_by_sql(sql, binds = [])
+ logging_query_plan do
+ result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
+ column_types = {}
+
+ if result_set.respond_to? :column_types
+ column_types = result_set.column_types
+ else
+ ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
+ end
+
+ result_set.map { |record| instantiate(record, column_types) }
+ end
+ end
+
+ # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
+ #
+ # ==== Parameters
+ #
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
+ #
+ # ==== Examples
+ #
+ # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
+ def count_by_sql(sql)
+ sql = sanitize_conditions(sql)
+ connection.select_value(sql, "#{name} Count").to_i
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 47133e77e8..058dd58efb 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -22,6 +22,13 @@ module ActiveRecord
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
+ config.action_dispatch.rescue_responses.merge!(
+ 'ActiveRecord::RecordNotFound' => :not_found,
+ 'ActiveRecord::StaleObjectError' => :conflict,
+ 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
+ 'ActiveRecord::RecordNotSaved' => :unprocessable_entity
+ )
+
rake_tasks do
load "active_record/railties/databases.rake"
end
@@ -31,7 +38,8 @@ module ActiveRecord
# first time. Also, make it output to STDERR.
console do |app|
require "active_record/railties/console_sandbox" if app.sandbox?
- ActiveRecord::Base.logger = Logger.new(STDERR)
+ console = ActiveSupport::Logger.new(STDERR)
+ Rails.logger.extend ActiveSupport::Logger.broadcast console
end
initializer "active_record.initialize_timezone" do
@@ -78,18 +86,30 @@ module ActiveRecord
end
end
- initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
- ActiveSupport.on_load(:active_record) do
- ActionDispatch::Reloader.to_cleanup do
- ActiveRecord::Base.clear_reloadable_connections!
- ActiveRecord::Base.clear_cache!
+ initializer "active_record.set_reloader_hooks" do |app|
+ hook = lambda do
+ ActiveRecord::Base.clear_reloadable_connections!
+ ActiveRecord::Base.clear_cache!
+ end
+
+ if app.config.reload_classes_only_on_change
+ ActiveSupport.on_load(:active_record) do
+ ActionDispatch::Reloader.to_prepare(&hook)
+ end
+ else
+ ActiveSupport.on_load(:active_record) do
+ ActionDispatch::Reloader.to_cleanup(&hook)
end
end
end
+ initializer "active_record.add_watchable_files" do |app|
+ config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"]
+ end
+
config.after_initialize do
ActiveSupport.on_load(:active_record) do
- instantiate_observers
+ ActiveRecord::Base.instantiate_observers
ActionDispatch::Reloader.to_prepare do
ActiveRecord::Base.instantiate_observers
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 44848b3391..14bb0a724e 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -1,8 +1,8 @@
require 'active_support/core_ext/object/inclusion'
+require 'active_record'
db_namespace = namespace :db do
task :load_config => :rails_env do
- require 'active_record'
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
@@ -38,6 +38,7 @@ db_namespace = namespace :db do
desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
task :create => :load_config do
configs_for_environment.each { |config| create_database(config) }
+ ActiveRecord::Base.establish_connection(configs_for_environment.first)
end
def mysql_creation_options(config)
@@ -111,8 +112,7 @@ db_namespace = namespace :db do
end
end
else
- # Bug with 1.9.2 Calling return within begin still executes else
- $stderr.puts "#{config['database']} already exists" unless config['adapter'] =~ /sqlite/
+ $stderr.puts "#{config['database']} already exists"
end
end
@@ -149,8 +149,22 @@ db_namespace = namespace :db do
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
task :migrate => [:environment, :load_config] do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
- ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
- db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
+ ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
+ end
+ db_namespace['_dump'].invoke
+ end
+
+ task :_dump do
+ case ActiveRecord::Base.schema_format
+ when :ruby then db_namespace["schema:dump"].invoke
+ when :sql then db_namespace["structure:dump"].invoke
+ else
+ raise "unknown schema format #{ActiveRecord::Base.schema_format}"
+ end
+ # Allow this task to be called as many times as required. An example is the
+ # migrate:redo task, which calls other two internally that depend on this one.
+ db_namespace['_dump'].reenable
end
namespace :migrate do
@@ -173,7 +187,7 @@ db_namespace = namespace :db do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
+ db_namespace['_dump'].invoke
end
# desc 'Runs the "down" for a given migration VERSION.'
@@ -181,7 +195,7 @@ db_namespace = namespace :db do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
+ db_namespace['_dump'].invoke
end
desc 'Display status of migrations'
@@ -193,11 +207,12 @@ db_namespace = namespace :db do
next # means "return" for rake task
end
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
+ db_list.map! { |version| "%.3d" % version }
file_list = []
ActiveRecord::Migrator.migrations_paths.each do |path|
Dir.foreach(path) do |file|
- # only files matching "20091231235959_some_name.rb" pattern
- if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
+ # match "20091231235959_some_name.rb" and "001_some_name.rb" pattern
+ if match_data = /^(\d{3,})_(.+)\.rb$/.match(file)
status = db_list.delete(match_data[1]) ? 'up' : 'down'
file_list << [status, match_data[1], match_data[2].humanize]
end
@@ -221,18 +236,21 @@ db_namespace = namespace :db do
task :rollback => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
+ db_namespace['_dump'].invoke
end
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
task :forward => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
- db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
+ db_namespace['_dump'].invoke
end
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
- task :reset => [ 'db:drop', 'db:setup' ]
+ task :reset => :environment do
+ db_namespace["drop"].invoke
+ db_namespace["setup"].invoke
+ end
# desc "Retrieves the charset for the current environment's database"
task :charset => :environment do
@@ -271,24 +289,23 @@ db_namespace = namespace :db do
# desc "Raises an error if there are pending migrations"
task :abort_if_pending_migrations => :environment do
- if defined? ActiveRecord
- pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
+ pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
- if pending_migrations.any?
- puts "You have #{pending_migrations.size} pending migrations:"
- pending_migrations.each do |pending_migration|
- puts ' %4d %s' % [pending_migration.version, pending_migration.name]
- end
- abort %{Run `rake db:migrate` to update your database then try again.}
+ if pending_migrations.any?
+ puts "You have #{pending_migrations.size} pending migrations:"
+ pending_migrations.each do |pending_migration|
+ puts ' %4d %s' % [pending_migration.version, pending_migration.name]
end
+ abort %{Run `rake db:migrate` to update your database then try again.}
end
end
desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)'
- task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ]
+ task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed]
desc 'Load the seed data from db/seeds.rb'
- task :seed => 'db:abort_if_pending_migrations' do
+ task :seed do
+ db_namespace['abort_if_pending_migrations'].invoke
Rails.application.load_seed
end
@@ -351,90 +368,122 @@ db_namespace = namespace :db do
abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded}
end
end
+
+ task :load_if_ruby => 'db:create' do
+ db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
+ end
end
namespace :structure do
- desc 'Dump the database structure to an SQL file'
+ desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql'
task :dump => :environment do
abcs = ActiveRecord::Base.configurations
+ filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case abcs[Rails.env]['adapter']
when /mysql/, 'oci', 'oracle'
ActiveRecord::Base.establish_connection(abcs[Rails.env])
- File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
+ File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
when /postgresql/
- ENV['PGHOST'] = abcs[Rails.env]['host'] if abcs[Rails.env]['host']
- ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]['port']
- ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password']
+ set_psql_env(abcs[Rails.env])
search_path = abcs[Rails.env]['schema_search_path']
unless search_path.blank?
search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ")
end
- `pg_dump -i -U "#{abcs[Rails.env]['username']}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]['database']}`
+ `pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}`
raise 'Error dumping database' if $?.exitstatus == 1
when /sqlite/
- dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile']
- `sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql`
+ dbfile = abcs[Rails.env]['database']
+ `sqlite3 #{dbfile} .schema > #{filename}`
when 'sqlserver'
- `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\#{Rails.env}_structure.sql -A -U`
+ `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f #{filename} -A -U`
when "firebird"
set_firebird_env(abcs[Rails.env])
db_string = firebird_db_string(abcs[Rails.env])
- sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_structure.sql"
+ sh "isql -a #{db_string} > #{filename}"
else
raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
end
if ActiveRecord::Base.connection.supports_migrations?
- File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
+ File.open(filename, "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
end
end
- end
-
- namespace :test do
- # desc "Recreate the test database from the current schema.rb"
- task :load => 'db:test:purge' do
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
- ActiveRecord::Schema.verbose = false
- db_namespace['schema:load'].invoke
- end
- # desc "Recreate the test database from the current environment's database schema"
- task :clone => %w(db:schema:dump db:test:load)
+ # desc "Recreate the databases from the structure.sql file"
+ task :load => [:environment, :load_config] do
+ env = ENV['RAILS_ENV'] || 'test'
- # desc "Recreate the test databases from the development structure"
- task :clone_structure => [ 'db:structure:dump', 'db:test:purge' ] do
abcs = ActiveRecord::Base.configurations
- case abcs['test']['adapter']
+ filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
+ case abcs[env]['adapter']
when /mysql/
- ActiveRecord::Base.establish_connection(:test)
+ ActiveRecord::Base.establish_connection(abcs[env])
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
- IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table|
+ IO.read(filename).split("\n\n").each do |table|
ActiveRecord::Base.connection.execute(table)
end
when /postgresql/
- ENV['PGHOST'] = abcs['test']['host'] if abcs['test']['host']
- ENV['PGPORT'] = abcs['test']['port'].to_s if abcs['test']['port']
- ENV['PGPASSWORD'] = abcs['test']['password'].to_s if abcs['test']['password']
- `psql -U "#{abcs['test']['username']}" -f "#{Rails.root}/db/#{Rails.env}_structure.sql" #{abcs['test']['database']} #{abcs['test']['template']}`
+ set_psql_env(abcs[env])
+ `psql -f "#{filename}" #{abcs[env]['database']}`
when /sqlite/
- dbfile = abcs['test']['database'] || abcs['test']['dbfile']
- `sqlite3 #{dbfile} < "#{Rails.root}/db/#{Rails.env}_structure.sql"`
+ dbfile = abcs[env]['database']
+ `sqlite3 #{dbfile} < "#{filename}"`
when 'sqlserver'
- `sqlcmd -S #{abcs['test']['host']} -d #{abcs['test']['database']} -U #{abcs['test']['username']} -P #{abcs['test']['password']} -i db\\#{Rails.env}_structure.sql`
+ `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
when 'oci', 'oracle'
- ActiveRecord::Base.establish_connection(:test)
- IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl|
+ ActiveRecord::Base.establish_connection(abcs[env])
+ IO.read(filename).split(";\n\n").each do |ddl|
ActiveRecord::Base.connection.execute(ddl)
end
when 'firebird'
- set_firebird_env(abcs['test'])
- db_string = firebird_db_string(abcs['test'])
- sh "isql -i #{Rails.root}/db/#{Rails.env}_structure.sql #{db_string}"
+ set_firebird_env(abcs[env])
+ db_string = firebird_db_string(abcs[env])
+ sh "isql -i #{filename} #{db_string}"
else
- raise "Task not supported by '#{abcs['test']['adapter']}'"
+ raise "Task not supported by '#{abcs[env]['adapter']}'"
end
end
+ task :load_if_sql => 'db:create' do
+ db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
+ end
+ end
+
+ namespace :test do
+
+ # desc "Recreate the test database from the current schema"
+ task :load => 'db:test:purge' do
+ case ActiveRecord::Base.schema_format
+ when :ruby
+ db_namespace["test:load_schema"].invoke
+ when :sql
+ db_namespace["test:load_structure"].invoke
+ end
+ end
+
+ # desc "Recreate the test database from an existent structure.sql file"
+ task :load_structure => 'db:test:purge' do
+ begin
+ old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test'
+ db_namespace["structure:load"].invoke
+ ensure
+ ENV['RAILS_ENV'] = old_env
+ end
+ end
+
+ # desc "Recreate the test database from an existent schema.rb file"
+ task :load_schema => 'db:test:purge' do
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
+ ActiveRecord::Schema.verbose = false
+ db_namespace["schema:load"].invoke
+ end
+
+ # desc "Recreate the test database from a fresh schema.rb file"
+ task :clone => %w(db:schema:dump db:test:load_schema)
+
+ # desc "Recreate the test database from a fresh structure.sql file"
+ task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
+
# desc "Empty the test database"
task :purge => :environment do
abcs = ActiveRecord::Base.configurations
@@ -447,7 +496,7 @@ db_namespace = namespace :db do
drop_database(abcs['test'])
create_database(abcs['test'])
when /sqlite/
- dbfile = abcs['test']['database'] || abcs['test']['dbfile']
+ dbfile = abcs['test']['database']
File.delete(dbfile) if File.exist?(dbfile)
when 'sqlserver'
test = abcs.deep_dup['test']
@@ -470,7 +519,7 @@ db_namespace = namespace :db do
# desc 'Check for pending migrations and load the test schema'
task :prepare => 'db:abort_if_pending_migrations' do
- if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
+ unless ActiveRecord::Base.configurations.blank?
db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke
end
end
@@ -494,7 +543,7 @@ end
namespace :railties do
namespace :install do
- # desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
+ # desc "Copies missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2"
task :migrations => :'db:load_config' do
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
railties = ActiveSupport::OrderedHash.new
@@ -565,3 +614,10 @@ end
def firebird_db_string(config)
FireRuby::Database.db_string_for(config.symbolize_keys)
end
+
+def set_psql_env(config)
+ ENV['PGHOST'] = config['host'] if config['host']
+ ENV['PGPORT'] = config['port'].to_s if config['port']
+ ENV['PGPASSWORD'] = config['password'].to_s if config['password']
+ ENV['PGUSER'] = config['username'].to_s if config['username']
+end
diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb
index 6b9af2a0cb..0b75983484 100644
--- a/activerecord/lib/active_record/railties/jdbcmysql_error.rb
+++ b/activerecord/lib/active_record/railties/jdbcmysql_error.rb
@@ -1,5 +1,5 @@
#FIXME Remove if ArJdbcMysql will give.
-module ArJdbcMySQL
+module ArJdbcMySQL #:nodoc:
class Error < StandardError
attr_accessor :error_number, :sql_state
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
new file mode 100644
index 0000000000..836b15e2ce
--- /dev/null
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -0,0 +1,26 @@
+require 'active_support/concern'
+require 'active_support/core_ext/class/attribute'
+
+module ActiveRecord
+ module ReadonlyAttributes
+ extend ActiveSupport::Concern
+
+ included do
+ config_attribute :_attr_readonly
+ self._attr_readonly = []
+ end
+
+ module ClassMethods
+ # Attributes listed as readonly will be used to create a new record but update operations will
+ # ignore these fields.
+ def attr_readonly(*attributes)
+ self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
+ end
+
+ # Returns an array of all the attributes that have been specified as readonly.
+ def readonly_attributes
+ self._attr_readonly
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 5285060288..8f8a3ec3bb 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -7,7 +7,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :reflections
+ extend ActiveModel::Configuration
+ config_attribute :reflections
self.reflections = {}
end
@@ -124,7 +125,7 @@ module ActiveRecord
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
def class_name
- @class_name ||= options[:class_name] || derive_class_name
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
end
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
@@ -174,7 +175,7 @@ module ActiveRecord
def initialize(macro, name, options, active_record)
super
- @collection = macro.in?([:has_many, :has_and_belongs_to_many])
+ @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
end
# Returns a new, unsaved instance of the associated class. +options+ will
@@ -228,8 +229,8 @@ module ActiveRecord
end
end
- def columns(tbl_name, log_msg)
- @columns ||= klass.connection.columns(tbl_name, log_msg)
+ def columns(tbl_name)
+ @columns ||= klass.connection.columns(tbl_name)
end
def reset_column_information
@@ -262,6 +263,10 @@ module ActiveRecord
[self]
end
+ def nested?
+ false
+ end
+
# An array of arrays of conditions. Each item in the outside array corresponds to a reflection
# in the #chain. The inside arrays are simply conditions (and each condition may itself be
# a hash, array, arel predicate, etc...)
@@ -433,7 +438,7 @@ module ActiveRecord
# of relevant reflections, plus any :source_type or polymorphic :as constraints.
def conditions
@conditions ||= begin
- conditions = source_reflection.conditions
+ conditions = source_reflection.conditions.map { |c| c.dup }
# Add to it the conditions from this reflection if necessary.
conditions.first << options[:conditions] if options[:conditions]
@@ -457,7 +462,7 @@ module ActiveRecord
source_reflection.source_macro
end
- # A through association is nested iff there would be more than one join table
+ # A through association is nested if there would be more than one join table
def nested?
chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ecefaa633c..ac70aeba67 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -1,19 +1,16 @@
+# -*- coding: utf-8 -*-
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/module/delegation'
+require 'active_support/deprecation'
module ActiveRecord
# = Active Record Relation
class Relation
JoinOperation = Struct.new(:relation, :join_class, :on)
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
- MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order]
+ MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind, :references]
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq]
- include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
-
- # These are explicitly delegated to improve performance (avoids method_missing)
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :column_hash,:to => :klass
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
attr_reader :table, :klass, :loaded
attr_accessor :extensions, :default_scoped
@@ -136,14 +133,35 @@ module ActiveRecord
first || new(attributes, options, &block)
end
- def respond_to?(method, include_private = false)
- arel.respond_to?(method, include_private) ||
- Array.method_defined?(method) ||
- @klass.respond_to?(method, include_private) ||
- super
+ # Runs EXPLAIN on the query or queries triggered by this relation and
+ # returns the result as a string. The string is formatted imitating the
+ # ones printed by the database shell.
+ #
+ # Note that this method actually runs the queries, since the results of some
+ # are needed by the next ones when eager loading is going on.
+ #
+ # Please see further details in the
+ # {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
+ def explain
+ _, queries = collecting_queries_for_explain { exec_queries }
+ exec_explain(queries)
end
def to_a
+ # We monitor here the entire execution rather than individual SELECTs
+ # because from the point of view of the user fetching the records of a
+ # relation is a single unit of work. You want to know if this call takes
+ # too long, not if the individual queries take too long.
+ #
+ # It could be the case that none of the queries involved surpass the
+ # threshold, and at the same time the sum of them all does. The user
+ # should get a query plan logged in that case.
+ logging_query_plan do
+ exec_queries
+ end
+ end
+
+ def exec_queries
return @records if loaded?
default_scoped = with_default_scope
@@ -174,6 +192,7 @@ module ActiveRecord
@loaded = true
@records
end
+ private :exec_queries
def as_json(options = nil) #:nodoc:
to_a.as_json(options)
@@ -219,7 +238,7 @@ module ActiveRecord
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
- @klass.send(:with_scope, self, :overwrite) { yield }
+ @klass.with_scope(self, :overwrite) { yield }
end
# Updates all records with details given if they match a set of conditions supplied, limits and order can
@@ -337,7 +356,7 @@ module ActiveRecord
end
end
- # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
# therefore all callbacks and filters are fired off before the object is deleted. This method is
# less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
#
@@ -477,6 +496,10 @@ module ActiveRecord
to_a.inspect
end
+ def pretty_print(q)
+ q.pp(self.to_a)
+ end
+
def with_default_scope #:nodoc:
if default_scoped? && default_scope = klass.send(:build_default_scope)
default_scope = default_scope.merge(self)
@@ -487,20 +510,6 @@ module ActiveRecord
end
end
- protected
-
- def method_missing(method, *args, &block)
- if Array.method_defined?(method)
- to_a.send(method, *args, &block)
- elsif @klass.respond_to?(method)
- scoping { @klass.send(method, *args, &block) }
- elsif arel.respond_to?(method)
- arel.send(method, *args, &block)
- else
- super
- end
- end
-
private
def references_eager_loaded_tables?
@@ -516,8 +525,30 @@ module ActiveRecord
# always convert table names to downcase as in Oracle quoted table names are in uppercase
joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
-
- (tables_in_string(to_sql) - joined_tables).any?
+ string_tables = tables_in_string(to_sql)
+
+ if (references_values - joined_tables).any?
+ true
+ elsif (string_tables - joined_tables).any?
+ ActiveSupport::Deprecation.warn(
+ "It looks like you are eager loading table(s) (one of: #{string_tables.join(', ')}) " \
+ "that are referenced in a string SQL snippet. For example: \n" \
+ "\n" \
+ " Post.includes(:comments).where(\"comments.title = 'foo'\")\n" \
+ "\n" \
+ "Currently, Active Record recognises the table in the string, and knows to JOIN the " \
+ "comments table to the query, rather than loading comments in a separate query. " \
+ "However, doing this without writing a full-blown SQL parser is inherently flawed. " \
+ "Since we don't want to write an SQL parser, we are removing this functionality. " \
+ "From now on, you must explicitly tell Active Record when you are referencing a table " \
+ "from a string:\n" \
+ "\n" \
+ " Post.includes(:comments).where(\"comments.title = 'foo'\").references(:comments)\n\n"
+ )
+ true
+ else
+ false
+ end
end
def tables_in_string(string)
@@ -526,6 +557,5 @@ module ActiveRecord
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
end
-
end
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index af86771d2d..63365e501b 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -166,6 +166,37 @@ module ActiveRecord
0
end
+ # This method is designed to perform select by a single column as direct SQL query
+ # Returns <tt>Array</tt> with values of the specified column name
+ # The values has same data type as column.
+ #
+ # Examples:
+ #
+ # Person.pluck(:id) # SELECT people.id FROM people
+ # Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
+ # Person.where(:confirmed => true).limit(5).pluck(:id)
+ #
+ def pluck(column_name)
+ key = column_name.to_s.split('.', 2).last
+
+ if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
+ column_name = "#{table_name}.#{column_name}"
+ end
+
+ result = klass.connection.select_all(select(column_name).arel)
+ types = result.column_types.merge klass.column_types
+ column = types[key]
+
+ result.map do |attributes|
+ value = klass.initialize_attributes(attributes)[key]
+ if column
+ column.type_cast value
+ else
+ value
+ end
+ end
+ end
+
private
def perform_calculation(operation, column_name, options = {})
@@ -317,7 +348,7 @@ module ActiveRecord
def select_for_count
if @select_values.present?
select = @select_values.join(", ")
- select if select !~ /(,|\*)/
+ select if select !~ /[,*]/
end
end
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
new file mode 100644
index 0000000000..f5fdf437bf
--- /dev/null
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -0,0 +1,49 @@
+require 'active_support/core_ext/module/delegation'
+
+module ActiveRecord
+ module Delegation
+ # Set up common delegations for performance (avoids method_missing)
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
+ delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
+ :connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
+
+ def self.delegate_to_scoped_klass(method)
+ if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{method}(*args, &block)
+ scoping { @klass.#{method}(*args, &block) }
+ end
+ RUBY
+ else
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{method}(*args, &block)
+ scoping { @klass.send(#{method.inspect}, *args, &block) }
+ end
+ RUBY
+ end
+ end
+
+ def respond_to?(method, include_private = false)
+ super || Array.method_defined?(method) ||
+ @klass.respond_to?(method, include_private) ||
+ arel.respond_to?(method, include_private)
+ end
+
+ protected
+
+ def method_missing(method, *args, &block)
+ if Array.method_defined?(method)
+ ::ActiveRecord::Delegation.delegate method, :to => :to_a
+ to_a.send(method, *args, &block)
+ elsif @klass.respond_to?(method)
+ ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
+ scoping { @klass.send(method, *args, &block) }
+ elsif arel.respond_to?(method)
+ ::ActiveRecord::Delegation.delegate method, :to => :arel
+ arel.send(method, *args, &block)
+ else
+ super
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 7eeb3dde70..f1ac421a50 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -134,7 +134,7 @@ module ActiveRecord
def last(*args)
if args.any?
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
- if order_values.empty? && reorder_value.nil?
+ if order_values.empty?
order("#{primary_key} DESC").limit(*args).reverse
else
to_a.last(*args)
@@ -187,11 +187,11 @@ module ActiveRecord
def exists?(id = false)
return false if id.nil?
- id = id.id if ActiveRecord::Base === id
+ id = id.id if ActiveRecord::Model === id
join_dependency = construct_join_dependency_for_association_find
relation = construct_relation_for_association_find(join_dependency)
- relation = relation.except(:select).select("1").limit(1)
+ relation = relation.except(:select, :order).select("1").limit(1)
case id
when Array, Hash
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 7e8ddd1b5d..1d04e763f6 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -15,42 +15,61 @@ module ActiveRecord
table = Arel::Table.new(table_name, engine)
end
- attribute = table[column.to_sym]
-
- case value
- when ActiveRecord::Relation
- value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
- attribute.in(value.arel.ast)
- when Array, ActiveRecord::Associations::CollectionProxy
- values = value.to_a.map { |x|
- x.is_a?(ActiveRecord::Base) ? x.id : x
- }
-
- if values.include?(nil)
- values = values.compact
- if values.empty?
- attribute.eq nil
- else
- attribute.in(values.compact).or attribute.eq(nil)
- end
+ build(table[column.to_sym], value)
+ end
+ end
+ predicates.flatten
+ end
+
+ def self.references(attributes)
+ references = attributes.map do |key, value|
+ if value.is_a?(Hash)
+ key
+ else
+ key = key.to_s
+ key.split('.').first.to_sym if key.include?('.')
+ end
+ end
+ references.compact
+ end
+
+ private
+ def self.build(attribute, value)
+ case value
+ when ActiveRecord::Relation
+ value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
+ attribute.in(value.arel.ast)
+ when Array, ActiveRecord::Associations::CollectionProxy
+ values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x}
+ ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
+
+ array_predicates = ranges.map {|range| attribute.in(range)}
+
+ if values.include?(nil)
+ values = values.compact
+ case values.length
+ when 0
+ array_predicates << attribute.eq(nil)
+ when 1
+ array_predicates << attribute.eq(values.first).or(attribute.eq(nil))
else
- attribute.in(values)
+ array_predicates << attribute.in(values).or(attribute.eq(nil))
end
-
- when Range, Arel::Relation
- attribute.in(value)
- when ActiveRecord::Base
- attribute.eq(value.id)
- when Class
- # FIXME: I think we need to deprecate this behavior
- attribute.eq(value.name)
else
- attribute.eq(value)
+ array_predicates << attribute.in(values)
end
+
+ array_predicates.inject {|composite, predicate| composite.or(predicate)}
+ when Range, Arel::Relation
+ attribute.in(value)
+ when ActiveRecord::Model
+ attribute.eq(value.id)
+ when Class
+ # FIXME: I think we need to deprecate this behavior
+ attribute.eq(value.name)
+ else
+ attribute.eq(value)
end
end
-
- predicates.flatten
- end
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 670ba0987d..87dd513880 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -9,7 +9,8 @@ module ActiveRecord
:select_values, :group_values, :order_values, :joins_values,
:where_values, :having_values, :bind_values,
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
- :from_value, :reorder_value, :reverse_order_value
+ :from_value, :reordering_value, :reverse_order_value,
+ :uniq_value, :references_values
def includes(*args)
args.reject! {|a| a.blank? }
@@ -37,8 +38,26 @@ module ActiveRecord
relation
end
+ # Used to indicate that an association is referenced by an SQL string, and should
+ # therefore be JOINed in any query rather than loaded separately.
+ #
+ # For example:
+ #
+ # User.includes(:posts).where("posts.name = 'foo'")
+ # # => Doesn't JOIN the posts table, resulting in an error.
+ #
+ # User.includes(:posts).where("posts.name = 'foo'").references(:posts)
+ # # => Query now knows the string references posts, so adds a JOIN
+ def references(*args)
+ return self if args.blank?
+
+ relation = clone
+ relation.references_values = (references_values + args.flatten.map(&:to_s)).uniq
+ relation
+ end
+
# Works in two unique ways.
- #
+ #
# First: takes a block so it can be used just like Array#select.
#
# Model.scoped.select { |m| m.field == value }
@@ -56,16 +75,16 @@ module ActiveRecord
# array, it actually returns a relation object and can have other query
# methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
#
- # This method will also take multiple parameters:
+ # The argument to the method can also be an array of fields.
#
- # >> Model.select(:field, :other_field, :and_one_more)
+ # >> Model.select([:field, :other_field, :and_one_more])
# => [#<Model field: "value", other_field: "value", and_one_more: "value">]
#
- # Any attributes that do not have fields retrieved by a select
- # will return `nil` when the getter method for that attribute is used:
+ # Accessing attributes of an object that do not have fields retrieved by a select
+ # will throw <tt>ActiveModel::MissingAttributeError</tt>:
#
# >> Model.select(:field).first.other_field
- # => nil
+ # => ActiveModel::MissingAttributeError: missing attribute: other_field
def select(value = Proc.new)
if block_given?
to_a.select {|*block_args| value.call(*block_args) }
@@ -87,16 +106,33 @@ module ActiveRecord
def order(*args)
return self if args.blank?
+ args = args.flatten
+ references = args.reject { |arg| Arel::Node === arg }
+ .map { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }
+ .compact
+
relation = clone
- relation.order_values += args.flatten
+ relation = relation.references(references) if references.any?
+ relation.order_values += args
relation
end
+ # Replaces any existing order defined on the relation with the specified order.
+ #
+ # User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
+ #
+ # Subsequent calls to order on the same relation will be appended. For example:
+ #
+ # User.order('email DESC').reorder('id ASC').order('name ASC')
+ #
+ # generates a query with 'ORDER BY id ASC, name ASC'.
+ #
def reorder(*args)
return self if args.blank?
relation = clone
- relation.reorder_value = args.flatten
+ relation.reordering_value = true
+ relation.order_values = args.flatten
relation
end
@@ -121,6 +157,7 @@ module ActiveRecord
return self if opts.blank?
relation = clone
+ relation = relation.references(PredicateBuilder.references(opts)) if Hash === opts
relation.where_values += build_where(opts, rest)
relation
end
@@ -129,6 +166,7 @@ module ActiveRecord
return self if opts.blank?
relation = clone
+ relation = relation.references(PredicateBuilder.references(opts)) if Hash === opts
relation.having_values += build_where(opts, rest)
relation
end
@@ -158,6 +196,39 @@ module ActiveRecord
relation
end
+ # Returns a chainable relation with zero records, specifically an
+ # instance of the NullRelation class.
+ #
+ # The returned NullRelation inherits from Relation and implements the
+ # Null Object pattern so it is an object with defined null behavior:
+ # it always returns an empty array of records and does not query the database.
+ #
+ # Any subsequent condition chained to the returned relation will continue
+ # generating an empty relation and will not fire any query to the database.
+ #
+ # Used in cases where a method or scope could return zero records but the
+ # result needs to be chainable.
+ #
+ # For example:
+ #
+ # @posts = current_user.visible_posts.where(:name => params[:name])
+ # # => the visible_posts method is expected to return a chainable Relation
+ #
+ # def visible_posts
+ # case role
+ # when 'Country Manager'
+ # Post.where(:country => country)
+ # when 'Reviewer'
+ # Post.published
+ # when 'Bad User'
+ # Post.none # => returning [] instead breaks the previous code
+ # end
+ # end
+ #
+ def none
+ NullRelation.new(@klass, @table)
+ end
+
def readonly(value = true)
relation = clone
relation.readonly_value = value
@@ -176,9 +247,25 @@ module ActiveRecord
relation
end
+ # Specifies whether the records should be unique or not. For example:
+ #
+ # User.select(:name)
+ # # => Might return two records with the same name
+ #
+ # User.select(:name).uniq
+ # # => Returns 1 record per unique name
+ #
+ # User.select(:name).uniq.uniq(false)
+ # # => You can also remove the uniqueness
+ def uniq(value = true)
+ relation = clone
+ relation.uniq_value = value
+ relation
+ end
+
# Used to extend a scope with additional methods, either through
- # a module or through a block provided.
- #
+ # a module or through a block provided.
+ #
# The object returned is a relation, which can be further extended.
#
# === Using a module
@@ -200,7 +287,7 @@ module ActiveRecord
#
# scope = Model.scoped.extending do
# def page(number)
- # # pagination code goes here
+ # # pagination code goes here
# end
# end
# scope.page(params[:page])
@@ -209,7 +296,7 @@ module ActiveRecord
#
# scope = Model.scoped.extending(Pagination) do
# def per_page(number)
- # # pagination code goes here
+ # # pagination code goes here
# end
# end
def extending(*modules)
@@ -246,12 +333,13 @@ module ActiveRecord
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
- order = @reorder_value ? @reorder_value : @order_values
+ order = @order_values
order = reverse_sql_order(order) if @reverse_order_value
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
build_select(arel, @select_values.uniq)
+ arel.distinct(@uniq_value)
arel.from(@from_value) if @from_value
arel.lock(@lock_value) if @lock_value
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index ba882beca9..7131aa29b6 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -22,7 +22,7 @@ module ActiveRecord
end
end
- (Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method|
+ (Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
value = r.send(:"#{method}_values")
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
end
@@ -48,7 +48,7 @@ module ActiveRecord
merged_relation.where_values = merged_wheres
- (Relation::SINGLE_VALUE_METHODS - [:lock, :create_with]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :create_with, :reordering]).each do |method|
value = r.send(:"#{method}_value")
merged_relation.send(:"#{method}_value=", value) unless value.nil?
end
@@ -57,6 +57,15 @@ module ActiveRecord
merged_relation = merged_relation.create_with(r.create_with_value) unless r.create_with_value.empty?
+ if (r.reordering_value)
+ # override any order specified in the original relation
+ merged_relation.reordering_value = true
+ merged_relation.order_values = r.order_values
+ else
+ # merge in order_values from r
+ merged_relation.order_values += r.order_values
+ end
+
# Apply scope extension modules
merged_relation.send :apply_modules, r.extensions
@@ -113,7 +122,7 @@ module ActiveRecord
result
end
- VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend,
+ VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend, :references,
:order, :select, :readonly, :group, :having, :from, :lock ]
def apply_finder_options(options)
@@ -124,7 +133,7 @@ module ActiveRecord
finders = options.dup
finders.delete_if { |key, value| value.nil? && key != :limit }
- ([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder|
+ ((VALID_FIND_OPTIONS - [:conditions, :include, :extend]) & finders.keys).each do |finder|
relation = relation.send(finder, finders[finder])
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 9ceab2eabc..fb4b89b87b 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -8,12 +8,13 @@ module ActiveRecord
class Result
include Enumerable
- attr_reader :columns, :rows
+ attr_reader :columns, :rows, :column_types
def initialize(columns, rows)
- @columns = columns
- @rows = rows
- @hash_rows = nil
+ @columns = columns
+ @rows = rows
+ @hash_rows = nil
+ @column_types = {}
end
def each
@@ -24,6 +25,31 @@ module ActiveRecord
hash_rows
end
+ alias :map! :map
+ alias :collect! :map
+
+ def empty?
+ rows.empty?
+ end
+
+ def to_ary
+ hash_rows
+ end
+
+ def [](idx)
+ hash_rows[idx]
+ end
+
+ def last
+ hash_rows.last
+ end
+
+ def initialize_copy(other)
+ @columns = columns.dup
+ @rows = rows.dup
+ @hash_rows = nil
+ end
+
private
def hash_rows
@hash_rows ||= @rows.map { |row|
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
new file mode 100644
index 0000000000..2d7d83d160
--- /dev/null
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -0,0 +1,194 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module Sanitization
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def quote_value(value, column = nil) #:nodoc:
+ connection.quote(value,column)
+ end
+
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
+ def sanitize(object) #:nodoc:
+ connection.quote(object)
+ end
+
+ protected
+
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
+ # them into a valid SQL fragment for a WHERE clause.
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
+ # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
+ # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
+ def sanitize_sql_for_conditions(condition, table_name = self.table_name)
+ return nil if condition.blank?
+
+ case condition
+ when Array; sanitize_sql_array(condition)
+ when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
+ else condition
+ end
+ end
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
+
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
+ # them into a valid SQL fragment for a SET clause.
+ # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
+ def sanitize_sql_for_assignment(assignments)
+ case assignments
+ when Array; sanitize_sql_array(assignments)
+ when Hash; sanitize_sql_hash_for_assignment(assignments)
+ else assignments
+ end
+ end
+
+ # Accepts a hash of SQL conditions and replaces those attributes
+ # that correspond to a +composed_of+ relationship with their expanded
+ # aggregate attribute values.
+ # Given:
+ # class Person < ActiveRecord::Base
+ # composed_of :address, :class_name => "Address",
+ # :mapping => [%w(address_street street), %w(address_city city)]
+ # end
+ # Then:
+ # { :address => Address.new("813 abc st.", "chicago") }
+ # # => { :address_street => "813 abc st.", :address_city => "chicago" }
+ def expand_hash_conditions_for_aggregates(attrs)
+ expanded_attrs = {}
+ attrs.each do |attr, value|
+ unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
+ mapping = aggregate_mapping(aggregation)
+ mapping.each do |field_attr, aggregate_attr|
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
+ expanded_attrs[field_attr] = value
+ else
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
+ end
+ end
+ else
+ expanded_attrs[attr] = value
+ end
+ end
+ expanded_attrs
+ end
+
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
+ # { :name => "foo'bar", :group_id => 4 }
+ # # => "name='foo''bar' and group_id= 4"
+ # { :status => nil, :group_id => [1,2,3] }
+ # # => "status IS NULL and group_id IN (1,2,3)"
+ # { :age => 13..18 }
+ # # => "age BETWEEN 13 AND 18"
+ # { 'other_records.id' => 7 }
+ # # => "`other_records`.`id` = 7"
+ # { :other_records => { :id => 7 } }
+ # # => "`other_records`.`id` = 7"
+ # And for value objects on a composed_of relationship:
+ # { :address => Address.new("123 abc st.", "chicago") }
+ # # => "address_street='123 abc st.' and address_city='chicago'"
+ def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
+ attrs = expand_hash_conditions_for_aggregates(attrs)
+
+ table = Arel::Table.new(table_name).alias(default_table_name)
+ PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
+ connection.visitor.accept b
+ }.join(' AND ')
+ end
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
+
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
+ # { :status => nil, :group_id => 1 }
+ # # => "status = NULL , group_id = 1"
+ def sanitize_sql_hash_for_assignment(attrs)
+ attrs.map do |attr, value|
+ "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
+ end.join(', ')
+ end
+
+ # Accepts an array of conditions. The array has each value
+ # sanitized and interpolated into the SQL statement.
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
+ def sanitize_sql_array(ary)
+ statement, *values = ary
+ if values.first.is_a?(Hash) && statement =~ /:\w+/
+ replace_named_bind_variables(statement, values.first)
+ elsif statement.include?('?')
+ replace_bind_variables(statement, values)
+ elsif statement.blank?
+ statement
+ else
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
+ end
+ end
+
+ alias_method :sanitize_conditions, :sanitize_sql
+
+ def replace_bind_variables(statement, values) #:nodoc:
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
+ bound = values.dup
+ c = connection
+ statement.gsub('?') { quote_bound_value(bound.shift, c) }
+ end
+
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
+ if $1 == ':' # skip postgresql casts
+ $& # return the whole match
+ elsif bind_vars.include?(match = $2.to_sym)
+ quote_bound_value(bind_vars[match])
+ else
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
+ end
+ end
+ end
+
+ def expand_range_bind_variables(bind_vars) #:nodoc:
+ expanded = []
+
+ bind_vars.each do |var|
+ next if var.is_a?(Hash)
+
+ if var.is_a?(Range)
+ expanded << var.first
+ expanded << var.last
+ else
+ expanded << var
+ end
+ end
+
+ expanded
+ end
+
+ def quote_bound_value(value, c = connection) #:nodoc:
+ if value.respond_to?(:map) && !value.acts_like?(:string)
+ if value.respond_to?(:empty?) && value.empty?
+ c.quote(nil)
+ else
+ value.map { |v| c.quote(v) }.join(',')
+ end
+ else
+ c.quote(value)
+ end
+ end
+
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
+ unless expected == provided
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
+ end
+ end
+ end
+
+ # TODO: Deprecate this
+ def quoted_id #:nodoc:
+ quote_value(id, column_for_attribute(self.class.primary_key))
+ end
+
+ private
+
+ # Quote strings appropriately for SQL statements.
+ def quote_value(value, column = nil)
+ self.class.connection.quote(value, column)
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 6fe305f843..dcbd165e58 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -112,7 +112,7 @@ HEADER
# AR has an optimization which handles zero-scale decimals as integers. This
# code ensures that the dumper still dumps the column as a decimal.
- spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
+ spec[:type] = if column.type == :integer && /^(numeric|decimal)/ =~ column.sql_type
'decimal'
else
column.type.to_s
@@ -127,10 +127,14 @@ HEADER
end.compact
# find all migration keys used in this table
- keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
+ keys = [:name, :limit, :precision, :scale, :default, :null]
# figure out the lengths for each column based on above keys
- lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
+ lengths = keys.map { |key|
+ column_specs.map { |spec|
+ spec[key] ? spec[key].length + 2 : 0
+ }.max
+ }
# the string we're going to sprintf our values against, with standardized column widths
format_string = lengths.map{ |len| "%-#{len}s" }
@@ -190,6 +194,11 @@ HEADER
index_lengths = (index.lengths || []).compact
statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
+ index_orders = (index.orders || {})
+ statement_parts << (':order => ' + index.orders.inspect) unless index_orders.empty?
+
+ statement_parts << (':where => ' + index.where.inspect) if index.where
+
' ' + statement_parts.join(', ')
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
new file mode 100644
index 0000000000..236ec563d2
--- /dev/null
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -0,0 +1,34 @@
+require 'active_record/scoping/default'
+require 'active_record/scoping/named'
+require 'active_record/base'
+
+module ActiveRecord
+ class SchemaMigration < ActiveRecord::Base
+ attr_accessible :version
+
+ def self.table_name
+ Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
+ end
+
+ def self.create_table
+ unless connection.table_exists?(table_name)
+ connection.create_table(table_name, :id => false) do |t|
+ t.column :version, :string, :null => false
+ end
+ connection.add_index table_name, :version, :unique => true,
+ :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
+ end
+ end
+
+ def self.drop_table
+ if connection.table_exists?(table_name)
+ connection.remove_index table_name, :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
+ connection.drop_table(table_name)
+ end
+ end
+
+ def version
+ super.to_i
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
new file mode 100644
index 0000000000..a8f5e96190
--- /dev/null
+++ b/activerecord/lib/active_record/scoping.rb
@@ -0,0 +1,152 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module Scoping
+ extend ActiveSupport::Concern
+
+ included do
+ include Default
+ include Named
+ end
+
+ module ClassMethods
+ # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
+ # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
+ # <tt>:create</tt> parameters are an attributes hash.
+ #
+ # class Article < ActiveRecord::Base
+ # def self.create_with_scope
+ # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
+ # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
+ # a = create(1)
+ # a.blog_id # => 1
+ # end
+ # end
+ # end
+ #
+ # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
+ # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
+ #
+ # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
+ # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
+ # array of strings format for your joins.
+ #
+ # class Article < ActiveRecord::Base
+ # def self.find_with_scope
+ # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
+ # with_scope(:find => limit(10)) do
+ # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
+ # end
+ # with_scope(:find => where(:author_id => 3)) do
+ # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
+ # end
+ # end
+ # end
+ # end
+ #
+ # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
+ #
+ # class Article < ActiveRecord::Base
+ # def self.find_with_exclusive_scope
+ # with_scope(:find => where(:blog_id => 1).limit(1)) do
+ # with_exclusive_scope(:find => limit(10)) do
+ # all # => SELECT * from articles LIMIT 10
+ # end
+ # end
+ # end
+ # end
+ #
+ # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
+ def with_scope(scope = {}, action = :merge, &block)
+ # If another Active Record class has been passed in, get its current scope
+ scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
+
+ previous_scope = self.current_scope
+
+ if scope.is_a?(Hash)
+ # Dup first and second level of hash (method and params).
+ scope = scope.dup
+ scope.each do |method, params|
+ scope[method] = params.dup unless params == true
+ end
+
+ scope.assert_valid_keys([ :find, :create ])
+ relation = construct_finder_arel(scope[:find] || {})
+ relation.default_scoped = true unless action == :overwrite
+
+ if previous_scope && previous_scope.create_with_value && scope[:create]
+ scope_for_create = if action == :merge
+ previous_scope.create_with_value.merge(scope[:create])
+ else
+ scope[:create]
+ end
+
+ relation = relation.create_with(scope_for_create)
+ else
+ scope_for_create = scope[:create]
+ scope_for_create ||= previous_scope.create_with_value if previous_scope
+ relation = relation.create_with(scope_for_create) if scope_for_create
+ end
+
+ scope = relation
+ end
+
+ scope = previous_scope.merge(scope) if previous_scope && action == :merge
+
+ self.current_scope = scope
+ begin
+ yield
+ ensure
+ self.current_scope = previous_scope
+ end
+ end
+
+ protected
+
+ # Works like with_scope, but discards any nested properties.
+ def with_exclusive_scope(method_scoping = {}, &block)
+ if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
+ raise ArgumentError, <<-MSG
+ New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
+
+ User.unscoped.where(:active => true)
+
+ Or call unscoped with a block:
+
+ User.unscoped do
+ User.where(:active => true).all
+ end
+
+ MSG
+ end
+ with_scope(method_scoping, :overwrite, &block)
+ end
+
+ def current_scope #:nodoc:
+ Thread.current["#{self}_current_scope"]
+ end
+
+ def current_scope=(scope) #:nodoc:
+ Thread.current["#{self}_current_scope"] = scope
+ end
+
+ private
+
+ def construct_finder_arel(options = {}, scope = nil)
+ relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
+ relation = scope.merge(relation) if scope
+ relation
+ end
+
+ end
+
+ def populate_with_current_scope_attributes
+ return unless self.class.scope_attributes?
+
+ self.class.scope_attributes.each do |att,value|
+ send("#{att}=", value) if respond_to?("#{att}=")
+ end
+ end
+
+ end
+end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
new file mode 100644
index 0000000000..5f05d146f2
--- /dev/null
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -0,0 +1,142 @@
+require 'active_support/concern'
+
+module ActiveRecord
+ module Scoping
+ module Default
+ extend ActiveSupport::Concern
+
+ included do
+ # Stores the default scope for the class
+ config_attribute :default_scopes
+ self.default_scopes = []
+ end
+
+ module ClassMethods
+ # Returns a scope for the model without the default_scope.
+ #
+ # class Post < ActiveRecord::Base
+ # def self.default_scope
+ # where :published => true
+ # end
+ # end
+ #
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
+ #
+ # This method also accepts a block. All queries inside the block will
+ # not use the default_scope:
+ #
+ # Post.unscoped {
+ # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
+ # }
+ #
+ # It is recommended to use the block form of unscoped because chaining
+ # unscoped with <tt>scope</tt> does not work. Assuming that
+ # <tt>published</tt> is a <tt>scope</tt>, the following two statements
+ # are equal: the default_scope is applied on both.
+ #
+ # Post.unscoped.published
+ # Post.published
+ def unscoped #:nodoc:
+ block_given? ? relation.scoping { yield } : relation
+ end
+
+ def before_remove_const #:nodoc:
+ self.current_scope = nil
+ end
+
+ protected
+
+ # Use this macro in your model to set a default scope for all operations on
+ # the model.
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope where(:published => true)
+ # end
+ #
+ # Article.all # => SELECT * FROM articles WHERE published = true
+ #
+ # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
+ # applied while updating a record.
+ #
+ # Article.new.published # => true
+ # Article.create.published # => true
+ #
+ # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope { where(:published_at => Time.now - 1.week) }
+ # end
+ #
+ # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
+ # macro, and it will be called when building the default scope.)
+ #
+ # If you use multiple <tt>default_scope</tt> declarations in your model then they will
+ # be merged together:
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope where(:published => true)
+ # default_scope where(:rating => 'G')
+ # end
+ #
+ # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
+ #
+ # This is also the case with inheritance and module includes where the parent or module
+ # defines a <tt>default_scope</tt> and the child or including class defines a second one.
+ #
+ # If you need to do more complex things with a default scope, you can alternatively
+ # define it as a class method:
+ #
+ # class Article < ActiveRecord::Base
+ # def self.default_scope
+ # # Should return a scope, you can call 'super' here etc.
+ # end
+ # end
+ def default_scope(scope = {})
+ scope = Proc.new if block_given?
+ self.default_scopes = default_scopes + [scope]
+ end
+
+ def build_default_scope #:nodoc:
+ if method(:default_scope).owner != ActiveRecord::Scoping::Default::ClassMethods
+ evaluate_default_scope { default_scope }
+ elsif default_scopes.any?
+ evaluate_default_scope do
+ default_scopes.inject(relation) do |default_scope, scope|
+ if scope.is_a?(Hash)
+ default_scope.apply_finder_options(scope)
+ elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
+ default_scope.merge(scope.call)
+ else
+ default_scope.merge(scope)
+ end
+ end
+ end
+ end
+ end
+
+ def ignore_default_scope? #:nodoc:
+ Thread.current["#{self}_ignore_default_scope"]
+ end
+
+ def ignore_default_scope=(ignore) #:nodoc:
+ Thread.current["#{self}_ignore_default_scope"] = ignore
+ end
+
+ # The ignore_default_scope flag is used to prevent an infinite recursion situation where
+ # a default scope references a scope which has a default scope which references a scope...
+ def evaluate_default_scope
+ return if ignore_default_scope?
+
+ begin
+ self.ignore_default_scope = true
+ yield
+ ensure
+ self.ignore_default_scope = false
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
new file mode 100644
index 0000000000..0edc3f1dcc
--- /dev/null
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -0,0 +1,202 @@
+require 'active_support/core_ext/array'
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/class/attribute'
+
+module ActiveRecord
+ # = Active Record Named \Scopes
+ module Scoping
+ module Named
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Returns an anonymous \scope.
+ #
+ # posts = Post.scoped
+ # posts.size # Fires "select count(*) from posts" and returns the count
+ # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
+ #
+ # fruits = Fruit.scoped
+ # fruits = fruits.where(:color => 'red') if options[:red_only]
+ # fruits = fruits.limit(10) if limited?
+ #
+ # Anonymous \scopes tend to be useful when procedurally generating complex
+ # queries, where passing 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 scoped(options = nil)
+ if options
+ scoped.apply_finder_options(options)
+ else
+ if current_scope
+ current_scope.clone
+ else
+ scope = relation.clone
+ scope.default_scoped = true
+ scope
+ end
+ end
+ end
+
+ ##
+ # Collects attributes from scopes that should be applied when creating
+ # an AR instance for the particular class this is called on.
+ def scope_attributes # :nodoc:
+ if current_scope
+ current_scope.scope_for_create
+ else
+ scope = relation.clone
+ scope.default_scoped = true
+ scope.scope_for_create
+ end
+ end
+
+ ##
+ # Are there default attributes associated with this scope?
+ def scope_attributes? # :nodoc:
+ current_scope || default_scopes.any?
+ end
+
+ # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
+ # such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
+ #
+ # class Shirt < ActiveRecord::Base
+ # scope :red, where(:color => 'red')
+ # scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
+ # end
+ #
+ # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
+ # in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
+ #
+ # Note that this is simply 'syntactic sugar' for defining an actual class method:
+ #
+ # class Shirt < ActiveRecord::Base
+ # def self.red
+ # where(:color => 'red')
+ # end
+ # end
+ #
+ # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
+ # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
+ # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
+ # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable;
+ # <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
+ # all behave as if Shirt.red really was an Array.
+ #
+ # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce
+ # all shirts that are both red and dry clean only.
+ # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
+ # returns the number of garments for which these criteria obtain. Similarly with
+ # <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
+ #
+ # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which
+ # the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If,
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :shirts
+ # end
+ #
+ # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
+ # only shirts.
+ #
+ # Named \scopes can also be procedural:
+ #
+ # class Shirt < ActiveRecord::Base
+ # scope :colored, lambda { |color| where(:color => color) }
+ # end
+ #
+ # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
+ #
+ # On Ruby 1.9 you can use the 'stabby lambda' syntax:
+ #
+ # scope :colored, ->(color) { where(:color => color) }
+ #
+ # Note that scopes defined with \scope will be evaluated when they are defined, rather than
+ # when they are used. For example, the following would be incorrect:
+ #
+ # class Post < ActiveRecord::Base
+ # scope :recent, where('published_at >= ?', Time.current - 1.week)
+ # end
+ #
+ # The example above would be 'frozen' to the <tt>Time.current</tt> value when the <tt>Post</tt>
+ # class was defined, and so the resultant SQL query would always be the same. The correct
+ # way to do this would be via a lambda, which will re-evaluate the scope each time
+ # it is called:
+ #
+ # class Post < ActiveRecord::Base
+ # scope :recent, lambda { where('published_at >= ?', Time.current - 1.week) }
+ # end
+ #
+ # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
+ #
+ # class Shirt < ActiveRecord::Base
+ # scope :red, where(:color => 'red') do
+ # def dom_id
+ # 'red_shirts'
+ # end
+ # end
+ # end
+ #
+ # Scopes can also be used while creating/building a record.
+ #
+ # class Article < ActiveRecord::Base
+ # scope :published, where(:published => true)
+ # end
+ #
+ # Article.published.new.published # => true
+ # Article.published.create.published # => true
+ #
+ # Class methods on your model are automatically available
+ # on scopes. Assuming the following setup:
+ #
+ # class Article < ActiveRecord::Base
+ # scope :published, where(:published => true)
+ # scope :featured, where(:featured => true)
+ #
+ # def self.latest_article
+ # order('published_at desc').first
+ # end
+ #
+ # def self.titles
+ # map(&:title)
+ # end
+ #
+ # end
+ #
+ # We are able to call the methods like this:
+ #
+ # Article.published.featured.latest_article
+ # Article.featured.titles
+
+ def scope(name, scope_options = {})
+ name = name.to_sym
+ valid_scope_name?(name)
+ extension = Module.new(&Proc.new) if block_given?
+
+ scope_proc = lambda do |*args|
+ options = scope_options.respond_to?(:call) ? unscoped { scope_options.call(*args) } : scope_options
+ options = scoped.apply_finder_options(options) if options.is_a?(Hash)
+
+ relation = scoped.merge(options)
+
+ extension ? relation.extending(extension) : relation
+ end
+
+ singleton_class.send(:redefine_method, name, &scope_proc)
+ end
+
+ protected
+
+ def valid_scope_name?(name)
+ if respond_to?(name, true)
+ logger.warn "Creating scope :#{name}. " \
+ "Overwriting existing method #{self.name}.#{name}."
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 5ad40d8cd9..41e3b92499 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -7,8 +7,8 @@ module ActiveRecord #:nodoc:
def serializable_hash(options = nil)
options = options.try(:clone) || {}
- options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
- options[:except] |= Array.wrap(self.class.inheritance_column)
+ options[:except] = Array(options[:except]).map { |n| n.to_s }
+ options[:except] |= Array(self.class.inheritance_column)
super(options)
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 0e7f57aa43..2e60521638 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/conversions'
module ActiveRecord #:nodoc:
@@ -163,8 +162,9 @@ module ActiveRecord #:nodoc:
#
# class IHaveMyOwnXML < ActiveRecord::Base
# def to_xml(options = {})
+ # require 'builder'
# options[:indent] ||= 2
- # xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ # xml = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
# xml.instruct! unless options[:skip_instruct]
# xml.level_one do
# xml.tag!(:second_level, 'content')
@@ -179,7 +179,7 @@ module ActiveRecord #:nodoc:
class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
def initialize(*args)
super
- options[:except] = Array.wrap(options[:except]) | Array.wrap(@serializable.class.inheritance_column)
+ options[:except] = Array(options[:except]) | Array(@serializable.class.inheritance_column)
end
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index 76c37cc367..ce43ae8066 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -51,20 +51,20 @@ module ActiveRecord
class SessionStore < ActionDispatch::Session::AbstractStore
module ClassMethods # :nodoc:
def marshal(data)
- ActiveSupport::Base64.encode64(Marshal.dump(data)) if data
+ ::Base64.encode64(Marshal.dump(data)) if data
end
def unmarshal(data)
- Marshal.load(ActiveSupport::Base64.decode64(data)) if data
+ Marshal.load(::Base64.decode64(data)) if data
end
def drop_table!
- connection_pool.clear_table_cache!(table_name)
+ connection.schema_cache.clear_table_cache!(table_name)
connection.drop_table table_name
end
def create_table!
- connection_pool.clear_table_cache!(table_name)
+ connection.schema_cache.clear_table_cache!(table_name)
connection.create_table(table_name) do |t|
t.string session_id_column, :limit => 255
t.text data_column_name
@@ -116,7 +116,7 @@ module ActiveRecord
define_method(:session_id) { sessid }
define_method(:session_id=) { |session_id| self.sessid = session_id }
else
- class << self; remove_method :find_by_session_id; end
+ class << self; remove_possible_method :find_by_session_id; end
def self.find_by_session_id(session_id)
find :first, :conditions => {:session_id=>session_id}
@@ -169,11 +169,11 @@ module ActiveRecord
# are implemented as class methods that you may override. By default,
# marshaling data is
#
- # ActiveSupport::Base64.encode64(Marshal.dump(data))
+ # ::Base64.encode64(Marshal.dump(data))
#
# and unmarshaling data is
#
- # Marshal.load(ActiveSupport::Base64.decode64(data))
+ # Marshal.load(::Base64.decode64(data))
#
# This marshaling behavior is intended to store the widest range of
# binary session data in a +text+ column. For higher performance,
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 8cc84f81d0..1c7b839e5e 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -15,7 +15,7 @@ module ActiveRecord
# class User < ActiveRecord::Base
# store :settings, accessors: [ :color, :homepage ]
# end
- #
+ #
# u = User.new(color: 'black', homepage: '37signals.com')
# u.color # Accessor stored attribute
# u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
@@ -26,7 +26,7 @@ module ActiveRecord
# end
module Store
extend ActiveSupport::Concern
-
+
module ClassMethods
def store(store_attribute, options = {})
serialize store_attribute, Hash
@@ -34,17 +34,19 @@ module ActiveRecord
end
def store_accessor(store_attribute, *keys)
- Array(keys).flatten.each do |key|
+ keys.flatten.each do |key|
define_method("#{key}=") do |value|
+ send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
send(store_attribute)[key] = value
send("#{store_attribute}_will_change!")
end
-
+
define_method(key) do
+ send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
send(store_attribute)[key]
end
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index ffe9b08dce..4d881f0f7d 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -1,3 +1,7 @@
+require 'active_support/deprecation'
+require 'active_support/test_case'
+
+ActiveSupport::Deprecation.warn('ActiveRecord::TestCase is deprecated, please use ActiveSupport::TestCase')
module ActiveRecord
# = Active Record Test Case
#
@@ -9,15 +13,12 @@ module ActiveRecord
cleanup_identity_map
end
- def cleanup_identity_map
- ActiveRecord::IdentityMap.clear
+ def teardown
+ SQLCounter.log.clear
end
- # Backport skip to Ruby 1.8. test/unit doesn't support it, so just
- # make it a noop.
- unless instance_methods.map(&:to_s).include?("skip")
- def skip(message)
- end
+ def cleanup_identity_map
+ ActiveRecord::IdentityMap.clear
end
def assert_date_from_db(expected, actual, message = nil)
@@ -31,43 +32,63 @@ module ActiveRecord
end
def assert_sql(*patterns_to_match)
- ActiveRecord::SQLCounter.log = []
+ SQLCounter.log = []
yield
- ActiveRecord::SQLCounter.log
+ SQLCounter.log
ensure
failed_patterns = []
patterns_to_match.each do |pattern|
- failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
+ failed_patterns << pattern unless SQLCounter.log.any?{ |sql| pattern === sql }
end
- assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
end
def assert_queries(num = 1)
- ActiveRecord::SQLCounter.log = []
+ SQLCounter.log = []
yield
ensure
- assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
+ assert_equal num, SQLCounter.log.size, "#{SQLCounter.log.size} instead of #{num} queries were executed.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
end
def assert_no_queries(&block)
- prev_ignored_sql = ActiveRecord::SQLCounter.ignored_sql
- ActiveRecord::SQLCounter.ignored_sql = []
+ prev_ignored_sql = SQLCounter.ignored_sql
+ SQLCounter.ignored_sql = []
assert_queries(0, &block)
ensure
- ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql
+ SQLCounter.ignored_sql = prev_ignored_sql
end
- def with_kcode(kcode)
- if RUBY_VERSION < '1.9'
- orig_kcode, $KCODE = $KCODE, kcode
- begin
- yield
- ensure
- $KCODE = orig_kcode
- end
- else
- yield
- end
+ end
+
+ class SQLCounter
+ class << self
+ attr_accessor :ignored_sql, :log
+ end
+
+ self.log = []
+
+ self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
+
+ # FIXME: this needs to be refactored so specific database can add their own
+ # ignored SQL. This ignored SQL is for Oracle.
+ ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
+
+
+ attr_reader :ignore
+
+ def initialize(ignore = Regexp.union(self.class.ignored_sql))
+ @ignore = ignore
+ end
+
+ def call(name, start, finish, message_id, values)
+ sql = values[:sql]
+
+ # FIXME: this seems bad. we should probably have a better way to indicate
+ # the query was cached
+ return if 'CACHE' == values[:name] || ignore =~ sql
+ self.class.log << sql
end
end
+
+ ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 0c760e9850..c717fdea47 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -33,7 +33,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :record_timestamps
+ config_attribute :record_timestamps, :instance_writer => true
self.record_timestamps = true
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index ae97a3f3ca..b492377d18 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -211,7 +211,7 @@ module ActiveRecord
def after_commit(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
- options[:if] = Array.wrap(options[:if])
+ options[:if] = Array(options[:if])
options[:if] << "transaction_include_action?(:#{options[:on]})"
end
set_callback(:commit, :after, *args, &block)
@@ -220,7 +220,7 @@ module ActiveRecord
def after_rollback(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
- options[:if] = Array.wrap(options[:if])
+ options[:if] = Array(options[:if])
options[:if] << "transaction_include_action?(:#{options[:on]})"
end
set_callback(:rollback, :after, *args, &block)
@@ -301,7 +301,7 @@ module ActiveRecord
protected
# Save the new record state and id of a record so it can be restored later if a transaction fails.
- def remember_transaction_record_state #:nodoc
+ def remember_transaction_record_state #:nodoc:
@_start_transaction_state ||= {}
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
unless @_start_transaction_state.include?(:new_record)
@@ -314,7 +314,7 @@ module ActiveRecord
end
# Clear the new record state and id of a record.
- def clear_transaction_record_state #:nodoc
+ def clear_transaction_record_state #:nodoc:
if defined?(@_start_transaction_state)
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
@@ -322,7 +322,7 @@ module ActiveRecord
end
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
- def restore_transaction_record_state(force = false) #:nodoc
+ def restore_transaction_record_state(force = false) #:nodoc:
if defined?(@_start_transaction_state)
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
if @_start_transaction_state[:level] < 1
@@ -341,12 +341,12 @@ module ActiveRecord
end
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
- def transaction_record_state(state) #:nodoc
+ def transaction_record_state(state) #:nodoc:
@_start_transaction_state[state] if defined?(@_start_transaction_state)
end
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
- def transaction_include_action?(action) #:nodoc
+ def transaction_include_action?(action) #:nodoc:
case action
when :create
transaction_record_state(:new_record)
diff --git a/activerecord/lib/active_record/translation.rb b/activerecord/lib/active_record/translation.rb
new file mode 100644
index 0000000000..ddcb5f2a7a
--- /dev/null
+++ b/activerecord/lib/active_record/translation.rb
@@ -0,0 +1,22 @@
+module ActiveRecord
+ module Translation
+ include ActiveModel::Translation
+
+ # Set the lookup ancestors for ActiveModel.
+ def lookup_ancestors #:nodoc:
+ klass = self
+ classes = [klass]
+ return classes if klass == ActiveRecord::Base
+
+ while klass != klass.base_class
+ classes << klass = klass.superclass
+ end
+ classes
+ end
+
+ # Set the i18n scope to overwrite ActiveModel.
+ def i18n_scope #:nodoc:
+ :activerecord
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index 7af0352a31..afce149da9 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -2,8 +2,9 @@ module ActiveRecord
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
- record.errors.add(attribute, :invalid, options.merge(:value => value))
+ if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?(record.validation_context) }.any?
+ record.errors.add(attribute, :invalid, options.merge(:value => value))
+ end
end
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 2e2ea8c42b..9556878f63 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/array/prepend_and_append'
module ActiveRecord
module Validations
@@ -25,8 +25,13 @@ module ActiveRecord
relation = build_relation(finder_class, table, attribute, value)
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
- Array.wrap(options[:scope]).each do |scope_item|
+ Array(options[:scope]).each do |scope_item|
scope_value = record.send(scope_item)
+ reflection = record.class.reflect_on_association(scope_item)
+ if reflection
+ scope_value = record.send(reflection.foreign_key)
+ scope_item = reflection.foreign_key
+ end
relation = relation.and(table[scope_item].eq(scope_value))
end
@@ -46,21 +51,28 @@ module ActiveRecord
class_hierarchy = [record.class]
while class_hierarchy.first != @klass
- class_hierarchy.insert(0, class_hierarchy.first.superclass)
+ class_hierarchy.prepend(class_hierarchy.first.superclass)
end
class_hierarchy.detect { |klass| !klass.abstract_class? }
end
def build_relation(klass, table, attribute, value) #:nodoc:
- column = klass.columns_hash[attribute.to_s]
- value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
+ reflection = klass.reflect_on_association(attribute)
+ if reflection
+ column = klass.columns_hash[reflection.foreign_key]
+ attribute = reflection.foreign_key
+ value = value.attributes[reflection.primary_key_column.name]
+ else
+ column = klass.columns_hash[attribute.to_s]
+ end
+ value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text?
if !options[:case_sensitive] && value && column.text?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
relation = klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
- value = klass.connection.case_sensitive_modifier(value)
+ value = klass.connection.case_sensitive_modifier(value) unless value.nil?
relation = table[attribute].eq(value)
end
@@ -81,7 +93,7 @@ module ActiveRecord
#
# class Person < ActiveRecord::Base
# validates_uniqueness_of :user_name, :scope => :account_id
- # end
+ # end
#
# Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once
# per semester for a particular class.
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index 838aa8fb1e..0c35adc11d 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index f6159deeeb..1509e34473 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -3,7 +3,7 @@ require 'rails/generators/active_record'
module ActiveRecord
module Generators
class MigrationGenerator < Base
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
def create_migration_file
set_local_assigns!
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index ce8d7eed42..d084a00ed7 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -2,14 +2,20 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<%- if migration_action == 'add' -%>
def change
<% attributes.each do |attribute| -%>
- add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %>
+ add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
+ <%- if attribute.has_index? -%>
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
+ <%- end %>
<%- end -%>
end
<%- else -%>
def up
<% attributes.each do |attribute| -%>
<%- if migration_action -%>
- <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %>
+ <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><%= attribute.inject_options %><% end %>
+ <% if attribute.has_index? && migration_action == 'add' %>
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
+ <% end -%>
<%- end -%>
<%- end -%>
end
@@ -17,7 +23,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
def down
<% attributes.reverse.each do |attribute| -%>
<%- if migration_action -%>
- <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %>
+ <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><%= attribute.inject_options %><% end %>
<%- end -%>
<%- end -%>
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index f7caa43ac8..99a022461e 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -3,7 +3,7 @@ require 'rails/generators/active_record'
module ActiveRecord
module Generators
class ModelGenerator < Base
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
check_class_collision
@@ -26,6 +26,10 @@ module ActiveRecord
template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") if behavior == :invoke
end
+ def attributes_with_index
+ attributes.select { |a| a.has_index? || (a.reference? && options[:indexes]) }
+ end
+
hook_for :test_framework
protected
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
index 851930344a..3a3cf86d73 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
@@ -2,16 +2,14 @@ class <%= migration_class_name %> < ActiveRecord::Migration
def change
create_table :<%= table_name %> do |t|
<% attributes.each do |attribute| -%>
- t.<%= attribute.type %> :<%= attribute.name %>
+ t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
<% end -%>
<% if options[:timestamps] %>
t.timestamps
<% end -%>
end
-<% if options[:indexes] -%>
-<% attributes.select {|attr| attr.reference? }.each do |attribute| -%>
- add_index :<%= table_name %>, :<%= attribute.name %>_id
-<% end -%>
+<% attributes_with_index.each do |attribute| -%>
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<% end -%>
end
end
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index 1c2942170e..69dfd2503e 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -9,11 +9,16 @@ module ActiveRecord
class FakeAdapter < AbstractAdapter
attr_accessor :tables, :primary_keys
+ @columns = Hash.new { |h,k| h[k] = [] }
+ class << self
+ attr_reader :columns
+ end
+
def initialize(connection, logger)
super
@tables = []
@primary_keys = {}
- @columns = Hash.new { |h,k| h[k] = [] }
+ @columns = self.class.columns
end
def primary_key(table)
@@ -28,7 +33,7 @@ module ActiveRecord
options[:null])
end
- def columns(table_name, message)
+ def columns(table_name)
@columns[table_name]
end
end
diff --git a/activerecord/test/assets/test.txt b/activerecord/test/assets/test.txt
new file mode 100644
index 0000000000..6754f0612e
--- /dev/null
+++ b/activerecord/test/assets/test.txt
@@ -0,0 +1 @@
+%00
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 94497e37c7..852fc0e26e 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -1,170 +1,163 @@
require "cases/helper"
-class AdapterTest < ActiveRecord::TestCase
- def setup
- @connection = ActiveRecord::Base.connection
- end
-
- def test_tables
- tables = @connection.tables
- assert tables.include?("accounts")
- assert tables.include?("authors")
- assert tables.include?("tasks")
- assert tables.include?("topics")
- end
+module ActiveRecord
+ class AdapterTest < ActiveRecord::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
- def test_table_exists?
- assert @connection.table_exists?("accounts")
- assert !@connection.table_exists?("nonexistingtable")
- end
+ def test_tables
+ tables = @connection.tables
+ assert tables.include?("accounts")
+ assert tables.include?("authors")
+ assert tables.include?("tasks")
+ assert tables.include?("topics")
+ end
- def test_indexes
- idx_name = "accounts_idx"
-
- if @connection.respond_to?(:indexes)
- indexes = @connection.indexes("accounts")
- assert indexes.empty?
-
- @connection.add_index :accounts, :firm_id, :name => idx_name
- indexes = @connection.indexes("accounts")
- assert_equal "accounts", indexes.first.table
- # OpenBase does not have the concept of a named index
- # Indexes are merely properties of columns.
- assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
- assert !indexes.first.unique
- assert_equal ["firm_id"], indexes.first.columns
- else
- warn "#{@connection.class} does not respond to #indexes"
+ def test_table_exists?
+ assert @connection.table_exists?("accounts")
+ assert !@connection.table_exists?("nonexistingtable")
+ assert !@connection.table_exists?(nil)
end
- ensure
- @connection.remove_index(:accounts, :name => idx_name) rescue nil
- end
+ def test_indexes
+ idx_name = "accounts_idx"
+
+ if @connection.respond_to?(:indexes)
+ indexes = @connection.indexes("accounts")
+ assert indexes.empty?
+
+ @connection.add_index :accounts, :firm_id, :name => idx_name
+ indexes = @connection.indexes("accounts")
+ assert_equal "accounts", indexes.first.table
+ # OpenBase does not have the concept of a named index
+ # Indexes are merely properties of columns.
+ assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
+ assert !indexes.first.unique
+ assert_equal ["firm_id"], indexes.first.columns
+ else
+ warn "#{@connection.class} does not respond to #indexes"
+ end
- def test_current_database
- if @connection.respond_to?(:current_database)
- assert_equal ARTest.connection_config['arunit']['database'], @connection.current_database
+ ensure
+ @connection.remove_index(:accounts, :name => idx_name) rescue nil
end
- end
- if current_adapter?(:MysqlAdapter)
- def test_charset
- assert_not_nil @connection.charset
- assert_not_equal 'character_set_database', @connection.charset
- assert_equal @connection.show_variable('character_set_database'), @connection.charset
+ def test_current_database
+ if @connection.respond_to?(:current_database)
+ assert_equal ARTest.connection_config['arunit']['database'], @connection.current_database
+ end
end
- def test_collation
- assert_not_nil @connection.collation
- assert_not_equal 'collation_database', @connection.collation
- assert_equal @connection.show_variable('collation_database'), @connection.collation
- end
+ if current_adapter?(:MysqlAdapter)
+ def test_charset
+ assert_not_nil @connection.charset
+ assert_not_equal 'character_set_database', @connection.charset
+ assert_equal @connection.show_variable('character_set_database'), @connection.charset
+ end
- def test_show_nonexistent_variable_returns_nil
- assert_nil @connection.show_variable('foo_bar_baz')
- end
+ def test_collation
+ assert_not_nil @connection.collation
+ assert_not_equal 'collation_database', @connection.collation
+ assert_equal @connection.show_variable('collation_database'), @connection.collation
+ end
- def test_not_specifying_database_name_for_cross_database_selects
- begin
- assert_nothing_raised do
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
+ def test_show_nonexistent_variable_returns_nil
+ assert_nil @connection.show_variable('foo_bar_baz')
+ end
- config = ARTest.connection_config
- ActiveRecord::Base.connection.execute(
- "SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \
- "FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses"
- )
+ def test_not_specifying_database_name_for_cross_database_selects
+ begin
+ assert_nothing_raised do
+ ActiveRecord::Model.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
+
+ config = ARTest.connection_config
+ ActiveRecord::Model.connection.execute(
+ "SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \
+ "FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses"
+ )
+ end
+ ensure
+ ActiveRecord::Model.establish_connection 'arunit'
end
- ensure
- ActiveRecord::Base.establish_connection 'arunit'
end
end
- end
- def test_table_alias
- def @connection.test_table_alias_length() 10; end
- class << @connection
- alias_method :old_table_alias_length, :table_alias_length
- alias_method :table_alias_length, :test_table_alias_length
- end
+ def test_table_alias
+ def @connection.test_table_alias_length() 10; end
+ class << @connection
+ alias_method :old_table_alias_length, :table_alias_length
+ alias_method :table_alias_length, :test_table_alias_length
+ end
- assert_equal 'posts', @connection.table_alias_for('posts')
- assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
- assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
+ assert_equal 'posts', @connection.table_alias_for('posts')
+ assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
+ assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
- class << @connection
- remove_method :table_alias_length
- alias_method :table_alias_length, :old_table_alias_length
+ class << @connection
+ remove_method :table_alias_length
+ alias_method :table_alias_length, :old_table_alias_length
+ end
end
- end
- # test resetting sequences in odd tables in postgreSQL
- if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
- require 'models/movie'
- require 'models/subscriber'
+ # test resetting sequences in odd tables in postgreSQL
+ if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
+ require 'models/movie'
+ require 'models/subscriber'
- def test_reset_empty_table_with_custom_pk
- Movie.delete_all
- Movie.connection.reset_pk_sequence! 'movies'
- assert_equal 1, Movie.create(:name => 'fight club').id
- end
+ def test_reset_empty_table_with_custom_pk
+ Movie.delete_all
+ Movie.connection.reset_pk_sequence! 'movies'
+ assert_equal 1, Movie.create(:name => 'fight club').id
+ end
- if ActiveRecord::Base.connection.adapter_name != "FrontBase"
- def test_reset_table_with_non_integer_pk
- Subscriber.delete_all
- Subscriber.connection.reset_pk_sequence! 'subscribers'
- sub = Subscriber.new(:name => 'robert drake')
- sub.id = 'bob drake'
- assert_nothing_raised { sub.save! }
+ if ActiveRecord::Base.connection.adapter_name != "FrontBase"
+ def test_reset_table_with_non_integer_pk
+ Subscriber.delete_all
+ Subscriber.connection.reset_pk_sequence! 'subscribers'
+ sub = Subscriber.new(:name => 'robert drake')
+ sub.id = 'bob drake'
+ assert_nothing_raised { sub.save! }
+ end
end
end
- end
- def test_uniqueness_violations_are_translated_to_specific_exception
- @connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
- assert_raises(ActiveRecord::RecordNotUnique) do
+ def test_uniqueness_violations_are_translated_to_specific_exception
@connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
+ assert_raises(ActiveRecord::RecordNotUnique) do
+ @connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
+ end
end
- end
- def test_foreign_key_violations_are_translated_to_specific_exception
- unless @connection.adapter_name == 'SQLite'
- assert_raises(ActiveRecord::InvalidForeignKey) do
- # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
- if @connection.prefetch_primary_key?
- id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
- @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
- else
- @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ def test_foreign_key_violations_are_translated_to_specific_exception
+ unless @connection.adapter_name == 'SQLite'
+ assert_raises(ActiveRecord::InvalidForeignKey) do
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if @connection.prefetch_primary_key?
+ id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
+ @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
+ else
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ end
end
end
end
- end
- def test_disable_referential_integrity
- assert_nothing_raised do
- @connection.disable_referential_integrity do
- # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
- if @connection.prefetch_primary_key?
- id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
- @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
- else
- @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ def test_disable_referential_integrity
+ assert_nothing_raised do
+ @connection.disable_referential_integrity do
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if @connection.prefetch_primary_key?
+ id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
+ @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
+ else
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ end
+ # should deleted created record as otherwise disable_referential_integrity will try to enable contraints after executed block
+ # and will fail (at least on Oracle)
+ @connection.execute "DELETE FROM fk_test_has_fk"
end
- # should deleted created record as otherwise disable_referential_integrity will try to enable contraints after executed block
- # and will fail (at least on Oracle)
- @connection.execute "DELETE FROM fk_test_has_fk"
end
end
end
-
- def test_deprecated_visitor_for
- visitor_klass = Class.new(Arel::Visitors::ToSql)
- Arel::Visitors::VISITORS['fuuu'] = visitor_klass
- pool = stub(:spec => stub(:config => { :adapter => 'fuuu' }))
- visitor = assert_deprecated {
- ActiveRecord::ConnectionAdapters::AbstractAdapter.visitor_for(pool)
- }
- assert visitor.is_a?(visitor_klass)
- end
end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 2a89430da9..fa2ba8d592 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -3,13 +3,13 @@ require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
- @connection = ActiveRecord::Base.connection
+ @connection = ActiveRecord::Model.connection
end
def test_mysql_reconnect_attribute_after_connection_with_reconnect_true
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true}))
- assert ActiveRecord::Base.connection.raw_connection.reconnect
+ ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => true}))
+ assert ActiveRecord::Model.connection.raw_connection.reconnect
end
end
@@ -25,8 +25,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def test_mysql_reconnect_attribute_after_connection_with_reconnect_false
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false}))
- assert !ActiveRecord::Base.connection.raw_connection.reconnect
+ ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => false}))
+ assert !ActiveRecord::Model.connection.raw_connection.reconnect
end
end
@@ -114,7 +114,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase
# Test that MySQL allows multiple results for stored procedures
if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
def test_multi_results
- rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
+ rows = ActiveRecord::Model.connection.select_rows('CALL ten();')
assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'"
end
@@ -123,11 +123,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
private
def run_without_connection
- original_connection = ActiveRecord::Base.remove_connection
+ original_connection = ActiveRecord::Model.remove_connection
begin
yield original_connection
ensure
- ActiveRecord::Base.establish_connection(original_connection)
+ ActiveRecord::Model.establish_connection(original_connection)
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 146b77a95c..7fe2c02c04 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -17,11 +17,7 @@ module ActiveRecord
end
def test_client_encoding
- if "<3".respond_to?(:encoding)
- assert_equal Encoding::UTF_8, @conn.client_encoding
- else
- assert_equal 'utf8', @conn.client_encoding
- end
+ assert_equal Encoding::UTF_8, @conn.client_encoding
end
def test_exec_insert_number
@@ -41,13 +37,11 @@ module ActiveRecord
value = result.rows.last.last
- if "<3".respond_to?(:encoding)
- # FIXME: this should probably be inside the mysql AR adapter?
- value.force_encoding(@conn.client_encoding)
+ # FIXME: this should probably be inside the mysql AR adapter?
+ value.force_encoding(@conn.client_encoding)
- # The strings in this file are utf-8, so transcode to utf-8
- value.encode!(Encoding::UTF_8)
- end
+ # The strings in this file are utf-8, so transcode to utf-8
+ value.encode!(Encoding::UTF_8)
assert_equal str, value
end
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb
index a2155d1dd1..29f885c6e7 100644
--- a/activerecord/test/cases/adapters/mysql/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/schema_test.rb
@@ -13,8 +13,8 @@ module ActiveRecord
table = Post.table_name
@db_name = db
- @omgpost = Class.new(Post) do
- set_table_name "#{db}.#{table}"
+ @omgpost = Class.new(ActiveRecord::Base) do
+ self.table_name = "#{db}.#{table}"
def self.name; 'Post'; end
end
end
@@ -23,6 +23,10 @@ module ActiveRecord
assert @omgpost.find(:first)
end
+ def test_primary_key
+ assert_equal 'id', @omgpost.primary_key
+ end
+
def test_table_exists?
name = @omgpost.table_name
assert @connection.table_exists?(name), "#{name} table should exist"
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 26091c713b..8e2b9ca9a5 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -3,7 +3,7 @@ require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
- @connection = ActiveRecord::Base.connection
+ @connection = ActiveRecord::Model.connection
end
def test_no_automatic_reconnection_after_timeout
@@ -32,11 +32,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
private
def run_without_connection
- original_connection = ActiveRecord::Base.remove_connection
+ original_connection = ActiveRecord::Model.remove_connection
begin
yield original_connection
ensure
- ActiveRecord::Base.establish_connection(original_connection)
+ ActiveRecord::Model.establish_connection(original_connection)
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb
new file mode 100644
index 0000000000..68ed361aeb
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb
@@ -0,0 +1,26 @@
+require "cases/helper"
+require 'models/developer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class Mysql2Adapter
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain
+ assert_match %(developers | const), explain
+ end
+
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain
+ assert_match %(developers | const), explain
+ assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` IN (1)), explain
+ assert_match %(audit_logs | ALL), explain
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 858d1da2dd..d5676bc522 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -13,8 +13,8 @@ module ActiveRecord
table = Post.table_name
@db_name = db
- @omgpost = Class.new(Post) do
- set_table_name "#{db}.#{table}"
+ @omgpost = Class.new(ActiveRecord::Base) do
+ self.table_name = "#{db}.#{table}"
def self.name; 'Post'; end
end
end
@@ -23,6 +23,10 @@ module ActiveRecord
assert @omgpost.find(:first)
end
+ def test_primary_key
+ assert_equal 'id', @omgpost.primary_key
+ end
+
def test_table_exists?
name = @omgpost.table_name
assert @connection.table_exists?(name), "#{name} table should exist"
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index e4746d4aa3..447d729e52 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -21,6 +21,18 @@ class PostgresqlActiveSchemaTest < ActiveRecord::TestCase
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
end
+ def test_add_index
+ # add_index calls index_name_exists? which can't work since execute is stubbed
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) do |*|
+ false
+ end
+
+ expected = %(CREATE UNIQUE INDEX "index_people_on_last_name" ON "people" ("last_name") WHERE state = 'active')
+ assert_equal expected, add_index(:people, :last_name, :unique => true, :where => "state = 'active'")
+
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:remove_method, :index_name_exists?)
+ end
+
private
def method_missing(method_symbol, *arguments)
ActiveRecord::Base.connection.send(method_symbol, *arguments)
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 21b97b3b39..4baec749ff 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -2,6 +2,9 @@ require "cases/helper"
module ActiveRecord
class PostgresqlConnectionTest < ActiveRecord::TestCase
+ class NonExistentTable < ActiveRecord::Base
+ end
+
def setup
super
@connection = ActiveRecord::Base.connection
@@ -10,5 +13,17 @@ module ActiveRecord
def test_encoding
assert_not_nil @connection.encoding
end
+
+ # Ensure, we can set connection params using the example of Generic
+ # Query Optimizer (geqo). It is 'on' per default.
+ def test_connection_options
+ params = ActiveRecord::Base.connection_config.dup
+ params[:options] = "-c geqo=off"
+ NonExistentTable.establish_connection(params)
+
+ # Verify the connection param has been applied.
+ expect = NonExistentTable.connection.query('show geqo').first.first
+ assert_equal 'off', expect
+ end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb
new file mode 100644
index 0000000000..0b61f61572
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb
@@ -0,0 +1,28 @@
+require "cases/helper"
+require 'models/developer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLAdapter
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match %(QUERY PLAN), explain
+ assert_match %(Index Scan using developers_pkey on developers), explain
+ end
+
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(QUERY PLAN), explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match %(Index Scan using developers_pkey on developers), explain
+ assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain
+ assert_match %(Seq Scan on audit_logs), explain
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
new file mode 100644
index 0000000000..1644a58d92
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -0,0 +1,149 @@
+require "cases/helper"
+require 'active_record/base'
+require 'active_record/connection_adapters/postgresql_adapter'
+
+class PostgresqlHstoreTest < ActiveRecord::TestCase
+ class Hstore < ActiveRecord::Base
+ self.table_name = 'hstores'
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ begin
+ @connection.transaction do
+ @connection.create_table('hstores') do |t|
+ t.hstore 'tags', :default => ''
+ end
+ end
+ rescue ActiveRecord::StatementInvalid
+ return skip "do not test on PG without hstore"
+ end
+ @column = Hstore.columns.find { |c| c.name == 'tags' }
+ end
+
+ def teardown
+ @connection.execute 'drop table if exists hstores'
+ end
+
+ def test_column
+ assert_equal :hstore, @column.type
+ end
+
+ def test_type_cast_hstore
+ assert @column
+
+ data = "\"1\"=>\"2\""
+ hash = @column.class.string_to_hstore data
+ assert_equal({'1' => '2'}, hash)
+ assert_equal({'1' => '2'}, @column.type_cast(data))
+
+ assert_equal({}, @column.type_cast(""))
+ assert_equal({'key'=>nil}, @column.type_cast('key => NULL'))
+ assert_equal({'c'=>'}','"a"'=>'b "a b'}, @column.type_cast(%q(c=>"}", "\"a\""=>"b \"a b")))
+ end
+
+ def test_gen1
+ assert_equal(%q(" "=>""), @column.class.hstore_to_string({' '=>''}))
+ end
+
+ def test_gen2
+ assert_equal(%q(","=>""), @column.class.hstore_to_string({','=>''}))
+ end
+
+ def test_gen3
+ assert_equal(%q("="=>""), @column.class.hstore_to_string({'='=>''}))
+ end
+
+ def test_gen4
+ assert_equal(%q(">"=>""), @column.class.hstore_to_string({'>'=>''}))
+ end
+
+ def test_parse1
+ assert_equal({'a'=>nil,'b'=>nil,'c'=>'NuLl','null'=>'c'}, @column.type_cast('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
+ end
+
+ def test_parse2
+ assert_equal({" " => " "}, @column.type_cast("\\ =>\\ "))
+ end
+
+ def test_parse3
+ assert_equal({"=" => ">"}, @column.type_cast("==>>"))
+ end
+
+ def test_parse4
+ assert_equal({"=a"=>"q=w"}, @column.type_cast('\=a=>q=w'))
+ end
+
+ def test_parse5
+ assert_equal({"=a"=>"q=w"}, @column.type_cast('"=a"=>q\=w'))
+ end
+
+ def test_parse6
+ assert_equal({"\"a"=>"q>w"}, @column.type_cast('"\"a"=>q>w'))
+ end
+
+ def test_parse7
+ assert_equal({"\"a"=>"q\"w"}, @column.type_cast('\"a=>q"w'))
+ end
+
+ def test_rewrite
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
+ x = Hstore.find :first
+ x.tags = { '"a\'' => 'b' }
+ assert x.save!
+ end
+
+
+ def test_select
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
+ x = Hstore.find :first
+ assert_equal({'1' => '2'}, x.tags)
+ end
+
+ def test_select_multikey
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
+ x = Hstore.find :first
+ assert_equal({'1' => '2', '2' => '3'}, x.tags)
+ end
+
+ def test_create
+ assert_cycle('a' => 'b', '1' => '2')
+ end
+
+ def test_nil
+ assert_cycle('a' => nil)
+ end
+
+ def test_quotes
+ assert_cycle('a' => 'b"ar', '1"foo' => '2')
+ end
+
+ def test_whitespace
+ assert_cycle('a b' => 'b ar', '1"foo' => '2')
+ end
+
+ def test_backslash
+ assert_cycle('a\\b' => 'b\\ar', '1"foo' => '2')
+ end
+
+ def test_comma
+ assert_cycle('a, b' => 'bar', '1"foo' => '2')
+ end
+
+ def test_arrow
+ assert_cycle('a=>b' => 'bar', '1"foo' => '2')
+ end
+
+ private
+ def assert_cycle hash
+ x = Hstore.create!(:tags => hash)
+ x.reload
+ assert_equal(hash, x.tags)
+
+ # make sure updates work
+ x.tags = hash
+ x.save!
+ x.reload
+ assert_equal(hash, x.tags)
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index d57794daf8..898d28456b 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -179,6 +179,12 @@ module ActiveRecord
assert_equal Arel.sql('$2'), bind
end
+ def test_partial_index
+ @connection.add_index 'ex', %w{ id number }, :name => 'partial', :where => "number > 100"
+ index = @connection.indexes('ex').find { |idx| idx.name == 'partial' }
+ assert_equal "(number > 100)", index.where
+ end
+
private
def insert(ctx, data)
binds = data.map { |name, value|
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index c8f8714f66..18670b4177 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -10,36 +10,39 @@ class SchemaTest < ActiveRecord::TestCase
INDEX_A_NAME = 'a_index_things_on_name'
INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
INDEX_C_NAME = 'c_index_full_text_search'
+ INDEX_D_NAME = 'd_index_things_on_description_desc'
INDEX_A_COLUMN = 'name'
INDEX_B_COLUMN_S1 = 'email'
INDEX_B_COLUMN_S2 = 'moment'
INDEX_C_COLUMN = %q{(to_tsvector('english', coalesce(things.name, '')))}
+ INDEX_D_COLUMN = 'description'
COLUMNS = [
'id integer',
'name character varying(50)',
'email character varying(50)',
+ 'description character varying(100)',
'moment timestamp without time zone default now()'
]
PK_TABLE_NAME = 'table_with_pk'
class Thing1 < ActiveRecord::Base
- set_table_name "test_schema.things"
+ self.table_name = "test_schema.things"
end
class Thing2 < ActiveRecord::Base
- set_table_name "test_schema2.things"
+ self.table_name = "test_schema2.things"
end
class Thing3 < ActiveRecord::Base
- set_table_name 'test_schema."things.table"'
+ self.table_name = 'test_schema."things.table"'
end
class Thing4 < ActiveRecord::Base
- set_table_name 'test_schema."Things"'
+ self.table_name = 'test_schema."Things"'
end
class Thing5 < ActiveRecord::Base
- set_table_name 'things'
+ self.table_name = 'things'
end
def setup
@@ -54,6 +57,8 @@ class SchemaTest < ActiveRecord::TestCase
@connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});"
@connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});"
@connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});"
+ @connection.execute "CREATE INDEX #{INDEX_D_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_D_COLUMN} DESC);"
+ @connection.execute "CREATE INDEX #{INDEX_D_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_D_COLUMN} DESC);"
@connection.execute "CREATE TABLE #{SCHEMA_NAME}.#{PK_TABLE_NAME} (id serial primary key)"
end
@@ -63,11 +68,15 @@ class SchemaTest < ActiveRecord::TestCase
end
def test_schema_change_with_prepared_stmt
+ altered = false
@connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]]
@connection.exec_query "alter table developers add column zomg int", 'sql', []
+ altered = true
@connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]]
ensure
- @connection.exec_query "alter table developers drop column if exists zomg", 'sql', []
+ # We are not using DROP COLUMN IF EXISTS because that syntax is only
+ # supported by pg 9.X
+ @connection.exec_query("alter table developers drop column zomg", 'sql', []) if altered
end
def test_table_exists?
@@ -184,11 +193,15 @@ class SchemaTest < ActiveRecord::TestCase
end
def test_dump_indexes_for_schema_one
- do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1)
+ do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN)
end
def test_dump_indexes_for_schema_two
- do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2)
+ do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2, INDEX_D_COLUMN)
+ end
+
+ def test_dump_indexes_for_schema_multiple_schemas_in_search_path
+ do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN)
end
def test_with_uppercase_index_name
@@ -288,13 +301,16 @@ class SchemaTest < ActiveRecord::TestCase
@connection.schema_search_path = "'$user', public"
end
- def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name)
+ def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name, third_index_column_name)
with_schema_search_path(this_schema_name) do
indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name}
- assert_equal 2,indexes.size
+ assert_equal 3,indexes.size
do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name)
do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name)
+ do_dump_index_assertions_for_one_index(indexes[2], INDEX_D_NAME, third_index_column_name)
+
+ assert_equal :desc, indexes.select{|i| i.name == INDEX_D_NAME}[0].orders[INDEX_D_COLUMN]
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 5f08f79171..9e7b08ef34 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -1,3 +1,5 @@
+require 'cases/helper'
+
class PostgreSQLUtilsTest < ActiveSupport::TestCase
include ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils
diff --git a/activerecord/test/cases/adapters/postgresql/view_test.rb b/activerecord/test/cases/adapters/postgresql/view_test.rb
index 303ba9245a..66e07b71a0 100644
--- a/activerecord/test/cases/adapters/postgresql/view_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/view_test.rb
@@ -14,7 +14,7 @@ class ViewTest < ActiveRecord::TestCase
]
class ThingView < ActiveRecord::Base
- set_table_name 'test_schema.view_things'
+ self.table_name = 'test_schema.view_things'
end
def setup
diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
new file mode 100644
index 0000000000..b227bce680
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
@@ -0,0 +1,26 @@
+require "cases/helper"
+require 'models/developer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SQLite3Adapter
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
+ end
+
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
+ assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain
+ assert_match(/(SCAN )?TABLE audit_logs/, explain)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index e0152e7ccf..46da1b0a2b 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -70,9 +70,9 @@ module ActiveRecord
assert_equal bd.to_f, @conn.type_cast(bd, nil)
end
- def test_type_cast_unknown
+ def test_type_cast_unknown_should_raise_error
obj = Class.new.new
- assert_equal YAML.dump(obj), @conn.type_cast(obj, nil)
+ assert_raise(TypeError) { @conn.type_cast(obj, nil) }
end
def test_quoted_id
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index eb6f071dc1..17bde6cb62 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -23,8 +23,6 @@ module ActiveRecord
end
def test_column_types
- return skip('only test encoding on 1.9') unless "<3".encoding_aware?
-
owner = Owner.create!(:name => "hello".encode('ascii-8bit'))
owner.reload
select = Owner.columns.map { |c| "typeof(#{c.name})" }.join ', '
@@ -144,8 +142,6 @@ module ActiveRecord
end
def test_quote_binary_column_escapes_it
- return unless "<3".respond_to?(:encode)
-
DualEncoding.connection.execute(<<-eosql)
CREATE TABLE dual_encodings (
id integer PRIMARY KEY AUTOINCREMENT,
@@ -158,6 +154,7 @@ module ActiveRecord
binary.save!
assert_equal str, binary.data
+ ensure
DualEncoding.connection.drop_table('dual_encodings')
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index ff376a68d8..6733f3e889 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -61,7 +61,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_duplicated_includes
- categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null")
+ categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null").references(:categorizations)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.all.size
@@ -69,7 +69,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_twice_includes_edge_cases
- categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null")
+ categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null").references(:posts)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.all.size
@@ -127,7 +127,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
silly.parent_id = 1
assert silly.save
- topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id')
+ topics = Topic.find(:all, :include => :replies, :order => ['topics.id', 'replies_topics.id'])
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
index d75791cab9..7965bb404c 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -4,7 +4,7 @@ require 'models/tagging'
module Namespaced
class Post < ActiveRecord::Base
- set_table_name 'posts'
+ self.table_name = 'posts'
has_one :tagging, :as => :taggable, :class_name => 'Tagging'
end
end
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 2cf9f89c3c..1e1958410c 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -6,7 +6,6 @@ require 'models/comment'
require 'models/category'
require 'models/categorization'
require 'models/tagging'
-require 'active_support/core_ext/array/random_access'
module Remembered
extend ActiveSupport::Concern
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index c6e451fc57..b79c69bbb5 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -61,7 +61,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_conditions_with_or
- posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
+ posts = authors(:david).posts.find(
+ :all, :include => :comments, :references => :comments,
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
+ )
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
"expected to find only david's posts"
end
@@ -164,7 +167,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
car_post.categories << categories(:technology)
comment = car_post.comments.create!(:body => "hmm")
- categories = Category.find(:all, :conditions => ["posts.id=?", car_post.id],
+ categories = Category.find(:all, :conditions => { 'posts.id' => car_post.id },
:include => {:posts => :comments})
categories.each do |category|
assert_equal [comment], category.posts[0].comments
@@ -252,6 +255,50 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ def test_nested_loading_through_has_one_association
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts})
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_order
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts}, :order => 'author_addresses.id')
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_order_on_association
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts}, :order => 'authors.id')
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_order_on_nested_association
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts}, :order => 'posts.id')
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_conditions
+ aa = AuthorAddress.find(
+ author_addresses(:david_address).id, :include => {:author => :posts},
+ :conditions => "author_addresses.id > 0", :references => :author_addresses
+ )
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_conditions_on_association
+ aa = AuthorAddress.find(
+ author_addresses(:david_address).id, :include => {:author => :posts},
+ :conditions => "authors.id > 0", :references => :authors
+ )
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_conditions_on_nested_association
+ aa = AuthorAddress.find(
+ author_addresses(:david_address).id, :include => {:author => :posts},
+ :conditions => "posts.id > 0", :references => :posts
+ )
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
def test_eager_association_loading_with_belongs_to_and_foreign_keys
pets = Pet.find(:all, :include => :owner)
assert_equal 3, pets.length
@@ -297,7 +344,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.find(:all, :include => :post, :conditions => ['posts.id = ?',4])
+ ActiveSupport::Deprecation.silence do
+ Comment.find(:all, :include => :post, :conditions => ['posts.id = ?',4])
+ end
end
end
@@ -316,7 +365,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
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
- Comment.find(:all, :include => :post, :conditions => ["#{quoted_posts_id} = ?",4])
+ ActiveSupport::Deprecation.silence do
+ Comment.find(:all, :include => :post, :conditions => ["#{quoted_posts_id} = ?",4])
+ end
end
end
@@ -329,7 +380,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
assert_nothing_raised do
- Comment.find(:all, :include => :post, :order => quoted_posts_id)
+ ActiveSupport::Deprecation.silence do
+ Comment.find(:all, :include => :post, :order => quoted_posts_id)
+ end
end
end
@@ -493,22 +546,27 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ posts = ActiveSupport::Deprecation.silence do
+ Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ end
assert_equal 2, posts.size
- count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ count = ActiveSupport::Deprecation.silence do
+ Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ end
assert_equal count, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => { 'authors.name' => 'David' })
assert_equal 0, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
assert_queries(1) do
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10,
- :conditions => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ])
+ :conditions => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ],
+ :references => [:authors, :comments])
assert_equal 0, posts.size
end
end
@@ -522,7 +580,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_count_eager_with_has_many_and_limit_and_high_offset
- posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
+ posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => { 'authors.name' => 'David' })
assert_equal 0, posts
end
@@ -534,7 +592,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
author = authors(:david)
author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
- assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null')
+ assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null', :references => :comments)
end
def test_eager_count_performed_on_a_has_many_through_association_with_multi_table_conditional
@@ -573,6 +631,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts = authors(:david).posts.find(:all,
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :references => :comments,
:limit => 2
)
assert_equal 2, posts.size
@@ -580,6 +639,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
count = Post.count(
:include => [ :comments, :author ],
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :references => [:authors, :comments],
:limit => 2
)
assert_equal count, posts.size
@@ -589,7 +649,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts = nil
Post.send(:with_scope, :find => {
:include => :comments,
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :references => :comments
}) do
posts = authors(:david).posts.find(:all, :limit => 2)
assert_equal 2, posts.size
@@ -597,7 +658,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.send(:with_scope, :find => {
:include => [ :comments, :author ],
- :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
+ :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :references => [:authors, :comments]
}) do
count = Post.count(:limit => 2)
assert_equal count, posts.size
@@ -609,6 +671,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts = authors(:david).posts.find(:all,
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :references => :comments,
:limit => 2
)
assert_equal 2, posts.size
@@ -616,6 +679,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
count = Post.count(
:include => [ :comments, :author ],
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :references => [:authors, :comments],
:limit => 2
)
assert_equal count, posts.size
@@ -623,9 +687,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_scoped_order_using_association_limiting_without_explicit_scope
- posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
+ posts_with_explicit_order = Post.find(
+ :all, :conditions => 'comments.id is not null', :references => :comments,
+ :include => :comments, :order => 'posts.id DESC', :limit => 2
+ )
posts_with_scoped_order = Post.send(:with_scope, :find => {:order => 'posts.id DESC'}) do
- Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
+ Post.find(
+ :all, :conditions => 'comments.id is not null',
+ :references => :comments, :include => :comments, :limit => 2
+ )
end
assert_equal posts_with_explicit_order, posts_with_scoped_order
end
@@ -738,17 +808,49 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_limited_eager_with_order
- assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
- assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
+ assert_equal(
+ posts(:thinking, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => 'UPPER(posts.title)', :limit => 2, :offset => 1
+ )
+ )
+ assert_equal(
+ posts(:sti_post_and_comments, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1
+ )
+ )
end
def test_limited_eager_with_multiple_order_columns
- assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1)
- 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)
+ assert_equal(
+ posts(:thinking, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1
+ )
+ )
+ 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)
+ assert_equal(
+ people(:david, :susan),
+ Person.find(
+ :all, :include => [:readers, :primary_contact, :number1_fan],
+ :conditions => "number1_fans_people.first_name like 'M%'",
+ :references => :number1_fans_people,
+ :order => 'people.id', :limit => 2, :offset => 0
+ )
+ )
end
def test_preload_with_interpolation
@@ -863,11 +965,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_count_with_include
if current_adapter?(:SybaseAdapter)
- assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15", :references => :comments)
elsif current_adapter?(:OpenBaseAdapter)
- assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15", :references => :comments)
else
- assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15", :references => :comments)
end
end
@@ -878,7 +980,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_conditions_on_join_table_with_include_and_limit
- assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
+ assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => { 'developers_projects.access_level' => 1 }, :limit => 5).size
end
def test_order_on_join_table_with_include_and_limit
@@ -1060,4 +1162,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
end
end
+
+ def test_deep_including_through_habtm
+ posts = Post.find(:all, :include => {:categories => :categorizations}, :order => "posts.id")
+ assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length }
+ assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length }
+ assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
+ end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 8dc1423375..d7c489c2b5 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -36,11 +36,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
def test_marshalling_extensions
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
david = developers(:david)
assert_equal projects(:action_controller), david.projects.find_most_recent
@@ -51,11 +46,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
def test_marshalling_named_extensions
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
david = developers(:david)
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
@@ -71,6 +61,12 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension', extension_name(MyApplication::Business::Developer)
end
+ def test_proxy_association_after_scoped
+ post = posts(:welcome)
+ assert_equal post.association(:comments), post.comments.the_association
+ assert_equal post.association(:comments), post.comments.scoped.the_association
+ end
+
private
def extension_name(model)
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 34d90cc395..f457dfb9b3 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
@@ -23,7 +23,7 @@ require 'models/treaty'
require 'active_support/core_ext/string/conversions'
class ProjectWithAfterCreateHook < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
has_and_belongs_to_many :developers,
:class_name => "DeveloperForProjectWithAfterCreateHook",
:join_table => "developers_projects",
@@ -39,7 +39,7 @@ class ProjectWithAfterCreateHook < ActiveRecord::Base
end
class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
has_and_belongs_to_many :projects,
:class_name => "ProjectWithAfterCreateHook",
:join_table => "developers_projects",
@@ -48,7 +48,7 @@ class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
end
class ProjectWithSymbolsForKeys < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
has_and_belongs_to_many :developers,
:class_name => "DeveloperWithSymbolsForKeys",
:join_table => :developers_projects,
@@ -57,7 +57,7 @@ class ProjectWithSymbolsForKeys < ActiveRecord::Base
end
class DeveloperWithSymbolsForKeys < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
has_and_belongs_to_many :projects,
:class_name => "ProjectWithSymbolsForKeys",
:join_table => :developers_projects,
@@ -66,7 +66,7 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
end
class DeveloperWithCounterSQL < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
has_and_belongs_to_many :projects,
:class_name => "DeveloperWithCounterSQL",
:join_table => "developers_projects",
@@ -77,7 +77,7 @@ end
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
- :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
+ :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings
def setup_data_for_habtm_case
ActiveRecord::Base.connection.execute('delete from countries_treaties')
@@ -445,6 +445,26 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert david.projects(true).empty?
end
+ def test_destroy_associations_destroys_multiple_associations
+ george = parrots(:george)
+ assert !george.pirates.empty?
+ assert !george.treasures.empty?
+
+ assert_no_difference "Pirate.count" do
+ assert_no_difference "Treasure.count" do
+ george.destroy_associations
+ end
+ end
+
+ join_records = Parrot.connection.select_all("SELECT * FROM parrots_pirates WHERE parrot_id = #{george.id}")
+ assert join_records.empty?
+ assert george.pirates(true).empty?
+
+ join_records = Parrot.connection.select_all("SELECT * FROM parrots_treasures WHERE parrot_id = #{george.id}")
+ assert join_records.empty?
+ assert george.treasures(true).empty?
+ end
+
def test_deprecated_push_with_attributes_was_removed
jamis = developers(:jamis)
assert_raise(NoMethodError) do
@@ -659,7 +679,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_join_table_alias
- assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
+ assert_equal(
+ 3,
+ Developer.find(
+ :all, :include => {:projects => :developers}, :references => :developers_projects_join,
+ :conditions => 'developers_projects_join.joined_on IS NOT NULL'
+ ).size
+ )
end
def test_join_with_group
@@ -669,7 +695,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
Project.columns.each { |c| group << "projects.#{c.name}" }
- assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
+ assert_equal(
+ 3,
+ Developer.find(
+ :all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL',
+ :references => :developers_projects_join, :group => group.join(",")
+ ).size
+ )
end
def test_find_grouped
@@ -805,12 +837,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
# clear cache possibly created by other tests
david.projects.reset_column_information
- # One query for columns, one for primary key
- assert_queries(2) { david.projects.columns; david.projects.columns }
+ assert_queries(1) { david.projects.columns; david.projects.columns }
## and again to verify that reset_column_information clears the cache correctly
david.projects.reset_column_information
- assert_queries(2) { david.projects.columns; david.projects.columns }
+ assert_queries(1) { david.projects.columns; david.projects.columns }
end
def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index cddd2a6f8c..3967009c82 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -8,6 +8,7 @@ require 'models/reply'
require 'models/category'
require 'models/post'
require 'models/author'
+require 'models/essay'
require 'models/comment'
require 'models/person'
require 'models/reader'
@@ -41,12 +42,37 @@ class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase
end
end
+class HasManyAssociationsTestForCountDistinctWithFinderSql < ActiveRecord::TestCase
+ class Invoice < ActiveRecord::Base
+ has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT DISTINCT line_items.amount from line_items"
+ end
+
+ def test_should_count_distinct_results
+ invoice = Invoice.new
+ invoice.custom_line_items << LineItem.new(:amount => 0)
+ invoice.custom_line_items << LineItem.new(:amount => 0)
+ invoice.save!
+
+ assert_equal 1, invoice.custom_line_items.count
+ end
+end
+
+class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
+ fixtures :authors, :posts, :comments
+
+ def test_should_generate_valid_sql
+ author = authors(:david)
+ # this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression
+ # if the reorder clauses are not correctly handled
+ assert author.posts_with_comments_sorted_by_comment_id.where('comments.id > 0').reorder('posts.comments_count DESC', 'posts.taggings_count DESC').last
+ end
+end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
:developers_projects, :topics, :authors, :comments,
- :people, :posts, :readers, :taggings, :cars
+ :people, :posts, :readers, :taggings, :cars, :essays
def setup
Client.destroyed_client_ids.clear
@@ -712,6 +738,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal number_of_clients + 1, companies(:first_firm).clients_of_firm.size
end
+ def test_find_or_initialize_returns_the_instantiated_object
+ client = companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client")
+ assert_equal client, companies(:first_firm).clients_of_firm[-1]
+ end
+
+ def test_find_or_initialize_only_instantiates_a_single_object
+ number_of_clients = Client.count
+ companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client").save!
+ companies(:first_firm).save!
+ assert_equal number_of_clients+1, Client.count
+ end
+
def test_find_or_create_with_hash
post = authors(:david).posts.find_or_create_by_title(:title => 'Yet another post', :body => 'somebody')
assert_equal post, authors(:david).posts.find_or_create_by_title(:title => 'Yet another post', :body => 'somebody')
@@ -1114,16 +1152,42 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_nil companies(:leetsoft).reload.client_of
assert_nil companies(:jadedpixel).reload.client_of
-
assert_equal num_accounts, Account.count
end
def test_restrict
- firm = RestrictedFirm.new(:name => 'restrict')
- firm.save!
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
firm.companies.create(:name => 'child')
+
assert !firm.companies.empty?
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.companies.exists?(:name => 'child')
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
+
+ def test_restrict_when_dependent_restrict_raises_config_set_to_false
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = false
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
+ firm.companies.create(:name => 'child')
+
+ assert !firm.companies.empty?
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+
+ assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.companies.exists?(:name => 'child')
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
end
def test_included_in_collection
@@ -1227,6 +1291,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert company.clients_using_sql.loaded?
end
+ def test_get_ids_for_ordered_association
+ assert_equal [companies(:second_client).id, companies(:first_client).id], companies(:first_firm).clients_ordered_by_name_ids
+ end
+
def test_assign_ids_ignoring_blanks
firm = Firm.create!(:name => 'Apple')
firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
@@ -1376,6 +1444,32 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_custom_primary_key_on_new_record_should_fetch_with_query
+ author = Author.new(:name => "David")
+ assert !author.essays.loaded?
+
+ assert_queries 1 do
+ assert_equal 1, author.essays.size
+ end
+
+ assert_equal author.essays, Essay.find_all_by_writer_id("David")
+
+ end
+
+ def test_has_many_custom_primary_key
+ david = authors(:david)
+ assert_equal david.essays, Essay.find_all_by_writer_id("David")
+ end
+
+ def test_blank_custom_primary_key_on_new_record_should_not_run_queries
+ author = Author.new
+ assert !author.essays.loaded?
+
+ assert_queries 0 do
+ assert_equal 0, author.essays.size
+ end
+ end
+
def test_calling_first_or_last_with_find_options_on_loaded_association_should_fetch_with_query
firm = companies(:first_firm)
firm.clients.class # force load target
@@ -1597,4 +1691,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb2], car.bulbs
assert_equal [bulb2], car.reload.bulbs
end
+
+ def test_building_has_many_association_with_restrict_dependency
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ klass = Class.new(ActiveRecord::Base)
+
+ assert_deprecated { klass.has_many :companies, :dependent => :restrict }
+ assert_not_deprecated { klass.has_many :companies }
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index b703c96ec1..9cc09194dc 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -67,6 +67,31 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_associate_existing_record_twice_should_add_records_twice
+ post = posts(:thinking)
+ person = people(:david)
+
+ assert_difference 'post.people.count', 2 do
+ post.people << person
+ post.people << person
+ end
+ end
+
+ def test_add_two_instance_and_then_deleting
+ post = posts(:thinking)
+ person = people(:david)
+
+ post.people << person
+ post.people << person
+
+ counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
+ assert_difference counts, -2 do
+ post.people.delete(person)
+ end
+
+ assert !post.people.reload.include?(person)
+ end
+
def test_associating_new
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
@@ -503,6 +528,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
end
+ def test_get_ids_for_has_many_through_with_conditions_should_not_preload
+ Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
+ ActiveRecord::Associations::Preloader.expects(:new).never
+ posts(:welcome).misc_tag_ids
+ end
+
def test_get_ids_for_loaded_associations
person = people(:michael)
person.posts(true)
@@ -707,7 +738,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_select_chosen_fields_only
author = authors(:david)
- assert_equal ['body'], author.comments.select('comments.body').first.attributes.keys
+ assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
end
def test_get_has_many_through_belongs_to_ids_with_conditions
@@ -816,7 +847,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_preloading_empty_through_association_via_joins
person = Person.create!(:first_name => "Gaga")
- person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').includes(:posts).to_a.first
+ person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').references(:readers).includes(:posts).to_a.first
assert person.posts.loaded?, 'person.posts should be loaded'
assert_equal [], person.posts
@@ -825,4 +856,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_explicitly_joining_join_table
assert_equal owners(:blackbeard).toys, owners(:blackbeard).toys.with_pet
end
+
+ def test_has_many_through_with_polymorphic_source
+ post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
+ assert_equal [tags(:general)], post.reload.tags
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 26931e3e85..246877bbed 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -157,11 +157,62 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_dependence_with_restrict
- firm = RestrictedFirm.new(:name => 'restrict')
- firm.save!
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
firm.create_account(:credit_limit => 10)
+
assert_not_nil firm.account
+
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.account.present?
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
+
+ def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = false
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
+ firm.create_account(:credit_limit => 10)
+
+ assert_not_nil firm.account
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+ assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.account.present?
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
+
+ def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false_and_attribute_name
+ old_backend = I18n.backend
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations 'en', :activerecord => {:attributes => {:restricted_firm => {:account => "account model"}}}
+
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = false
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
+ firm.create_account(:credit_limit => 10)
+
+ assert_not_nil firm.account
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+ assert_equal "Cannot delete record because a dependent account model exists", firm.errors[:base].first
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.account.present?
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ I18n.backend = old_backend
end
def test_successful_build_association
@@ -456,4 +507,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal car.id, bulb.attributes_after_initialize['car_id']
end
+
+ def test_building_has_one_association_with_dependent_restrict
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ klass = Class.new(ActiveRecord::Base)
+
+ assert_deprecated { klass.has_one :account, :dependent => :restrict }
+ assert_not_deprecated { klass.has_one :account }
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index e5e9ca6131..68a1e62328 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -67,7 +67,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
def test_find_with_implicit_inner_joins_does_not_set_associations
authors = Author.joins(:posts).select('authors.*')
assert !authors.empty?, "expected authors to be non-empty"
- assert authors.all? {|a| !a.send(:instance_variable_names).include?("@posts")}, "expected no authors to have the @posts association loaded"
+ assert authors.all? { |a| !a.instance_variable_defined?(:@posts) }, "expected no authors to have the @posts association loaded"
end
def test_count_honors_implicit_inner_joins
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 4ce8b85098..301755249c 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -362,7 +362,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
assert_raise ActiveRecord::EagerLoadPolymorphicError do
- tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
+ tags(:general).taggings.find(:all, :include => :taggable, :references => :bogus_table, :conditions => 'bogus_table.column = 1')
end
end
@@ -733,7 +733,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
Post.find(post_id).update_column :type, class_name
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
- klass.set_table_name 'posts'
+ klass.table_name = 'posts'
klass.send(association, association_name, :as => :taggable, :dependent => dependency)
klass.find(post_id)
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 530f5212a2..03d99d19f6 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -505,7 +505,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_through_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id'),
+ Author.where('tags.id = tags.id').references(:tags),
[authors(:bob)], :misc_post_first_blue_tags
)
end
@@ -526,7 +526,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_source_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id'),
+ Author.where('tags.id = tags.id').references(:tags),
[authors(:bob)], :misc_post_first_blue_tags_2
)
end
@@ -545,6 +545,15 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [organizations(:nsa)], organizations
end
+ def test_nested_has_many_through_should_not_be_autosaved
+ c = Categorization.new
+ c.author = authors(:david)
+ c.post_taggings.to_a
+ assert !c.post_taggings.empty?
+ c.save
+ assert !c.post_taggings.empty?
+ end
+
private
def assert_includes_and_joins_equal(query, expected, association)
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index ffe2993e0f..017905e0ac 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'models/computer'
require 'models/developer'
require 'models/project'
require 'models/company'
@@ -28,7 +29,7 @@ class AssociationsTest < ActiveRecord::TestCase
molecule.electrons.create(:name => 'electron_1')
molecule.electrons.create(:name => 'electron_2')
- liquids = Liquid.includes(:molecules => :electrons).where('molecules.id is not null')
+ liquids = Liquid.includes(:molecules => :electrons).references(:molecules).where('molecules.id is not null')
assert_equal 1, liquids[0].molecules.length
end
@@ -128,6 +129,11 @@ class AssociationsTest < ActiveRecord::TestCase
end
end
+ def test_association_with_references
+ firm = companies(:first_firm)
+ assert_equal ['foo'], firm.association_with_references.scoped.references_values
+ end
+
end
class AssociationProxyTest < ActiveRecord::TestCase
@@ -273,3 +279,18 @@ class OverridingAssociationsTest < ActiveRecord::TestCase
)
end
end
+
+class GeneratedMethodsTest < ActiveRecord::TestCase
+ fixtures :developers, :computers, :posts, :comments
+ def test_association_methods_override_attribute_methods_of_same_name
+ assert_equal(developers(:david), computers(:workstation).developer)
+ # this next line will fail if the attribute methods module is generated lazily
+ # after the association methods module is generated
+ assert_equal(developers(:david), computers(:workstation).developer)
+ assert_equal(developers(:david).id, computers(:workstation)[:developer])
+ end
+
+ def test_model_method_overrides_association_method
+ assert_equal(comments(:greetings).body, posts(:welcome).first_comment)
+ end
+end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index e03ed33591..764305459d 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'active_support/core_ext/object/inclusion'
+require 'thread'
module ActiveRecord
module AttributeMethods
@@ -14,8 +15,18 @@ module ActiveRecord
def setup
@klass = Class.new do
+ def self.superclass; Base; end
+ def self.active_record_super; Base; end
+ def self.base_class; self; end
+
include ActiveRecord::AttributeMethods
- include ActiveRecord::AttributeMethods::Read
+
+ def self.define_attribute_methods
+ # Created in the inherited/included hook for "proper" ARs
+ @attribute_methods_mutex ||= Mutex.new
+
+ super
+ end
def self.column_names
%w{ one two three }
@@ -33,9 +44,6 @@ module ActiveRecord
[name, FakeColumn.new(name)]
}]
end
-
- def self.serialized_attributes; {}; end
- def self.base_class; self; end
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index b1b41fed0d..4078b7eb0b 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -35,6 +35,30 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !t.attribute_present?("content")
end
+ def test_attribute_present_with_booleans
+ b1 = Boolean.new
+ b1.value = false
+ assert b1.attribute_present?(:value)
+
+ b2 = Boolean.new
+ b2.value = true
+ assert b2.attribute_present?(:value)
+
+ b3 = Boolean.new
+ assert !b3.attribute_present?(:value)
+
+ b4 = Boolean.new
+ b4.value = false
+ b4.save!
+ assert Boolean.find(b4.id).attribute_present?(:value)
+ end
+
+ def test_caching_nil_primary_key
+ klass = Class.new(Minimalistic)
+ klass.expects(:reset_primary_key).returns(nil).once
+ 2.times { klass.primary_key }
+ end
+
def test_attribute_keys_on_new_instance
t = Topic.new
assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
@@ -78,7 +102,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_respond_to?
topic = Topic.find(1)
assert_respond_to topic, "title"
- assert_respond_to topic, "_title"
assert_respond_to topic, "title?"
assert_respond_to topic, "title="
assert_respond_to topic, :title
@@ -95,9 +118,15 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_not_nil keyboard.key_number
assert_equal keyboard.key_number, keyboard.id
assert keyboard.respond_to?('key_number')
- assert keyboard.respond_to?('_key_number')
assert keyboard.respond_to?('id')
- assert keyboard.respond_to?('_id')
+ end
+
+ def test_id_before_type_cast_with_custom_primary_key
+ keyboard = Keyboard.create
+ keyboard.key_number = '10'
+ assert_equal '10', keyboard.id_before_type_cast
+ assert_equal nil, keyboard.read_attribute_before_type_cast('id')
+ assert_equal '10', keyboard.read_attribute_before_type_cast('key_number')
end
# Syck calls respond_to? before actually calling initialize
@@ -237,8 +266,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic.send(:write_attribute, :title, "Still another topic")
assert_equal "Still another topic", topic.title
- topic.send(:write_attribute, "title", "Still another topic: part 2")
+ topic[:title] = "Still another topic: part 2"
assert_equal "Still another topic: part 2", topic.title
+
+ topic.send(:write_attribute, "title", "Still another topic: part 3")
+ assert_equal "Still another topic: part 3", topic.title
+
+ topic["title"] = "Still another topic: part 4"
+ assert_equal "Still another topic: part 4", topic.title
end
def test_read_attribute
@@ -291,6 +326,39 @@ class AttributeMethodsTest < ActiveRecord::TestCase
# puts ""
end
+ def test_overridden_write_attribute
+ topic = Topic.new
+ def topic.write_attribute(attr_name, value)
+ super(attr_name, value.downcase)
+ end
+
+ topic.send(:write_attribute, :title, "Yet another topic")
+ assert_equal "yet another topic", topic.title
+
+ topic[:title] = "Yet another topic: part 2"
+ assert_equal "yet another topic: part 2", topic.title
+
+ topic.send(:write_attribute, "title", "Yet another topic: part 3")
+ assert_equal "yet another topic: part 3", topic.title
+
+ topic["title"] = "Yet another topic: part 4"
+ assert_equal "yet another topic: part 4", topic.title
+ end
+
+ def test_overridden_read_attribute
+ topic = Topic.new
+ topic.title = "Stop changing the topic"
+ def topic.read_attribute(attr_name)
+ super(attr_name).upcase
+ end
+
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, "title")
+ assert_equal "STOP CHANGING THE TOPIC", topic["title"]
+
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, :title)
+ assert_equal "STOP CHANGING THE TOPIC", topic[:title]
+ end
+
def test_read_overridden_attribute
topic = Topic.new(:title => 'a')
def topic.title() 'b' end
@@ -489,6 +557,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_write_time_to_date_attributes
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new
+ record.last_read = Time.utc(2010, 1, 1, 10)
+ assert_equal Date.civil(2010, 1, 1), record.last_read
+ end
+ end
+
def test_time_attributes_are_retrieved_in_current_time_zone
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
@@ -524,6 +600,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_setting_time_zone_aware_read_attribute
+ utc_time = Time.utc(2008, 1, 1)
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.create(:written_on => cst_time).reload
+ assert_equal utc_time, record[:written_on]
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record[:written_on].time_zone
+ assert_equal Time.utc(2007, 12, 31, 16), record[:written_on].time
+ end
+ end
+
def test_setting_time_zone_aware_attribute_with_string
utc_time = Time.utc(2008, 1, 1)
(-11..13).each do |timezone_offset|
@@ -543,6 +630,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = ' '
assert_nil record.written_on
+ assert_nil record[:written_on]
end
end
@@ -657,7 +745,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = Topic.new(:id => 5)
topic.id = 5
- topic.method(:id).owner.send(:remove_method, :id)
+ topic.method(:id).owner.send(:undef_method, :id)
assert_deprecated do
assert_equal 5, topic.id
@@ -666,17 +754,39 @@ class AttributeMethodsTest < ActiveRecord::TestCase
Topic.undefine_attribute_methods
end
+ def test_read_attribute_with_nil_should_not_asplode
+ assert_equal nil, Topic.new.read_attribute(nil)
+ end
+
+ # If B < A, and A defines an accessor for 'foo', we don't want to override
+ # that by defining a 'foo' method in the generated methods module for B.
+ # (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
+ def test_inherited_custom_accessors
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+ self.abstract_class = true
+ def title; "omg"; end
+ def title=(val); self.author_name = val; end
+ end
+ subklass = Class.new(klass)
+ [klass, subklass].each(&:define_attribute_methods)
+
+ topic = subklass.find(1)
+ assert_equal "omg", topic.title
+
+ topic.title = "lol"
+ assert_equal "lol", topic.author_name
+ end
+
private
def cached_columns
- @cached_columns ||= (time_related_columns_on_topic + serialized_columns_on_topic).map(&:name)
+ Topic.columns.find_all { |column|
+ !Topic.serialized_attributes.include? column.name
+ }.map(&:name)
end
def time_related_columns_on_topic
- Topic.columns.select { |c| c.type.in?([:time, :date, :datetime, :timestamp]) }
- end
-
- def serialized_columns_on_topic
- Topic.columns.select { |c| Topic.serialized_attributes.include?(c.name) }
+ Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }
end
def in_time_zone(zone)
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 4ad2cdfc7e..1376810adf 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -347,6 +347,17 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
client.save!
assert_no_queries { assert_equal apple, client.firm }
end
+
+ def test_validation_does_not_validate_stale_association_target
+ valid_developer = Developer.create!(:name => "Dude", :salary => 50_000)
+ invalid_developer = Developer.new()
+
+ auditlog = AuditLog.new(:message => "foo")
+ auditlog.developer = invalid_developer
+ auditlog.developer_id = valid_developer.id
+
+ assert auditlog.valid?
+ end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
@@ -575,6 +586,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
end
@@ -897,6 +909,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
end
@@ -1020,6 +1033,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@ship = Ship.create(:name => 'Nights Dirty Lightning')
@pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
end
@@ -1268,6 +1282,7 @@ class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@association_name = :birds
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@@ -1282,6 +1297,7 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@association_name = :parrots
@habtm = true
@@ -1297,6 +1313,7 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@pirate.birds.create(:name => 'cookoo')
end
@@ -1313,6 +1330,7 @@ class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::Tes
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@pirate.create_ship(:name => 'titanic')
super
@@ -1335,6 +1353,7 @@ class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
end
@@ -1355,6 +1374,7 @@ class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::Test
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
end
@@ -1377,6 +1397,7 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.new
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 12c1cfb30e..698c3d0cb1 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -23,10 +23,18 @@ require 'models/edge'
require 'models/joke'
require 'models/bulb'
require 'models/bird'
+require 'models/teapot'
require 'rexml/document'
require 'active_support/core_ext/exception'
require 'bcrypt'
+class FirstAbstractClass < ActiveRecord::Base
+ self.abstract_class = true
+end
+class SecondAbstractClass < FirstAbstractClass
+ self.abstract_class = true
+end
+class Photo < SecondAbstractClass; end
class Category < ActiveRecord::Base; end
class Categorization < ActiveRecord::Base; end
class Smarts < ActiveRecord::Base; end
@@ -69,6 +77,16 @@ end
class BasicsTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
+ def test_generated_methods_modules
+ modules = Computer.ancestors
+ assert modules.include?(Computer::GeneratedFeatureMethods)
+ assert_equal(Computer::GeneratedFeatureMethods, Computer.generated_feature_methods)
+ assert(modules.index(Computer.generated_attribute_methods) > modules.index(Computer.generated_feature_methods),
+ "generated_attribute_methods must be higher in inheritance hierarchy than generated_feature_methods")
+ assert_not_equal Computer.generated_feature_methods, Post.generated_feature_methods
+ assert(modules.index(Computer.generated_attribute_methods) < modules.index(ActiveRecord::Base.ancestors[1]))
+ end
+
def test_column_names_are_escaped
conn = ActiveRecord::Base.connection
classname = conn.class.name[/[^:]*$/]
@@ -169,6 +187,31 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_previously_changed
+ topic = Topic.find :first
+ topic.title = '<3<3<3'
+ assert_equal({}, topic.previous_changes)
+
+ topic.save!
+ expected = ["The First Topic", "<3<3<3"]
+ assert_equal(expected, topic.previous_changes['title'])
+ end
+
+ def test_previously_changed_dup
+ topic = Topic.find :first
+ topic.title = '<3<3<3'
+ topic.save!
+
+ t2 = topic.dup
+
+ assert_equal(topic.previous_changes, t2.previous_changes)
+
+ topic.title = "lolwut"
+ topic.save!
+
+ assert_not_equal(topic.previous_changes, t2.previous_changes)
+ end
+
def test_preserving_time_objects
assert_kind_of(
Time, Topic.find(1).bonus_time,
@@ -561,10 +604,12 @@ class BasicsTest < ActiveRecord::TestCase
weird = Weird.create('a$b' => 'value')
weird.reload
assert_equal 'value', weird.send('a$b')
+ assert_equal 'value', weird.read_attribute('a$b')
weird.update_column('a$b', 'value2')
weird.reload
assert_equal 'value2', weird.send('a$b')
+ assert_equal 'value2', weird.read_attribute('a$b')
end
def test_multiparameter_attributes_on_date
@@ -644,7 +689,7 @@ class BasicsTest < ActiveRecord::TestCase
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
end
def test_multiparameter_attributes_on_time_with_no_date
@@ -893,7 +938,7 @@ class BasicsTest < ActiveRecord::TestCase
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
end
def test_boolean
@@ -947,10 +992,9 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "b", duped_topic.title
# test if the attribute values have been duped
- topic.title = {"a" => "b"}
duped_topic = topic.dup
- duped_topic.title["a"] = "c"
- assert_equal "b", topic.title["a"]
+ duped_topic.title.replace "c"
+ assert_equal "a", topic.title
# test if attributes set as part of after_initialize are duped correctly
assert_equal topic.author_email_address, duped_topic.author_email_address
@@ -961,8 +1005,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_equal duped_topic.id, topic.id
duped_topic.reload
- # FIXME: I think this is poor behavior, and will fix it with #5686
- assert_equal({'a' => 'c'}.to_yaml, duped_topic.title)
+ assert_equal("c", duped_topic.title)
end
def test_dup_with_aggregate_of_same_name_as_attribute
@@ -1061,7 +1104,10 @@ class BasicsTest < ActiveRecord::TestCase
# TODO: extend defaults tests to other databases!
if current_adapter?(:PostgreSQLAdapter)
def test_default
+ tz = Default.default_timezone
+ Default.default_timezone = :local
default = Default.new
+ Default.default_timezone = tz
# fixed dates / times
assert_equal Date.new(2004, 1, 1), default.fixed_date
@@ -1104,7 +1150,8 @@ class BasicsTest < ActiveRecord::TestCase
# use a geometric function to test for an open path
objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
- assert_equal objs[0].isopen, 't'
+
+ assert_equal true, objs[0].isopen
# test alternate formats when defining the geometric types
@@ -1132,7 +1179,8 @@ class BasicsTest < ActiveRecord::TestCase
# use a geometric function to test for an closed path
objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
- assert_equal objs[0].isclosed, 't'
+
+ assert_equal true, objs[0].isclosed
end
end
@@ -1185,19 +1233,6 @@ class BasicsTest < ActiveRecord::TestCase
assert(auto.id > 0)
end
- def quote_column_name(name)
- "<#{name}>"
- end
-
- def test_quote_keys
- ar = AutoId.new
- source = {"foo" => "bar", "baz" => "quux"}
- actual = ar.send(:quote_columns, self, source)
- inverted = actual.invert
- assert_equal("<foo>", inverted["bar"])
- assert_equal("<baz>", inverted["quux"])
- end
-
def test_sql_injection_via_find
assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
Topic.find("123456 OR id > 0")
@@ -1235,6 +1270,42 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
+ def test_serialized_attribute_in_base_class
+ Topic.serialize("content", Hash)
+
+ hash = { 'content1' => 'value1', 'content2' => 'value2' }
+ important_topic = ImportantTopic.create("content" => hash)
+ assert_equal(hash, important_topic.content)
+
+ important_topic.reload
+ assert_equal(hash, important_topic.content)
+ end
+
+ # This test was added to fix GH #4004. Obviously the value returned
+ # is not really the value 'before type cast' so we should maybe think
+ # about changing that in the future.
+ def test_serialized_attribute_before_type_cast_returns_unserialized_value
+ klass = Class.new(ActiveRecord::Base)
+ klass.table_name = "topics"
+ klass.serialize :content, Hash
+
+ t = klass.new(:content => { :foo => :bar })
+ assert_equal({ :foo => :bar }, t.content_before_type_cast)
+ t.save!
+ t.reload
+ assert_equal({ :foo => :bar }, t.content_before_type_cast)
+ end
+
+ def test_serialized_attribute_declared_in_subclass
+ hash = { 'important1' => 'value1', 'important2' => 'value2' }
+ important_topic = ImportantTopic.create("important" => hash)
+ assert_equal(hash, important_topic.important)
+
+ important_topic.reload
+ assert_equal(hash, important_topic.important)
+ assert_equal(hash, important_topic.read_attribute(:important))
+ end
+
def test_serialized_time_attribute
myobj = Time.local(2008,1,1,1,0)
topic = Topic.create("content" => myobj).reload
@@ -1247,11 +1318,24 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
- def test_nil_serialized_attribute_with_class_constraint
+ def test_nil_serialized_attribute_without_class_constraint
topic = Topic.new
assert_nil topic.content
end
+ def test_nil_not_serialized_without_class_constraint
+ assert Topic.new(:content => nil).save
+ assert_equal 1, Topic.where(:content => nil).count
+ end
+
+ def test_nil_not_serialized_with_class_constraint
+ Topic.serialize :content, Hash
+ assert Topic.new(:content => nil).save
+ assert_equal 1, Topic.where(:content => nil).count
+ ensure
+ Topic.serialize(:content)
+ end
+
def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
myobj = MyObject.new('value1', 'value2')
topic = Topic.new(:content => myobj)
@@ -1359,22 +1443,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal author_name, Topic.find(topic.id).author_name
end
- if RUBY_VERSION < '1.9'
- def test_quote_chars
- with_kcode('UTF8') do
- str = 'The Narrator'
- topic = Topic.create(:author_name => str)
- assert_equal str, topic.author_name
-
- assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
- topic = Topic.find_by_author_name(str.mb_chars)
-
- assert_kind_of Topic, topic
- assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
- end
- end
- end
-
def test_toggle_attribute
assert !topics(:first).approved?
topics(:first).toggle!(:approved)
@@ -1401,86 +1469,48 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal dev, dev.reload
end
- def test_define_attr_method_with_value
- k = Class.new( ActiveRecord::Base )
- k.send(:define_attr_method, :table_name, "foo")
- assert_equal "foo", k.table_name
- end
-
- def test_define_attr_method_with_block
- k = Class.new( ActiveRecord::Base ) do
- class << self
- attr_accessor :foo_key
- end
- end
- k.foo_key = "id"
- k.send(:define_attr_method, :foo_key) { "sys_" + original_foo_key }
- assert_equal "sys_id", k.foo_key
- end
-
- def test_set_table_name_with_value
- k = Class.new( ActiveRecord::Base )
- k.table_name = "foo"
- assert_equal "foo", k.table_name
- k.set_table_name "bar"
- assert_equal "bar", k.table_name
- end
-
def test_switching_between_table_name
assert_difference("GoodJoke.count") do
- Joke.set_table_name "cold_jokes"
+ Joke.table_name = "cold_jokes"
Joke.create
- Joke.set_table_name "funny_jokes"
+ Joke.table_name = "funny_jokes"
Joke.create
end
end
+ def test_set_table_name_symbol_converted_to_string
+ Joke.table_name = :cold_jokes
+ assert_equal 'cold_jokes', Joke.table_name
+ end
+
def test_quoted_table_name_after_set_table_name
klass = Class.new(ActiveRecord::Base)
- klass.set_table_name "foo"
+ klass.table_name = "foo"
assert_equal "foo", klass.table_name
assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
- klass.set_table_name "bar"
+ klass.table_name = "bar"
assert_equal "bar", klass.table_name
assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
end
- def test_set_table_name_with_block
+ def test_set_table_name_with_inheritance
k = Class.new( ActiveRecord::Base )
- k.set_table_name { "ks" }
- assert_equal "ks", k.table_name
+ def k.name; "Foo"; end
+ def k.table_name; super + "ks"; end
+ assert_equal "foosks", k.table_name
end
- def test_set_primary_key_with_value
- k = Class.new( ActiveRecord::Base )
- k.primary_key = "foo"
- assert_equal "foo", k.primary_key
- k.set_primary_key "bar"
- assert_equal "bar", k.primary_key
- end
-
- def test_set_primary_key_with_block
- k = Class.new( ActiveRecord::Base )
- k.primary_key = 'id'
- k.set_primary_key { "sys_" + original_primary_key }
- assert_equal "sys_id", k.primary_key
- end
-
- def test_set_inheritance_column_with_value
- k = Class.new( ActiveRecord::Base )
- k.inheritance_column = "foo"
- assert_equal "foo", k.inheritance_column
- k.set_inheritance_column "bar"
- assert_equal "bar", k.inheritance_column
- end
-
- def test_set_inheritance_column_with_block
- k = Class.new( ActiveRecord::Base )
- k.set_inheritance_column { original_inheritance_column + "_id" }
- assert_equal "type_id", k.inheritance_column
+ def test_sequence_name_with_abstract_class
+ ak = Class.new(ActiveRecord::Base)
+ ak.abstract_class = true
+ k = Class.new(ak)
+ k.table_name = "projects"
+ orig_name = k.sequence_name
+ return skip "sequences not supported by db" unless orig_name
+ assert_equal k.reset_sequence_name, orig_name
end
def test_count_with_join
@@ -1582,7 +1612,7 @@ class BasicsTest < ActiveRecord::TestCase
Developer.find(:all)
end
assert developers.size >= 2
- for i in 1...developers.size
+ (1...developers.size).each do |i|
assert developers[i-1].salary >= developers[i].salary
end
end
@@ -1673,10 +1703,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_descends_from_active_record
- # Tries to call Object.abstract_class?
- assert_raise(NoMethodError) do
- ActiveRecord::Base.descends_from_active_record?
- end
+ assert !ActiveRecord::Base.descends_from_active_record?
# Abstract subclass of AR::Base.
assert LoosePerson.descends_from_active_record?
@@ -1699,6 +1726,10 @@ class BasicsTest < ActiveRecord::TestCase
# Concrete subclasses an abstract class which has a type column.
assert !SubStiPost.descends_from_active_record?
+
+ assert Teapot.descends_from_active_record?
+ assert !OtherTeapot.descends_from_active_record?
+ assert CoolTeapot.descends_from_active_record?
end
def test_find_on_abstract_base_class_doesnt_use_type_condition
@@ -1732,7 +1763,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_inspect_instance
topic = topics(:first)
- assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
+ assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", important: nil, approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
end
def test_inspect_new_instance
@@ -1761,10 +1792,18 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "The First Topic", topics(:first).becomes(Reply).title
end
+ def test_becomes_includes_errors
+ company = Company.new(:name => nil)
+ assert !company.valid?
+ original_errors = company.errors
+ client = company.becomes(Client)
+ assert_equal original_errors, client.errors
+ end
+
def test_silence_sets_log_level_to_error_in_block
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::DEBUG
ActiveRecord::Base.silence do
ActiveRecord::Base.logger.warn "warn"
@@ -1778,7 +1817,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_silence_sets_log_level_back_to_level_before_yield
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::WARN
ActiveRecord::Base.silence do
end
@@ -1790,7 +1829,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_benchmark_with_log_level
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::WARN
ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
@@ -1805,10 +1844,8 @@ class BasicsTest < ActiveRecord::TestCase
def test_benchmark_with_use_silence
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
- ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
- assert_no_match(/Loud/, log.string)
assert_match(/Quiet/, log.string)
ensure
ActiveRecord::Base.logger = original_logger
@@ -1840,9 +1877,9 @@ class BasicsTest < ActiveRecord::TestCase
def test_clear_cache!
# preheat cache
- c1 = Post.columns
+ c1 = Post.connection.schema_cache.columns['posts']
ActiveRecord::Base.clear_cache!
- c2 = Post.columns
+ c2 = Post.connection.schema_cache.columns['posts']
assert_not_equal c1, c2
end
@@ -1859,11 +1896,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshal_round_trip
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
expected = posts(:welcome)
marshalled = Marshal.dump(expected)
actual = Marshal.load(marshalled)
@@ -1872,11 +1904,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshal_new_record_round_trip
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
marshalled = Marshal.dump(Post.new)
post = Marshal.load(marshalled)
@@ -1884,11 +1911,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshalling_with_associations
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
post = Post.new
post.comments.build
@@ -1907,7 +1929,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal [], NonExistentTable.attribute_names
end
- def test_attribtue_names_on_abstract_class
+ def test_attribute_names_on_abstract_class
assert_equal [], AbstractCompany.attribute_names
end
@@ -1935,4 +1957,47 @@ class BasicsTest < ActiveRecord::TestCase
dev.update_attribute(:updated_at, nil)
assert_match(/\/#{dev.id}$/, dev.cache_key)
end
+
+ def test_uniq_delegates_to_scoped
+ scope = stub
+ Bird.stubs(:scoped).returns(mock(:uniq => scope))
+ assert_equal scope, Bird.uniq
+ end
+
+ def test_active_record_super
+ assert_equal ActiveRecord::Model, ActiveRecord::Base.active_record_super
+ assert_equal ActiveRecord::Base, Topic.active_record_super
+ assert_equal Topic, ImportantTopic.active_record_super
+ assert_equal ActiveRecord::Model, Teapot.active_record_super
+ assert_equal Teapot, OtherTeapot.active_record_super
+ assert_equal ActiveRecord::Model, CoolTeapot.active_record_super
+ end
+
+ def test_table_name_with_2_abstract_subclasses
+ assert_equal "photos", Photo.table_name
+ end
+
+ def test_column_types_typecast
+ topic = Topic.first
+ refute_equal 't.lo', topic.author_name
+
+ attrs = topic.attributes.dup
+ attrs.delete 'id'
+
+ typecast = Class.new {
+ def type_cast value
+ "t.lo"
+ end
+ }
+
+ types = { 'author_name' => typecast.new }
+ topic = Topic.allocate.init_with 'attributes' => attrs,
+ 'column_types' => types
+
+ assert_equal 't.lo', topic.author_name
+ end
+
+ def test_typecasting_aliases
+ assert_equal 10, Topic.select('10 as tenderlove').first.tenderlove
+ end
end
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index 06c14cb108..25d2896ab0 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -8,11 +8,11 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
require 'models/binary'
class BinaryTest < ActiveRecord::TestCase
- FIXTURES = %w(flowers.jpg example.log)
+ FIXTURES = %w(flowers.jpg example.log test.txt)
def test_mixed_encoding
str = "\x80"
- str.force_encoding('ASCII-8BIT') if str.respond_to?(:force_encoding)
+ str.force_encoding('ASCII-8BIT')
binary = Binary.new :name => 'いただきます!', :data => str
binary.save!
@@ -23,7 +23,7 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
# Mysql adapter doesn't properly encode things, so we have to do it
if current_adapter?(:MysqlAdapter)
- name.force_encoding('UTF-8') if name.respond_to?(:force_encoding)
+ name.force_encoding('UTF-8')
end
assert_equal 'いただきます!', name
end
@@ -33,7 +33,7 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
FIXTURES.each do |filename|
data = File.read(ASSETS_ROOT + "/#{filename}")
- data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+ data.force_encoding('ASCII-8BIT')
data.freeze
bin = Binary.new(:data => data)
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index c38814713a..0391319a00 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'models/company'
+require "models/contract"
require 'models/topic'
require 'models/edge'
require 'models/club'
@@ -48,11 +49,11 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_get_maximum_of_field_with_include
- assert_equal 55, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
+ assert_equal 55, Account.maximum(:credit_limit, :include => :firm, :references => :companies, :conditions => "companies.name != 'Summit'")
end
def test_should_get_maximum_of_field_with_scoped_include
- Account.send :with_scope, :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
+ Account.send :with_scope, :find => { :include => :firm, :references => :companies, :conditions => "companies.name != 'Summit'" } do
assert_equal 55, Account.maximum(:credit_limit)
end
end
@@ -269,7 +270,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_not_modify_options_when_using_includes
- options = {:conditions => 'companies.id > 1', :include => :firm}
+ options = {:conditions => 'companies.id > 1', :include => :firm, :references => :companies}
options_copy = options.dup
Account.count(:all, options)
@@ -446,4 +447,45 @@ class CalculationsTest < ActiveRecord::TestCase
distinct_authors_for_approved_count = Topic.group(:approved).count(:author_name, :distinct => true)[true]
assert_equal distinct_authors_for_approved_count, 2
end
+
+ def test_pluck
+ assert_equal [1,2,3,4], Topic.order(:id).pluck(:id)
+ end
+
+ def test_pluck_type_cast
+ topic = topics(:first)
+ relation = Topic.where(:id => topic.id)
+ assert_equal [ topic.approved ], relation.pluck(:approved)
+ assert_equal [ topic.last_read ], relation.pluck(:last_read)
+ assert_equal [ topic.written_on ], relation.pluck(:written_on)
+ end
+
+ def test_pluck_and_uniq
+ assert_equal [50, 53, 55, 60], Account.order(:credit_limit).uniq.pluck(:credit_limit)
+ end
+
+ def test_pluck_in_relation
+ company = Company.first
+ contract = company.contracts.create!
+ assert_equal [contract.id], company.contracts.pluck(:id)
+ end
+
+ def test_pluck_with_serialization
+ t = Topic.create!(:content => { :foo => :bar })
+ assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
+ end
+
+ def test_pluck_with_qualified_column_name
+ assert_equal [1,2,3,4], Topic.order(:id).pluck("topics.id")
+ end
+
+ def test_pluck_auto_table_name_prefix
+ c = Company.create!(:name => "test", :contracts => [Contract.new])
+ assert_equal [c.id], Company.joins(:contracts).pluck(:id)
+ end
+
+ def test_pluck_not_auto_table_name_prefix_if_column_joined
+ Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ assert_equal [7], Company.joins(:contracts).pluck(:developer_id)
+ end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 7f4d25790b..7690769226 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
class CallbackDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
class << self
def callback_string(callback_method)
@@ -48,7 +48,7 @@ class CallbackDeveloperWithFalseValidation < CallbackDeveloper
end
class ParentDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
attr_accessor :after_save_called
before_validation {|record| record.after_save_called = true}
end
@@ -58,7 +58,7 @@ class ChildDeveloper < ParentDeveloper
end
class RecursiveCallbackDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
before_save :on_before_save
after_save :on_after_save
@@ -79,7 +79,7 @@ class RecursiveCallbackDeveloper < ActiveRecord::Base
end
class ImmutableDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
validates_inclusion_of :salary, :in => 50000..200000
@@ -98,7 +98,7 @@ class ImmutableDeveloper < ActiveRecord::Base
end
class ImmutableMethodDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
validates_inclusion_of :salary, :in => 50000..200000
@@ -118,7 +118,7 @@ class ImmutableMethodDeveloper < ActiveRecord::Base
end
class OnCallbacksDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
before_validation { history << :before_validation }
before_validation(:on => :create){ history << :before_validation_on_create }
@@ -138,7 +138,7 @@ class OnCallbacksDeveloper < ActiveRecord::Base
end
class CallbackCancellationDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb
index 14884e42af..a44b49466f 100644
--- a/activerecord/test/cases/column_definition_test.rb
+++ b/activerecord/test/cases/column_definition_test.rb
@@ -126,17 +126,20 @@ module ActiveRecord
if current_adapter?(:PostgreSQLAdapter)
def test_bigint_column_should_map_to_integer
- bigint_column = PostgreSQLColumn.new('number', nil, "bigint")
+ oid = PostgreSQLAdapter::OID::Identity.new
+ bigint_column = PostgreSQLColumn.new('number', nil, oid, "bigint")
assert_equal :integer, bigint_column.type
end
def test_smallint_column_should_map_to_integer
- smallint_column = PostgreSQLColumn.new('number', nil, "smallint")
+ oid = PostgreSQLAdapter::OID::Identity.new
+ smallint_column = PostgreSQLColumn.new('number', nil, oid, "smallint")
assert_equal :integer, smallint_column.type
end
def test_uuid_column_should_map_to_string
- uuid_column = PostgreSQLColumn.new('unique_id', nil, "uuid")
+ oid = PostgreSQLAdapter::OID::Identity.new
+ uuid_column = PostgreSQLColumn.new('unique_id', nil, oid, "uuid")
assert_equal :string, uuid_column.type
end
end
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
new file mode 100644
index 0000000000..ccc57cb876
--- /dev/null
+++ b/activerecord/test/cases/column_test.rb
@@ -0,0 +1,29 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ColumnTest < ActiveRecord::TestCase
+ def test_type_cast_boolean
+ column = Column.new("field", nil, "boolean")
+ assert column.type_cast(true)
+ assert column.type_cast(1)
+ assert column.type_cast('1')
+ assert column.type_cast('t')
+ assert column.type_cast('T')
+ assert column.type_cast('true')
+ assert column.type_cast('TRUE')
+ assert column.type_cast('on')
+ assert column.type_cast('ON')
+ assert !column.type_cast(false)
+ assert !column.type_cast(0)
+ assert !column.type_cast('0')
+ assert !column.type_cast('f')
+ assert !column.type_cast('F')
+ assert !column.type_cast('false')
+ assert !column.type_cast('FALSE')
+ assert !column.type_cast('off')
+ assert !column.type_cast('OFF')
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb
new file mode 100644
index 0000000000..7dc6e8afcb
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb
@@ -0,0 +1,54 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class AbstractAdapterTest < ActiveRecord::TestCase
+ attr_reader :adapter
+
+ def setup
+ @adapter = AbstractAdapter.new nil, nil
+ end
+
+ def test_in_use?
+ # FIXME: change to refute in Rails 4.0 / mt
+ assert !adapter.in_use?, 'adapter is not in use'
+ assert adapter.lease, 'lease adapter'
+ assert adapter.in_use?, 'adapter is in use'
+ end
+
+ def test_lease_twice
+ assert adapter.lease, 'should lease adapter'
+ assert !adapter.lease, 'should not lease adapter'
+ end
+
+ def test_last_use
+ assert !adapter.last_use
+ adapter.lease
+ assert adapter.last_use
+ end
+
+ def test_expire_mutates_in_use
+ assert adapter.lease, 'lease adapter'
+ assert adapter.in_use?, 'adapter is in use'
+ adapter.expire
+ assert !adapter.in_use?, 'adapter is in use'
+ end
+
+ def test_close
+ pool = ConnectionPool.new(ConnectionSpecification.new({}, nil))
+ pool.connections << adapter
+ adapter.pool = pool
+
+ # Make sure the pool marks the connection in use
+ assert_equal adapter, pool.connection
+ assert adapter.in_use?
+
+ # Close should put the adapter back in the pool
+ adapter.close
+ assert !adapter.in_use?
+
+ assert_equal adapter, pool.connection
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index bd0d161838..dc99ac665c 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -8,6 +8,9 @@ module ActiveRecord
@handler.establish_connection 'america', Base.connection_pool.spec
@klass = Class.new do
def self.name; 'america'; end
+ class << self
+ alias active_record_super superclass
+ end
end
@subklass = Class.new(@klass) do
def self.name; 'north america'; end
@@ -40,8 +43,6 @@ module ActiveRecord
def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove
@handler.establish_connection 'north america', Base.connection_pool.spec
- assert_not_same @handler.retrieve_connection_pool(@klass),
- @handler.retrieve_connection_pool(@subklass)
@handler.remove_connection @subklass
assert_same @handler.retrieve_connection_pool(@klass),
diff --git a/activerecord/test/cases/connection_adapters/connection_specification_test.rb b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
new file mode 100644
index 0000000000..ea2196cda2
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
@@ -0,0 +1,12 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionSpecificationTest < ActiveRecord::TestCase
+ def test_dup_deep_copy_config
+ spec = ConnectionSpecification.new({ :a => :b }, "bar")
+ assert_not_equal(spec.config.object_id, spec.dup.config.object_id)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
new file mode 100644
index 0000000000..42e39d534c
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -0,0 +1,44 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SchemaCacheTest < ActiveRecord::TestCase
+ def setup
+ connection = ActiveRecord::Base.connection
+ @cache = SchemaCache.new connection
+ end
+
+ def test_primary_key
+ assert_equal 'id', @cache.primary_keys['posts']
+ end
+
+ def test_primary_key_for_non_existent_table
+ assert_nil @cache.primary_keys['omgponies']
+ end
+
+ def test_caches_columns
+ columns = @cache.columns['posts']
+ assert_equal columns, @cache.columns['posts']
+ end
+
+ def test_caches_columns_hash
+ columns_hash = @cache.columns_hash['posts']
+ assert_equal columns_hash, @cache.columns_hash['posts']
+ end
+
+ def test_clearing
+ @cache.columns['posts']
+ @cache.columns_hash['posts']
+ @cache.tables['posts']
+ @cache.primary_keys['posts']
+
+ @cache.clear!
+
+ assert_equal 0, @cache.columns.size
+ assert_equal 0, @cache.columns_hash.size
+ assert_equal 0, @cache.tables.size
+ assert_equal 0, @cache.primary_keys.size
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index f554ceef35..fe1b40d884 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "rack"
module ActiveRecord
module ConnectionAdapters
@@ -25,38 +26,25 @@ module ActiveRecord
assert ActiveRecord::Base.connection_handler.active_connections?
end
- class FakeBase < ActiveRecord::Base
- def self.establish_connection spec
- String === spec ? super : spec
- end
- end
+ def test_connection_pool_per_pid
+ return skip('must support fork') unless Process.respond_to?(:fork)
- def test_url_host_no_db
- spec = FakeBase.establish_connection 'postgres://foo?encoding=utf8'
- assert_equal({
- :adapter => "postgresql",
- :database => "",
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
+ object_id = ActiveRecord::Base.connection.object_id
- def test_url_host_db
- spec = FakeBase.establish_connection 'postgres://foo/bar?encoding=utf8'
- assert_equal({
- :adapter => "postgresql",
- :database => "bar",
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
+ rd, wr = IO.pipe
+
+ pid = fork {
+ rd.close
+ wr.write Marshal.dump ActiveRecord::Base.connection.object_id
+ wr.close
+ exit!
+ }
+
+ wr.close
- def test_url_port
- spec = FakeBase.establish_connection 'postgres://foo:123?encoding=utf8'
- assert_equal({
- :adapter => "postgresql",
- :database => "",
- :port => 123,
- :host => "foo",
- :encoding => "utf8" }, spec)
+ Process.waitpid pid
+ assert_not_equal object_id, Marshal.load(rd.read)
+ rd.close
end
def test_app_delegation
@@ -114,9 +102,10 @@ module ActiveRecord
test "proxy is polite to it's body and responds to it" do
body = Class.new(String) { def to_path; "/path"; end }.new
- proxy = ConnectionManagement::Proxy.new(body)
- assert proxy.respond_to?(:to_path)
- assert_equal proxy.to_path, "/path"
+ app = lambda { |_| [200, {}, body] }
+ response_body = ConnectionManagement.new(app).call(@env)[2]
+ assert response_body.respond_to?(:to_path)
+ assert_equal response_body.to_path, "/path"
end
end
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 8a0f453127..2c69bfde5b 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -4,6 +4,8 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionPoolTest < ActiveRecord::TestCase
def setup
+ super
+
# Keep a duplicate pool so we do not bother others
@pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
@@ -18,73 +20,76 @@ module ActiveRecord
end
end
- def test_active_connection?
- assert !@pool.active_connection?
- assert @pool.connection
- assert @pool.active_connection?
- @pool.release_connection
- assert !@pool.active_connection?
+ def teardown
+ super
+ @pool.disconnect!
end
- def test_pool_caches_columns
- columns = @pool.columns['posts']
- assert_equal columns, @pool.columns['posts']
+ def test_full_pool_exception
+ assert_raises(PoolFullError) do
+ (@pool.size + 1).times do
+ @pool.checkout
+ end
+ end
end
- def test_pool_caches_columns_hash
- columns_hash = @pool.columns_hash['posts']
- assert_equal columns_hash, @pool.columns_hash['posts']
- end
+ def test_reap_and_active
+ @pool.checkout
+ @pool.checkout
+ @pool.checkout
+ @pool.timeout = 0
- def test_clearing_column_cache
- @pool.columns['posts']
- @pool.columns_hash['posts']
+ connections = @pool.connections.dup
- @pool.clear_cache!
+ @pool.reap
- assert_equal 0, @pool.columns.size
- assert_equal 0, @pool.columns_hash.size
+ assert_equal connections.length, @pool.connections.length
end
- def test_primary_key
- assert_equal 'id', @pool.primary_keys['posts']
- end
+ def test_reap_inactive
+ @pool.checkout
+ @pool.checkout
+ @pool.checkout
+ @pool.timeout = 0
- def test_primary_key_for_non_existent_table
- assert_equal 'id', @pool.primary_keys['omgponies']
- end
+ connections = @pool.connections.dup
+ connections.each do |conn|
+ conn.extend(Module.new { def active?; false; end; })
+ end
- def test_primary_key_is_set_on_columns
- posts_columns = @pool.columns_hash['posts']
- assert posts_columns['id'].primary
+ @pool.reap
- (posts_columns.keys - ['id']).each do |key|
- assert !posts_columns[key].primary
- end
+ assert_equal 0, @pool.connections.length
+ ensure
+ connections.each(&:close)
end
- def test_clear_stale_cached_connections!
- pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
-
- threads = [
- Thread.new { pool.connection },
- Thread.new { pool.connection }]
+ def test_remove_connection
+ conn = @pool.checkout
+ assert conn.in_use?
- threads.map { |t| t.join }
+ length = @pool.connections.length
+ @pool.remove conn
+ assert conn.in_use?
+ assert_equal(length - 1, @pool.connections.length)
+ ensure
+ conn.close
+ end
- pool.extend Module.new {
- attr_accessor :checkins
- def checkin conn
- @checkins << conn
- conn.object_id
- end
- }
- pool.checkins = []
+ def test_remove_connection_for_thread
+ conn = @pool.connection
+ @pool.remove conn
+ assert_not_equal(conn, @pool.connection)
+ ensure
+ conn.close if conn
+ end
- cleared_threads = pool.clear_stale_cached_connections!
- assert((cleared_threads - threads.map { |x| x.object_id }).empty?,
- "threads should have been removed")
- assert_equal pool.checkins.length, threads.length
+ def test_active_connection?
+ assert !@pool.active_connection?
+ assert @pool.connection
+ assert @pool.active_connection?
+ @pool.release_connection
+ assert !@pool.active_connection?
end
def test_checkout_behaviour
@@ -96,24 +101,16 @@ module ActiveRecord
threads << Thread.new(i) do |pool_count|
connection = pool.connection
assert_not_nil connection
+ connection.close
end
end
- threads.each {|t| t.join}
+ threads.each(&:join)
Thread.new do
- threads.each do |t|
- thread_ids = pool.instance_variable_get(:@reserved_connections).keys
- assert thread_ids.include?(t.object_id)
- end
-
- pool.connection
- threads.each do |t|
- thread_ids = pool.instance_variable_get(:@reserved_connections).keys
- assert !thread_ids.include?(t.object_id)
- end
- end.join()
-
+ assert pool.connection
+ pool.connection.close
+ end.join
end
def test_automatic_reconnect=
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
new file mode 100644
index 0000000000..e6cb1b9521
--- /dev/null
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -0,0 +1,41 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionSpecification
+ class ResolverTest < ActiveRecord::TestCase
+ def resolve(spec)
+ Resolver.new(spec, {}).spec.config
+ end
+
+ def test_url_host_no_db
+ spec = resolve 'mysql://foo?encoding=utf8'
+ assert_equal({
+ :adapter => "mysql",
+ :database => "",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_host_db
+ spec = resolve 'mysql://foo/bar?encoding=utf8'
+ assert_equal({
+ :adapter => "mysql",
+ :database => "bar",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_port
+ spec = resolve 'mysql://foo:123?encoding=utf8'
+ assert_equal({
+ :adapter => "mysql",
+ :database => "",
+ :port => 123,
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index 3ed96a3ec8..cd3d19e783 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -6,9 +6,11 @@ require 'models/engine'
require 'models/reply'
require 'models/category'
require 'models/categorization'
+require 'models/dog'
+require 'models/dog_lover'
class CounterCacheTest < ActiveRecord::TestCase
- fixtures :topics, :categories, :categorizations, :cars
+ fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers
class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -61,7 +63,7 @@ class CounterCacheTest < ActiveRecord::TestCase
end
end
- test "reset counter should with belongs_to which has class_name" do
+ test "reset counter with belongs_to which has class_name" do
car = cars(:honda)
assert_nothing_raised do
Car.reset_counters(car.id, :engines)
@@ -71,6 +73,20 @@ class CounterCacheTest < ActiveRecord::TestCase
end
end
+ test "reset the right counter if two have the same class_name" do
+ david = dog_lovers(:david)
+
+ DogLover.increment_counter(:bred_dogs_count, david.id)
+ DogLover.increment_counter(:trained_dogs_count, david.id)
+
+ assert_difference 'david.reload.bred_dogs_count', -1 do
+ DogLover.reset_counters(david.id, :bred_dogs)
+ end
+ assert_difference 'david.reload.trained_dogs_count', -1 do
+ DogLover.reset_counters(david.id, :trained_dogs)
+ end
+ end
+
test "update counter with initial null value" do
category = categories(:general)
assert_equal 2, category.categorizations.count
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index b1ce846218..54e0b40b4f 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -497,6 +497,20 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.previous_changes.key?('created_on')
end
+ if ActiveRecord::Base.connection.supports_migrations?
+ class Testings < ActiveRecord::Base; end
+ def test_field_named_field
+ ActiveRecord::Base.connection.create_table :testings do |t|
+ t.string :field
+ end
+ assert_nothing_raised do
+ Testings.new.attributes
+ end
+ ensure
+ ActiveRecord::Base.connection.drop_table :testings rescue nil
+ end
+ end
+
private
def with_partial_updates(klass, on = true)
old = klass.partial_updates?
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 0236f9b0a1..9705a11387 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -99,5 +99,13 @@ module ActiveRecord
assert_not_nil new_topic.created_at
end
+ def test_dup_after_initialize_callbacks
+ topic = Topic.new
+ assert Topic.after_initialize_called
+ Topic.after_initialize_called = false
+ topic.dup
+ assert Topic.after_initialize_called
+ end
+
end
end
diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
new file mode 100644
index 0000000000..e118add44c
--- /dev/null
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -0,0 +1,48 @@
+require 'cases/helper'
+
+if ActiveRecord::Base.connection.supports_explain?
+ class ExplainSubscriberTest < ActiveRecord::TestCase
+ SUBSCRIBER = ActiveRecord::ExplainSubscriber.new
+
+ def test_collects_nothing_if_available_queries_for_explain_is_nil
+ with_queries(nil) do
+ SUBSCRIBER.call
+ assert_nil Thread.current[:available_queries_for_explain]
+ end
+ end
+
+ def test_collects_nothing_if_the_payload_has_an_exception
+ with_queries([]) do |queries|
+ SUBSCRIBER.call(:exception => Exception.new)
+ assert queries.empty?
+ end
+ end
+
+ def test_collects_nothing_for_ignored_payloads
+ with_queries([]) do |queries|
+ ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS.each do |ip|
+ SUBSCRIBER.call(:name => ip)
+ end
+ assert queries.empty?
+ end
+ end
+
+ def test_collects_pairs_of_queries_and_binds
+ sql = 'select 1 from users'
+ binds = [1, 2]
+ with_queries([]) do |queries|
+ SUBSCRIBER.call(:name => 'SQL', :sql => sql, :binds => binds)
+ assert_equal 1, queries.size
+ assert_equal sql, queries[0][0]
+ assert_equal binds, queries[0][1]
+ end
+ end
+
+ def with_queries(queries)
+ Thread.current[:available_queries_for_explain] = queries
+ yield queries
+ ensure
+ Thread.current[:available_queries_for_explain] = nil
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
new file mode 100644
index 0000000000..83c9b6e107
--- /dev/null
+++ b/activerecord/test/cases/explain_test.rb
@@ -0,0 +1,115 @@
+require 'cases/helper'
+require 'models/car'
+require 'active_support/core_ext/string/strip'
+
+if ActiveRecord::Base.connection.supports_explain?
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :cars
+
+ def base
+ ActiveRecord::Base
+ end
+
+ def connection
+ base.connection
+ end
+
+ def test_logging_query_plan_with_logger
+ base.logger.expects(:warn).with do |message|
+ message.starts_with?('EXPLAIN for:')
+ end
+
+ with_threshold(0) do
+ Car.where(:name => 'honda').all
+ end
+ end
+
+ def test_logging_query_plan_without_logger
+ original = base.logger
+ base.logger = nil
+
+ base.logger.expects(:warn).never
+
+ with_threshold(0) do
+ car = Car.where(:name => 'honda').first
+ assert_equal 'honda', car.name
+ end
+ ensure
+ base.logger = original
+ end
+
+ def test_collect_queries_for_explain
+ base.auto_explain_threshold_in_seconds = nil
+ queries = Thread.current[:available_queries_for_explain] = []
+
+ with_threshold(0) do
+ Car.where(:name => 'honda').all
+ end
+
+ sql, binds = queries[0]
+ assert_match "SELECT", sql
+ assert_match "honda", sql
+ assert_equal [], binds
+ ensure
+ Thread.current[:available_queries_for_explain] = nil
+ end
+
+ def test_collecting_queries_for_explain
+ result, queries = ActiveRecord::Base.collecting_queries_for_explain do
+ Car.where(:name => 'honda').all
+ end
+
+ sql, binds = queries[0]
+ assert_match "SELECT", sql
+ assert_match "honda", sql
+ assert_equal [], binds
+ assert_equal [cars(:honda)], result
+ end
+
+ def test_exec_explain_with_no_binds
+ sqls = %w(foo bar)
+ binds = [[], []]
+ queries = sqls.zip(binds)
+
+ connection.stubs(:explain).returns('query plan foo', 'query plan bar')
+ expected = sqls.map {|sql| "EXPLAIN for: #{sql}\nquery plan #{sql}"}.join("\n")
+ assert_equal expected, base.exec_explain(queries)
+ end
+
+ def test_exec_explain_with_binds
+ cols = [Object.new, Object.new]
+ cols[0].expects(:name).returns('wadus')
+ cols[1].expects(:name).returns('chaflan')
+
+ sqls = %w(foo bar)
+ binds = [[[cols[0], 1]], [[cols[1], 2]]]
+ queries = sqls.zip(binds)
+
+ connection.stubs(:explain).returns("query plan foo\n", "query plan bar\n")
+ expected = <<-SQL.strip_heredoc
+ EXPLAIN for: #{sqls[0]} [["wadus", 1]]
+ query plan foo
+
+ EXPLAIN for: #{sqls[1]} [["chaflan", 2]]
+ query plan bar
+ SQL
+ assert_equal expected, base.exec_explain(queries)
+ end
+
+ def test_silence_auto_explain
+ base.expects(:collecting_sqls_for_explain).never
+ base.logger.expects(:warn).never
+ base.silence_auto_explain do
+ with_threshold(0) { Car.all }
+ end
+ end
+
+ def with_threshold(threshold)
+ current_threshold = base.auto_explain_threshold_in_seconds
+ base.auto_explain_threshold_in_seconds = threshold
+ yield
+ ensure
+ base.auto_explain_threshold_in_seconds = current_threshold
+ end
+ end
+end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 3088ab012f..76c041397a 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -57,6 +57,11 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.first.replies.exists?
end
+ # ensures +exists?+ runs valid SQL by excluding order value
+ def test_exists_with_order
+ assert Topic.order(:id).uniq.exists?
+ end
+
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
@@ -369,6 +374,10 @@ class FinderTest < ActiveRecord::TestCase
assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
end
+ def test_find_on_hash_conditions_with_array_of_integers_and_ranges
+ assert_equal [1,2,3,5,6,7,8,9], Comment.find(:all, :conditions => {:id => [1..2, 3, 5, 6..8, 9]}).map(&:id).sort
+ end
+
def test_find_on_multiple_hash_conditions
assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
@@ -872,6 +881,17 @@ class FinderTest < ActiveRecord::TestCase
assert_equal 23, sig38.client_of
end
+ def test_find_or_create_from_two_attributes_and_hash
+ number_of_companies = Company.count
+ sig38 = Company.find_or_create_by_name_and_firm_id({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert_equal number_of_companies + 1, Company.count
+ assert_equal sig38, Company.find_or_create_by_name_and_firm_id({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert sig38.persisted?
+ assert_equal "38signals", sig38.name
+ assert_equal 17, sig38.firm_id
+ assert_equal 23, sig38.client_of
+ end
+
def test_find_or_create_from_one_aggregate_attribute
number_of_customers = Customer.count
created_customer = Customer.find_or_create_by_balance(Money.new(123))
@@ -1110,10 +1130,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
- assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
+ assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => 'author_addresses.id DESC ', :limit => 2).size
assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
- :order => ' author_addresses_authors.id DESC ', :limit => 3).size
+ :order => 'author_addresses_authors.id DESC ', :limit => 3).size
end
def test_find_with_nil_inside_set_passed_for_one_attribute
@@ -1140,7 +1160,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_with_limiting_with_custom_select
- posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
+ posts = Post.find(
+ :all, :include => :author, :select => ' posts.*, authors.id as "author_id"',
+ :references => :authors, :limit => 3, :order => 'posts.id'
+ )
assert_equal 3, posts.size
assert_equal [0, 1, 1], posts.map(&:author_id).sort
end
@@ -1154,7 +1177,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_one_message_with_custom_primary_key
- Toy.set_primary_key :name
+ Toy.primary_key = :name
begin
Toy.find 'Hello World!'
rescue ActiveRecord::RecordNotFound => e
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 7e2dafcd01..562b370c97 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -1,31 +1,34 @@
-require "cases/helper"
-require 'models/post'
+require 'cases/helper'
+require 'models/admin'
+require 'models/admin/account'
+require 'models/admin/randomly_named_c1'
+require 'models/admin/user'
require 'models/binary'
-require 'models/topic'
+require 'models/book'
+require 'models/category'
+require 'models/company'
require 'models/computer'
+require 'models/course'
require 'models/developer'
-require 'models/company'
-require 'models/task'
-require 'models/reply'
require 'models/joke'
-require 'models/course'
-require 'models/category'
+require 'models/matey'
require 'models/parrot'
require 'models/pirate'
-require 'models/treasure'
-require 'models/traffic_light'
-require 'models/matey'
+require 'models/post'
+require 'models/randomly_named_c1'
+require 'models/reply'
require 'models/ship'
-require 'models/book'
-require 'models/admin'
-require 'models/admin/account'
-require 'models/admin/user'
+require 'models/task'
+require 'models/topic'
+require 'models/traffic_light'
+require 'models/treasure'
require 'tempfile'
class FixturesTest < ActiveRecord::TestCase
self.use_instantiated_fixtures = true
self.use_transactional_fixtures = false
+ # other_topics fixture should not be included here
fixtures :topics, :developers, :accounts, :tasks, :categories, :funny_jokes, :binaries, :traffic_lights
FIXTURES = %w( accounts binaries companies customers
@@ -62,8 +65,9 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_create_fixtures
- ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
- assert Parrot.find_by_name('Curious George'), 'George is in the database'
+ fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
+ assert Parrot.find_by_name('Curious George'), 'George is not in the database'
+ assert fixtures.detect { |f| f.name == 'parrots' }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}"
end
def test_multiple_clean_fixtures
@@ -73,6 +77,13 @@ class FixturesTest < ActiveRecord::TestCase
fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::Fixtures, fixtures) }
end
+ def test_create_symbol_fixtures
+ fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
+
+ assert Course.find_by_name('Collection'), 'course is not in the database'
+ assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
+ end
+
def test_attributes
topics = create_fixtures("topics").first
assert_equal("The First Topic", topics["first"]["title"])
@@ -93,7 +104,7 @@ class FixturesTest < ActiveRecord::TestCase
# Reset cache to make finds on the new table work
ActiveRecord::Fixtures.reset_cache
- ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
+ ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t|
t.column :title, :string
t.column :author_name, :string
t.column :author_email_address, :string
@@ -115,23 +126,36 @@ class FixturesTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_prefix = 'prefix_'
ActiveRecord::Base.table_name_suffix = '_suffix'
- topics = create_fixtures("topics")
-
- first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
- assert_equal("The First Topic", first_row["title"])
+ other_topic_klass = Class.new(ActiveRecord::Base) do
+ def self.name
+ "OtherTopic"
+ end
+ end
- second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
- assert_nil(second_row["author_email_address"])
+ topics = [create_fixtures("other_topics")].flatten.first
# This checks for a caching problem which causes a bug in the fixtures
# class-level configuration helper.
assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create"
+
+ first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'David'")
+ assert_not_nil first_row, "The prefix_other_topics_suffix table appears to be empty despite create_fixtures: the row with author_name = 'David' was not found"
+ assert_equal("The First Topic", first_row["title"])
+
+ second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'Mary'")
+ assert_nil(second_row["author_email_address"])
+
+ assert_equal :prefix_other_topics_suffix, topics.table_name.to_sym
+ # This assertion should preferably be the last in the list, because calling
+ # other_topic_klass.table_name sets a class-level instance variable
+ assert_equal :prefix_other_topics_suffix, other_topic_klass.table_name.to_sym
+
ensure
# Restore prefix/suffix to its previous values
ActiveRecord::Base.table_name_prefix = old_prefix
ActiveRecord::Base.table_name_suffix = old_suffix
- ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
+ ActiveRecord::Base.connection.drop_table :prefix_other_topics_suffix rescue nil
end
end
@@ -179,7 +203,7 @@ class FixturesTest < ActiveRecord::TestCase
#sanity check to make sure that this file never exists
assert Dir[nonexistent_fixture_path+"*"].empty?
- assert_raise(FixturesFileNotFound) do
+ assert_raise(Errno::ENOENT) do
ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', nonexistent_fixture_path)
end
end
@@ -213,7 +237,7 @@ class FixturesTest < ActiveRecord::TestCase
def test_binary_in_fixtures
data = File.open(ASSETS_ROOT + "/flowers.jpg", 'rb') { |f| f.read }
- data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+ data.force_encoding('ASCII-8BIT')
data.freeze
assert_equal data, @flowers.data
end
@@ -573,7 +597,7 @@ class FasterFixturesTest < ActiveRecord::TestCase
load_extra_fixture('posts')
assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
- self.class.setup_fixture_accessors('posts')
+ self.class.setup_fixture_accessors :posts
assert_equal 'Welcome to the weblog', posts(:welcome).title
end
end
@@ -731,3 +755,34 @@ class FixtureLoadingTest < ActiveRecord::TestCase
ActiveRecord::TestCase.try_to_load_dependency(:works_out_fine)
end
end
+
+class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
+ ActiveRecord::Fixtures.reset_cache
+
+ set_fixture_class :randomly_named_a9 =>
+ ClassNameThatDoesNotFollowCONVENTIONS,
+ :'admin/randomly_named_a9' =>
+ Admin::ClassNameThatDoesNotFollowCONVENTIONS,
+ 'admin/randomly_named_b0' =>
+ Admin::ClassNameThatDoesNotFollowCONVENTIONS
+
+ fixtures :randomly_named_a9, 'admin/randomly_named_a9',
+ :'admin/randomly_named_b0'
+
+ def test_named_accessor_for_randomly_named_fixture_and_class
+ assert_kind_of ClassNameThatDoesNotFollowCONVENTIONS,
+ randomly_named_a9(:first_instance)
+ end
+
+ def test_named_accessor_for_randomly_named_namespaced_fixture_and_class
+ assert_kind_of Admin::ClassNameThatDoesNotFollowCONVENTIONS,
+ admin_randomly_named_a9(:first_instance)
+ assert_kind_of Admin::ClassNameThatDoesNotFollowCONVENTIONS,
+ admin_randomly_named_b0(:second_instance)
+ end
+
+ def test_table_name_is_defined_in_the_model
+ assert_equal 'randomly_named_table', ActiveRecord::Fixtures::all_loaded_fixtures["admin/randomly_named_a9"].table_name
+ assert_equal 'randomly_named_table', Admin::ClassNameThatDoesNotFollowCONVENTIONS.table_name
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 6735bc521b..9f5f012073 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -2,12 +2,14 @@ require File.expand_path('../../../../load_paths', __FILE__)
require 'config'
-require 'test/unit'
+require 'minitest/autorun'
require 'stringio'
require 'mocha'
+require 'cases/test_case'
require 'active_record'
require 'active_support/dependencies'
+require 'active_support/logger'
require 'support/config'
require 'support/connection'
@@ -20,6 +22,9 @@ ActiveSupport::Deprecation.debug = true
# Enable Identity Map only when ENV['IM'] is set to "true"
ActiveRecord::IdentityMap.enabled = (ENV['IM'] == "true")
+# Avoid deprecation warning setting dependent_restrict_raises to false. The default is true
+ActiveRecord::Base.dependent_restrict_raises = false
+
# Connect to the database
ARTest.connect
@@ -56,36 +61,13 @@ ensure
ActiveRecord::Base.default_timezone = old_zone
end
-module ActiveRecord
- class SQLCounter
- cattr_accessor :ignored_sql
- self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
-
- # FIXME: this needs to be refactored so specific database can add their own
- # ignored SQL. This ignored SQL is for Oracle.
- ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
-
- cattr_accessor :log
- self.log = []
-
- def call(name, start, finish, message_id, values)
- sql = values[:sql]
-
- # FIXME: this seems bad. we should probably have a better way to indicate
- # the query was cached
- unless 'CACHE' == values[:name]
- self.class.log << sql unless self.class.ignored_sql.any? { |r| sql =~ r }
- end
- end
- end
-
- ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
-end
-
unless ENV['FIXTURE_DEBUG']
module ActiveRecord::TestFixtures::ClassMethods
def try_to_load_dependency_with_silence(*args)
- ActiveRecord::Base.logger.silence { try_to_load_dependency_without_silence(*args)}
+ old = ActiveRecord::Base.logger.level
+ ActiveRecord::Base.logger.level = ActiveSupport::Logger::ERROR
+ try_to_load_dependency_without_silence(*args)
+ ActiveRecord::Base.logger.level = old
end
alias_method_chain :try_to_load_dependency, :silence
diff --git a/activerecord/test/cases/identity_map/middleware_test.rb b/activerecord/test/cases/identity_map/middleware_test.rb
index 60dcad4586..5b32a1c6d2 100644
--- a/activerecord/test/cases/identity_map/middleware_test.rb
+++ b/activerecord/test/cases/identity_map/middleware_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "rack"
module ActiveRecord
module IdentityMap
@@ -19,6 +20,7 @@ module ActiveRecord
called = false
mw = Middleware.new lambda { |env|
called = true
+ [200, {}, nil]
}
mw.call({})
assert called, 'middleware delegated'
@@ -27,6 +29,7 @@ module ActiveRecord
def test_im_enabled_during_delegation
mw = Middleware.new lambda { |env|
assert IdentityMap.enabled?, 'identity map should be enabled'
+ [200, {}, nil]
}
mw.call({})
end
diff --git a/activerecord/test/cases/inclusion_test.rb b/activerecord/test/cases/inclusion_test.rb
new file mode 100644
index 0000000000..8726ba5e51
--- /dev/null
+++ b/activerecord/test/cases/inclusion_test.rb
@@ -0,0 +1,113 @@
+require 'cases/helper'
+require 'models/teapot'
+
+class BasicInclusionModelTest < ActiveRecord::TestCase
+ def test_basic_model
+ Teapot.create!(:name => "Ronnie Kemper")
+ assert_equal "Ronnie Kemper", Teapot.find(1).name
+ end
+
+ def test_initialization
+ t = Teapot.new(:name => "Bob")
+ assert_equal "Bob", t.name
+ end
+
+ def test_inherited_model
+ teapot = CoolTeapot.create!(:name => "Bob")
+ teapot.reload
+
+ assert_equal "Bob", teapot.name
+ assert_equal "mmm", teapot.aaahhh
+ end
+
+ def test_generated_feature_methods
+ assert Teapot < Teapot::GeneratedFeatureMethods
+ end
+
+ def test_exists
+ t = Teapot.create!(:name => "Ronnie Kemper")
+ assert Teapot.exists?(t)
+ end
+
+ def test_predicate_builder
+ t = Teapot.create!(:name => "Bob")
+ assert_equal "Bob", Teapot.where(:id => [t]).first.name
+ assert_equal "Bob", Teapot.where(:id => t).first.name
+ end
+
+ def test_nested_model
+ assert_equal "ceiling_teapots", Ceiling::Teapot.table_name
+ end
+end
+
+class InclusionUnitTest < ActiveRecord::TestCase
+ def setup
+ @klass = Class.new { include ActiveRecord::Model }
+ end
+
+ def test_non_abstract_class
+ assert !@klass.abstract_class?
+ end
+
+ def test_abstract_class
+ @klass.abstract_class = true
+ assert @klass.abstract_class?
+ end
+
+ def test_establish_connection
+ assert @klass.respond_to?(:establish_connection)
+ assert ActiveRecord::Model.respond_to?(:establish_connection)
+ end
+
+ def test_adapter_connection
+ name = "#{ActiveRecord::Base.connection_config[:adapter]}_connection"
+ assert @klass.respond_to?(name)
+ assert ActiveRecord::Model.respond_to?(name)
+ end
+
+ def test_connection_handler
+ assert_equal ActiveRecord::Base.connection_handler, @klass.connection_handler
+ end
+
+ def test_mirrored_configuration
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ assert @klass.time_zone_aware_attributes
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ assert !@klass.time_zone_aware_attributes
+ ensure
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ end
+
+ # Doesn't really test anything, but this is here to ensure warnings don't occur
+ def test_included_twice
+ @klass.send :include, ActiveRecord::Model
+ end
+
+ def test_deprecation_proxy
+ assert_equal ActiveRecord::Model.name, ActiveRecord::Model::DeprecationProxy.name
+ assert_equal ActiveRecord::Base.superclass, assert_deprecated { ActiveRecord::Model::DeprecationProxy.superclass }
+
+ sup, sup2 = nil, nil
+ ActiveSupport.on_load(:__test_active_record_model_deprecation) do
+ sup = superclass
+ sup2 = send(:superclass)
+ end
+ assert_deprecated do
+ ActiveSupport.run_load_hooks(:__test_active_record_model_deprecation, ActiveRecord::Model::DeprecationProxy)
+ end
+ assert_equal ActiveRecord::Base.superclass, sup
+ assert_equal ActiveRecord::Base.superclass, sup2
+ end
+end
+
+class InclusionFixturesTest < ActiveRecord::TestCase
+ fixtures :teapots
+
+ def test_fixtured_record
+ assert_equal "Bob", teapots(:bob).name
+ end
+
+ def test_timestamped_fixture
+ assert_not_nil teapots(:bob).created_at
+ end
+end
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index b5d8314541..02df464469 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -65,7 +65,6 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_company_descends_from_active_record
- assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? }
assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base'
assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base'
assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
@@ -236,11 +235,11 @@ class InheritanceTest < ActiveRecord::TestCase
c.save
end
[ Company, Firm, Client].each { |klass| klass.reset_column_information }
- Company.set_inheritance_column('ruby_type')
+ Company.inheritance_column = 'ruby_type'
end
def switch_to_default_inheritance_column
[ Company, Firm, Client].each { |klass| klass.reset_column_information }
- Company.set_inheritance_column('type')
+ Company.inheritance_column = 'type'
end
end
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 3ae7b63dff..8f1cdd47ea 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -88,5 +88,17 @@ module ActiveRecord
LegacyMigration.down
assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
end
+
+ def test_migrate_down_with_table_name_prefix
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ ActiveRecord::Base.table_name_suffix = '_s'
+ migration = InvertibleMigration.new
+ migration.migrate(:up)
+ assert_nothing_raised { migration.migrate(:down) }
+ assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist"
+ ensure
+ ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = ''
+ end
+
end
end
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index e9bd7f07b6..807274ca67 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -6,12 +6,15 @@ require 'models/reader'
require 'models/legacy_thing'
require 'models/reference'
require 'models/string_key_object'
+require 'models/car'
+require 'models/engine'
+require 'models/wheel'
class LockWithoutDefault < ActiveRecord::Base; end
class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
- set_table_name :lock_without_defaults_cust
- set_locking_column :custom_lock_version
+ self.table_name = :lock_without_defaults_cust
+ self.locking_column = :custom_lock_version
end
class ReadonlyFirstNamePerson < Person
@@ -224,6 +227,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal lock_version, p1.lock_version
end
end
+
+ def test_polymorphic_destroy_with_dependencies_and_lock_version
+ car = Car.create!
+
+ assert_difference 'car.wheels.count' do
+ car.wheels << Wheel.create!
+ end
+ assert_difference 'car.wheels.count', -1 do
+ car.destroy
+ end
+ assert car.destroyed?
+ end
end
class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
@@ -278,6 +293,8 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
assert_equal true, p1.frozen?
assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) }
+ ensure
+ remove_counter_column_from(Person, 'legacy_things_count')
end
private
@@ -289,8 +306,8 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter)
end
- def remove_counter_column_from(model)
- model.connection.remove_column model.table_name, :test_count
+ def remove_counter_column_from(model, col = :test_count)
+ model.connection.remove_column model.table_name, col
model.reset_column_information
end
@@ -371,22 +388,32 @@ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) || in_memory_db?
end
end
+ def test_with_lock_commits_transaction
+ person = Person.find 1
+ person.with_lock do
+ person.first_name = 'fooman'
+ person.save!
+ end
+ assert_equal 'fooman', person.reload.first_name
+ end
+
+ def test_with_lock_rolls_back_transaction
+ person = Person.find 1
+ old = person.first_name
+ person.with_lock do
+ person.first_name = 'fooman'
+ person.save!
+ raise 'oops'
+ end rescue nil
+ assert_equal old, person.reload.first_name
+ end
+
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
def test_no_locks_no_wait
first, second = duel { Person.find 1 }
assert first.end > second.end
end
- # Hit by ruby deadlock detection since connection checkout is mutexed.
- if RUBY_VERSION < '1.9.0'
- def test_second_lock_waits
- assert [0.2, 1, 5].any? { |zzz|
- first, second = duel(zzz) { Person.find 1, :lock => true }
- second.end > first.end
- }
- end
- end
-
protected
def duel(zzz = 5)
t0, t1, t2, t3 = nil, nil, nil, nil
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 9e8475465e..d1f0ace184 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -5,7 +5,7 @@ require "active_support/log_subscriber/test_helper"
class LogSubscriberTest < ActiveRecord::TestCase
include ActiveSupport::LogSubscriber::TestHelper
- include ActiveSupport::BufferedLogger::Severity
+ include ActiveSupport::Logger::Severity
fixtures :posts
@@ -13,6 +13,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
@old_logger = ActiveRecord::Base.logger
@using_identity_map = ActiveRecord::IdentityMap.enabled?
ActiveRecord::IdentityMap.enabled = false
+ Developer.primary_key
super
ActiveRecord::LogSubscriber.attach_to(:active_record)
end
diff --git a/activerecord/test/cases/mass_assignment_security_test.rb b/activerecord/test/cases/mass_assignment_security_test.rb
index 9fff50edcb..8122857f52 100644
--- a/activerecord/test/cases/mass_assignment_security_test.rb
+++ b/activerecord/test/cases/mass_assignment_security_test.rb
@@ -50,6 +50,13 @@ module MassAssignmentTestHelpers
assert_equal 'm', person.gender
assert_equal 'rides a sweet bike', person.comments
end
+
+ def with_strict_sanitizer
+ ActiveRecord::Base.mass_assignment_sanitizer = :strict
+ yield
+ ensure
+ ActiveRecord::Base.mass_assignment_sanitizer = :logger
+ end
end
module MassAssignmentRelationTestHelpers
@@ -323,6 +330,13 @@ class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_one_build_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.build_best_friend(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create
def test_has_one_create_with_attr_protected_attributes
@@ -350,6 +364,13 @@ class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_one_create_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create!
def test_has_one_create_with_bang_with_attr_protected_attributes
@@ -377,6 +398,13 @@ class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_one_create_with_bang_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend!(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
end
@@ -438,6 +466,13 @@ class MassAssignmentSecurityBelongsToRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_belongs_to_create_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend_of(attributes_hash.except(:id, :comments))
+ assert_equal best_friend.id, @person.best_friend_of_id
+ end
+ end
+
# create!
def test_belongs_to_create_with_bang_with_attr_protected_attributes
@@ -465,6 +500,13 @@ class MassAssignmentSecurityBelongsToRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_belongs_to_create_with_bang_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend_of!(attributes_hash.except(:id, :comments))
+ assert_equal best_friend.id, @person.best_friend_of_id
+ end
+ end
+
end
@@ -499,6 +541,13 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_many_build_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.best_friends.build(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create
def test_has_many_create_with_attr_protected_attributes
@@ -526,6 +575,13 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_many_create_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.best_friends.create(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create!
def test_has_many_create_with_bang_with_attr_protected_attributes
@@ -553,6 +609,13 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_many_create_with_bang_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.best_friends.create!(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
end
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 0ab4f30363..ebf6e26385 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -104,7 +104,7 @@ class MethodScopingTest < ActiveRecord::TestCase
def test_scoped_find_include
# with the include, will retrieve only developers for the given project
scoped_developers = Developer.send(:with_scope, :find => { :include => :projects }) do
- Developer.find(:all, :conditions => 'projects.id = 2')
+ Developer.find(:all, :conditions => { 'projects.id' => 2 })
end
assert scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
@@ -204,7 +204,7 @@ class MethodScopingTest < ActiveRecord::TestCase
def test_scoped_count_include
# with the include, will retrieve only developers for the given project
Developer.send(:with_scope, :find => { :include => :projects }) do
- assert_equal 1, Developer.count(:conditions => 'projects.id = 2')
+ assert_equal 1, Developer.count(:conditions => { 'projects.id' => 2 })
end
end
@@ -339,7 +339,7 @@ class NestedScopingTest < ActiveRecord::TestCase
def test_nested_scoped_find_include
Developer.send(:with_scope, :find => { :include => :projects }) do
- Developer.send(:with_scope, :find => { :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :conditions => { 'projects.id' => 2 } }) do
assert_nothing_raised { Developer.find(1) }
assert_equal('David', Developer.find(:first).name)
end
@@ -348,7 +348,7 @@ class NestedScopingTest < ActiveRecord::TestCase
def test_nested_scoped_find_merged_include
# :include's remain unique and don't "double up" when merging
- Developer.send(:with_scope, :find => { :include => :projects, :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :include => :projects, :conditions => { 'projects.id' => 2 } }) do
Developer.send(:with_scope, :find => { :include => :projects }) do
assert_equal 1, Developer.scoped.includes_values.uniq.length
assert_equal 'David', Developer.find(:first).name
@@ -356,7 +356,7 @@ class NestedScopingTest < ActiveRecord::TestCase
end
# the nested scope doesn't remove the first :include
- Developer.send(:with_scope, :find => { :include => :projects, :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :include => :projects, :conditions => { 'projects.id' => 2 } }) do
Developer.send(:with_scope, :find => { :include => [] }) do
assert_equal 1, Developer.scoped.includes_values.uniq.length
assert_equal('David', Developer.find(:first).name)
@@ -364,7 +364,7 @@ class NestedScopingTest < ActiveRecord::TestCase
end
# mixing array and symbol include's will merge correctly
- Developer.send(:with_scope, :find => { :include => [:projects], :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :include => [:projects], :conditions => { 'projects.id' => 2 } }) do
Developer.send(:with_scope, :find => { :include => :projects }) do
assert_equal 1, Developer.scoped.includes_values.uniq.length
assert_equal('David', Developer.find(:first).name)
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
new file mode 100644
index 0000000000..f0b1f74bd3
--- /dev/null
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -0,0 +1,328 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class ChangeSchemaTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ @table_name = :testings
+ end
+
+ def teardown
+ super
+ connection.drop_table :testings rescue nil
+ ActiveRecord::Base.primary_key_prefix_type = nil
+ end
+
+ def test_create_table_without_id
+ testing_table_with_only_foo_attribute do
+ assert_equal connection.columns(:testings).size, 1
+ end
+ end
+
+ def test_add_column_with_primary_key_attribute
+ testing_table_with_only_foo_attribute do
+ connection.add_column :testings, :id, :primary_key
+ assert_equal connection.columns(:testings).size, 2
+ end
+ end
+
+ def test_create_table_adds_id
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert_equal %w(foo id), connection.columns(:testings).map(&:name).sort
+ end
+
+ def test_create_table_with_not_null_column
+ connection.create_table :testings do |t|
+ t.column :foo, :string, :null => false
+ end
+
+ assert_raises(ActiveRecord::StatementInvalid) do
+ connection.execute "insert into testings (foo) values (NULL)"
+ end
+ end
+
+ def test_create_table_with_defaults
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
+ mysql = current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
+
+ connection.create_table :testings do |t|
+ t.column :one, :string, :default => "hello"
+ t.column :two, :boolean, :default => true
+ t.column :three, :boolean, :default => false
+ t.column :four, :integer, :default => 1
+ t.column :five, :text, :default => "hello" unless mysql
+ end
+
+ columns = connection.columns(:testings)
+ one = columns.detect { |c| c.name == "one" }
+ two = columns.detect { |c| c.name == "two" }
+ three = columns.detect { |c| c.name == "three" }
+ four = columns.detect { |c| c.name == "four" }
+ five = columns.detect { |c| c.name == "five" } unless mysql
+
+ assert_equal "hello", one.default
+ assert_equal true, two.default
+ assert_equal false, three.default
+ assert_equal 1, four.default
+ assert_equal "hello", five.default unless mysql
+ end
+
+ def test_create_table_with_limits
+ connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 255
+
+ t.column :default_int, :integer
+
+ t.column :one_int, :integer, :limit => 1
+ t.column :four_int, :integer, :limit => 4
+ t.column :eight_int, :integer, :limit => 8
+ t.column :eleven_int, :integer, :limit => 11
+ end
+
+ columns = connection.columns(:testings)
+ foo = columns.detect { |c| c.name == "foo" }
+ assert_equal 255, foo.limit
+
+ default = columns.detect { |c| c.name == "default_int" }
+ one = columns.detect { |c| c.name == "one_int" }
+ four = columns.detect { |c| c.name == "four_int" }
+ eight = columns.detect { |c| c.name == "eight_int" }
+ eleven = columns.detect { |c| c.name == "eleven_int" }
+
+ if current_adapter?(:PostgreSQLAdapter)
+ assert_equal 'integer', default.sql_type
+ assert_equal 'smallint', one.sql_type
+ assert_equal 'integer', four.sql_type
+ assert_equal 'bigint', eight.sql_type
+ assert_equal 'integer', eleven.sql_type
+ elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
+ assert_match 'int(11)', default.sql_type
+ assert_match 'tinyint', one.sql_type
+ assert_match 'int', four.sql_type
+ assert_match 'bigint', eight.sql_type
+ assert_match 'int(11)', eleven.sql_type
+ elsif current_adapter?(:OracleAdapter)
+ assert_equal 'NUMBER(38)', default.sql_type
+ assert_equal 'NUMBER(1)', one.sql_type
+ assert_equal 'NUMBER(4)', four.sql_type
+ assert_equal 'NUMBER(8)', eight.sql_type
+ end
+ end
+
+ def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
+
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert_equal %w(foo testing_id), connection.columns(:testings).map(&:name).sort
+ end
+
+ def test_create_table_with_primary_key_prefix_as_table_name
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
+
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert_equal %w(foo testingid), connection.columns(:testings).map(&:name).sort
+ end
+
+ def test_create_table_with_timestamps_should_create_datetime_columns
+ connection.create_table table_name do |t|
+ t.timestamps
+ end
+ created_columns = connection.columns(table_name)
+
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+
+ assert !created_at_column.null
+ assert !updated_at_column.null
+ end
+
+ def test_create_table_with_timestamps_should_create_datetime_columns_with_options
+ connection.create_table table_name do |t|
+ t.timestamps :null => false
+ end
+ created_columns = connection.columns(table_name)
+
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+
+ assert !created_at_column.null
+ assert !updated_at_column.null
+ end
+
+ def test_create_table_without_a_block
+ connection.create_table table_name
+ end
+
+ def test_add_column_not_null_without_default
+ # Sybase, and SQLite3 will not allow you to add a NOT NULL
+ # column to a table without a default value.
+ if current_adapter?(:SybaseAdapter, :SQLite3Adapter)
+ skip "not supported on #{connection.class}"
+ end
+
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+ connection.add_column :testings, :bar, :string, :null => false
+
+ assert_raise(ActiveRecord::StatementInvalid) do
+ connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
+ end
+ end
+
+ def test_add_column_not_null_with_default
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ con = connection
+ connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
+ connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
+ connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
+ assert_nothing_raised {connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
+
+ assert_raises(ActiveRecord::StatementInvalid) do
+ unless current_adapter?(:OpenBaseAdapter)
+ connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+ else
+ connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
+ "Testing Insert","id",2)
+ end
+ end
+ end
+
+ def test_change_column_quotes_column_names
+ connection.create_table :testings do |t|
+ t.column :select, :string
+ end
+
+ connection.change_column :testings, :select, :string, :limit => 10
+
+ # Oracle needs primary key value from sequence
+ if current_adapter?(:OracleAdapter)
+ connection.execute "insert into testings (id, #{connection.quote_column_name('select')}) values (testings_seq.nextval, '7 chars')"
+ else
+ connection.execute "insert into testings (#{connection.quote_column_name('select')}) values ('7 chars')"
+ end
+ end
+
+ def test_keeping_default_and_notnull_constaint_on_change
+ connection.create_table :testings do |t|
+ t.column :title, :string
+ end
+ person_klass = Class.new(ActiveRecord::Base)
+ person_klass.table_name = 'testings'
+
+ person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
+ person_klass.reset_column_information
+ assert_equal 99, person_klass.columns_hash["wealth"].default
+ assert_equal false, person_klass.columns_hash["wealth"].null
+ # Oracle needs primary key value from sequence
+ if current_adapter?(:OracleAdapter)
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
+ else
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
+ end
+
+ # change column default to see that column doesn't lose its not null definition
+ person_klass.connection.change_column_default "testings", "wealth", 100
+ person_klass.reset_column_information
+ assert_equal 100, person_klass.columns_hash["wealth"].default
+ assert_equal false, person_klass.columns_hash["wealth"].null
+
+ # rename column to see that column doesn't lose its not null and/or default definition
+ person_klass.connection.rename_column "testings", "wealth", "money"
+ person_klass.reset_column_information
+ assert_nil person_klass.columns_hash["wealth"]
+ assert_equal 100, person_klass.columns_hash["money"].default
+ assert_equal false, person_klass.columns_hash["money"].null
+
+ # change column
+ person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
+ person_klass.reset_column_information
+ assert_equal 1000, person_klass.columns_hash["money"].default
+ assert_equal false, person_klass.columns_hash["money"].null
+
+ # change column, make it nullable and clear default
+ person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
+ person_klass.reset_column_information
+ assert_nil person_klass.columns_hash["money"].default
+ assert_equal true, person_klass.columns_hash["money"].null
+
+ # change_column_null, make it not nullable and set null values to a default value
+ person_klass.connection.execute('UPDATE testings SET money = NULL')
+ person_klass.connection.change_column_null "testings", "money", false, 2000
+ person_klass.reset_column_information
+ assert_nil person_klass.columns_hash["money"].default
+ assert_equal false, person_klass.columns_hash["money"].null
+ assert_equal 2000, connection.select_values("SELECT money FROM testings").first.to_i
+ end
+
+ def test_column_exists
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert connection.column_exists?(:testings, :foo)
+ refute connection.column_exists?(:testings, :bar)
+ end
+
+ def test_column_exists_with_type
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ t.column :bar, :decimal, :precision => 8, :scale => 2
+ end
+
+ assert connection.column_exists?(:testings, :foo, :string)
+ refute connection.column_exists?(:testings, :foo, :integer)
+
+ assert connection.column_exists?(:testings, :bar, :decimal)
+ refute connection.column_exists?(:testings, :bar, :integer)
+ end
+
+ def test_column_exists_with_definition
+ connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :decimal, :precision => 8, :scale => 2
+ end
+
+ assert connection.column_exists?(:testings, :foo, :string, :limit => 100)
+ refute connection.column_exists?(:testings, :foo, :string, :limit => 50)
+ assert connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
+ refute connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
+ end
+
+ def test_column_exists_on_table_with_no_options_parameter_supplied
+ connection.create_table :testings do |t|
+ t.string :foo
+ end
+ connection.change_table :testings do |t|
+ assert t.column_exists?(:foo)
+ assert !(t.column_exists?(:bar))
+ end
+ end
+
+ private
+ def testing_table_with_only_foo_attribute
+ connection.create_table :testings, :id => false do |t|
+ t.column :foo, :string
+ end
+
+ yield
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
new file mode 100644
index 0000000000..040445ef12
--- /dev/null
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -0,0 +1,188 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class ColumnAttributesTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ def test_add_remove_single_field_using_string_arguments
+ refute TestModel.column_methods_hash.key?(:last_name)
+
+ add_column 'test_models', 'last_name', :string
+
+ TestModel.reset_column_information
+
+ assert TestModel.column_methods_hash.key?(:last_name)
+
+ remove_column 'test_models', 'last_name'
+
+ TestModel.reset_column_information
+ refute TestModel.column_methods_hash.key?(:last_name)
+ end
+
+ def test_add_remove_single_field_using_symbol_arguments
+ refute TestModel.column_methods_hash.key?(:last_name)
+
+ add_column :test_models, :last_name, :string
+
+ TestModel.reset_column_information
+ assert TestModel.column_methods_hash.key?(:last_name)
+
+ remove_column :test_models, :last_name
+
+ TestModel.reset_column_information
+ refute TestModel.column_methods_hash.key?(:last_name)
+ end
+
+ def test_unabstracted_database_dependent_types
+ skip "not supported" unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+
+ add_column :test_models, :intelligence_quotient, :tinyint
+ TestModel.reset_column_information
+ assert_match(/tinyint/, TestModel.columns_hash['intelligence_quotient'].sql_type)
+ end
+
+ # We specifically do a manual INSERT here, and then test only the SELECT
+ # functionality. This allows us to more easily catch INSERT being broken,
+ # but SELECT actually working fine.
+ def test_native_decimal_insert_manual_vs_automatic
+ correct_value = '0012345678901234567890.0123456789'.to_d
+
+ connection.add_column "test_models", "wealth", :decimal, :precision => '30', :scale => '10'
+
+ # Do a manual insertion
+ if current_adapter?(:OracleAdapter)
+ connection.execute "insert into test_models (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)"
+ elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
+ connection.execute "insert into test_models (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)"
+ elsif current_adapter?(:PostgreSQLAdapter)
+ connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, now(), now())"
+ else
+ connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, 0, 0)"
+ end
+
+ # SELECT
+ row = TestModel.find(:first)
+ assert_kind_of BigDecimal, row.wealth
+
+ # If this assert fails, that means the SELECT is broken!
+ unless current_adapter?(:SQLite3Adapter)
+ assert_equal correct_value, row.wealth
+ end
+
+ # Reset to old state
+ TestModel.delete_all
+
+ # Now use the Rails insertion
+ TestModel.create :wealth => BigDecimal.new("12345678901234567890.0123456789")
+
+ # SELECT
+ row = TestModel.find(:first)
+ assert_kind_of BigDecimal, row.wealth
+
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
+ unless current_adapter?(:SQLite3Adapter)
+ assert_equal correct_value, row.wealth
+ end
+ end
+
+ def test_add_column_with_precision_and_scale
+ connection.add_column 'test_models', 'wealth', :decimal, :precision => 9, :scale => 7
+
+ wealth_column = TestModel.columns_hash['wealth']
+ assert_equal 9, wealth_column.precision
+ assert_equal 7, wealth_column.scale
+ end
+
+ def test_change_column_preserve_other_column_precision_and_scale
+ skip "only on sqlite3" unless current_adapter?(:SQLite3Adapter)
+
+ connection.add_column 'test_models', 'last_name', :string
+ connection.add_column 'test_models', 'wealth', :decimal, :precision => 9, :scale => 7
+
+ wealth_column = TestModel.columns_hash['wealth']
+ assert_equal 9, wealth_column.precision
+ assert_equal 7, wealth_column.scale
+
+ connection.change_column 'test_models', 'last_name', :string, :null => false
+ TestModel.reset_column_information
+
+ wealth_column = TestModel.columns_hash['wealth']
+ assert_equal 9, wealth_column.precision
+ assert_equal 7, wealth_column.scale
+ end
+
+ def test_native_types
+ add_column "test_models", "first_name", :string
+ add_column "test_models", "last_name", :string
+ add_column "test_models", "bio", :text
+ add_column "test_models", "age", :integer
+ add_column "test_models", "height", :float
+ add_column "test_models", "wealth", :decimal, :precision => '30', :scale => '10'
+ add_column "test_models", "birthday", :datetime
+ add_column "test_models", "favorite_day", :date
+ add_column "test_models", "moment_of_truth", :datetime
+ add_column "test_models", "male", :boolean
+
+ TestModel.create :first_name => 'bob', :last_name => 'bobsen',
+ :bio => "I was born ....", :age => 18, :height => 1.78,
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
+
+ bob = TestModel.find(:first)
+ assert_equal 'bob', bob.first_name
+ assert_equal 'bobsen', bob.last_name
+ assert_equal "I was born ....", bob.bio
+ assert_equal 18, bob.age
+
+ # Test for 30 significant digits (beyond the 16 of float), 10 of them
+ # after the decimal place.
+
+ unless current_adapter?(:SQLite3Adapter)
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
+ end
+
+ assert_equal true, bob.male?
+
+ assert_equal String, bob.first_name.class
+ assert_equal String, bob.last_name.class
+ assert_equal String, bob.bio.class
+ assert_equal Fixnum, bob.age.class
+ assert_equal Time, bob.birthday.class
+
+ if current_adapter?(:OracleAdapter, :SybaseAdapter)
+ # Sybase, and Oracle don't differentiate between date/time
+ assert_equal Time, bob.favorite_day.class
+ else
+ assert_equal Date, bob.favorite_day.class
+ end
+
+ # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
+ # therefore no timezone change is done afterwards when default timezone is changed
+ unless current_adapter?(:OracleAdapter)
+ # Test DateTime column and defaults, including timezone.
+ # FIXME: moment of truth may be Time on 64-bit platforms.
+ if bob.moment_of_truth.is_a?(DateTime)
+
+ with_env_tz 'US/Eastern' do
+ bob.reload
+ assert_equal DateTime.local_offset, bob.moment_of_truth.offset
+ assert_not_equal 0, bob.moment_of_truth.offset
+ assert_not_equal "Z", bob.moment_of_truth.zone
+ # US/Eastern is -5 hours from GMT
+ assert_equal Rational(-5, 24), bob.moment_of_truth.offset
+ assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
+ end
+ end
+ end
+
+ assert_instance_of TrueClass, bob.male?
+ assert_kind_of BigDecimal, bob.wealth
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb
new file mode 100644
index 0000000000..913d935f7c
--- /dev/null
+++ b/activerecord/test/cases/migration/column_positioning_test.rb
@@ -0,0 +1,60 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class ColumnPositioningTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+ alias :conn :connection
+
+ def setup
+ super
+
+ unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+ skip "not supported on #{connection.class}"
+ end
+
+ @connection = ActiveRecord::Base.connection
+
+ connection.create_table :testings, :id => false do |t|
+ t.column :first, :integer
+ t.column :second, :integer
+ t.column :third, :integer
+ end
+ end
+
+ def teardown
+ super
+ connection.drop_table :testings rescue nil
+ ActiveRecord::Base.primary_key_prefix_type = nil
+ end
+
+ def test_column_positioning
+ assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_add_column_with_positioning
+ conn.add_column :testings, :new_col, :integer
+ assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_add_column_with_positioning_first
+ conn.add_column :testings, :new_col, :integer, :first => true
+ assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_add_column_with_positioning_after
+ conn.add_column :testings, :new_col, :integer, :after => :first
+ assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_change_column_with_positioning
+ conn.change_column :testings, :second, :integer, :first => true
+ assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
+
+ conn.change_column :testings, :second, :integer, :after => :third
+ assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
+ end
+
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index d108b456f0..7d026961be 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -67,6 +67,24 @@ module ActiveRecord
assert_equal [:drop_table, [:system_settings]], drop_table
end
+ def test_invert_create_table_with_options
+ @recorder.record :create_table, [:people_reminders, {:id => false}]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:people_reminders]], drop_table
+ end
+
+ def test_invert_create_join_table
+ @recorder.record :create_join_table, [:musics, :artists]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:artists_musics]], drop_table
+ end
+
+ def test_invert_create_join_table_with_table_name
+ @recorder.record :create_join_table, [:musics, :artists, {:table_name => :catalog}]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:catalog]], drop_table
+ end
+
def test_invert_rename_table
@recorder.record :rename_table, [:old, :new]
rename = @recorder.inverse.first
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
new file mode 100644
index 0000000000..0428d9ba76
--- /dev/null
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -0,0 +1,70 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class CreateJoinTableTest < ActiveRecord::TestCase
+ attr_reader :connection
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_create_join_table
+ connection.create_join_table :artists, :musics
+
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
+ ensure
+ connection.drop_table :artists_musics
+ end
+
+ def test_create_join_table_set_not_null_by_default
+ connection.create_join_table :artists, :musics
+
+ assert_equal [false, false], connection.columns(:artists_musics).map(&:null)
+ ensure
+ connection.drop_table :artists_musics
+ end
+
+ def test_create_join_table_with_strings
+ connection.create_join_table 'artists', 'musics'
+
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
+ ensure
+ connection.drop_table :artists_musics
+ end
+
+ def test_create_join_table_with_the_proper_order
+ connection.create_join_table :videos, :musics
+
+ assert_equal %w(music_id video_id), connection.columns(:musics_videos).map(&:name).sort
+ ensure
+ connection.drop_table :musics_videos
+ end
+
+ def test_create_join_table_with_the_table_name
+ connection.create_join_table :artists, :musics, :table_name => :catalog
+
+ assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
+ ensure
+ connection.drop_table :catalog
+ end
+
+ def test_create_join_table_with_the_table_name_as_string
+ connection.create_join_table :artists, :musics, :table_name => 'catalog'
+
+ assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
+ ensure
+ connection.drop_table :catalog
+ end
+
+ def test_create_join_table_with_column_options
+ connection.create_join_table :artists, :musics, :column_options => {:null => true}
+
+ assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
+ ensure
+ connection.drop_table :artists_musics
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
new file mode 100644
index 0000000000..fe53510ba2
--- /dev/null
+++ b/activerecord/test/cases/migration/helper.rb
@@ -0,0 +1,64 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class << self
+ attr_accessor :message_count
+ end
+
+ def puts(text="")
+ ActiveRecord::Migration.message_count ||= 0
+ ActiveRecord::Migration.message_count += 1
+ end
+
+ module TestHelper
+ attr_reader :connection, :table_name
+
+ class TestModel < ActiveRecord::Base
+ self.table_name = 'test_models'
+ end
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ connection.create_table :test_models do |t|
+ t.timestamps
+ end
+
+ TestModel.reset_column_information
+ end
+
+ def teardown
+ super
+ TestModel.reset_table_name
+ TestModel.reset_sequence_name
+ connection.drop_table :test_models rescue nil
+ end
+
+ private
+ def add_column(*args)
+ connection.add_column(*args)
+ end
+
+ def remove_column(*args)
+ connection.remove_column(*args)
+ end
+
+ def rename_column(*args)
+ connection.rename_column(*args)
+ end
+
+ def add_index(*args)
+ connection.add_index(*args)
+ end
+
+ def change_column(*args)
+ connection.change_column(*args)
+ end
+
+ def rename_table(*args)
+ connection.rename_table(*args)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb
new file mode 100644
index 0000000000..dd9492924c
--- /dev/null
+++ b/activerecord/test/cases/migration/index_test.rb
@@ -0,0 +1,185 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class IndexTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ @table_name = :testings
+
+ connection.create_table table_name do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :string, :limit => 100
+
+ t.string :first_name
+ t.string :last_name, :limit => 100
+ t.string :key, :limit => 100
+ t.boolean :administrator
+ end
+ end
+
+ def teardown
+ super
+ connection.drop_table :testings rescue nil
+ ActiveRecord::Base.primary_key_prefix_type = nil
+ end
+
+ def test_rename_index
+ skip "not supported on openbase" if current_adapter?(:OpenBaseAdapter)
+
+ # keep the names short to make Oracle and similar behave
+ connection.add_index(table_name, [:foo], :name => 'old_idx')
+ connection.rename_index(table_name, 'old_idx', 'new_idx')
+
+ # if the adapter doesn't support the indexes call, pick defaults that let the test pass
+ refute connection.index_name_exists?(table_name, 'old_idx', false)
+ assert connection.index_name_exists?(table_name, 'new_idx', true)
+ end
+
+ def test_double_add_index
+ skip "not supported on openbase" if current_adapter?(:OpenBaseAdapter)
+
+ connection.add_index(table_name, [:foo], :name => 'some_idx')
+ assert_raises(ArgumentError) {
+ connection.add_index(table_name, [:foo], :name => 'some_idx')
+ }
+ end
+
+ def test_remove_nonexistent_index
+ skip "not supported on openbase" if current_adapter?(:OpenBaseAdapter)
+
+ # we do this by name, so OpenBase is a wash as noted above
+ assert_raise(ArgumentError) { connection.remove_index(table_name, "no_such_index") }
+ end
+
+ def test_add_index_name_length_limit
+ good_index_name = 'x' * connection.index_name_length
+ too_long_index_name = good_index_name + 'x'
+
+ assert_raises(ArgumentError) {
+ connection.add_index(table_name, "foo", :name => too_long_index_name)
+ }
+
+ refute connection.index_name_exists?(table_name, too_long_index_name, false)
+ connection.add_index(table_name, "foo", :name => good_index_name)
+
+ assert connection.index_name_exists?(table_name, good_index_name, false)
+ connection.remove_index(table_name, :name => good_index_name)
+ end
+
+ def test_index_symbol_names
+ connection.add_index table_name, :foo, :name => :symbol_index_name
+ assert connection.index_exists?(table_name, :foo, :name => :symbol_index_name)
+
+ connection.remove_index table_name, :name => :symbol_index_name
+ refute connection.index_exists?(table_name, :foo, :name => :symbol_index_name)
+ end
+
+ def test_index_exists
+ connection.add_index :testings, :foo
+
+ assert connection.index_exists?(:testings, :foo)
+ assert !connection.index_exists?(:testings, :bar)
+ end
+
+ def test_index_exists_on_multiple_columns
+ connection.add_index :testings, [:foo, :bar]
+
+ assert connection.index_exists?(:testings, [:foo, :bar])
+ end
+
+ def test_unique_index_exists
+ connection.add_index :testings, :foo, :unique => true
+
+ assert connection.index_exists?(:testings, :foo, :unique => true)
+ end
+
+ def test_named_index_exists
+ connection.add_index :testings, :foo, :name => "custom_index_name"
+
+ assert connection.index_exists?(:testings, :foo, :name => "custom_index_name")
+ end
+
+ def test_add_index_attribute_length_limit
+ connection.add_index :testings, [:foo, :bar], :length => {:foo => 10, :bar => nil}
+
+ assert connection.index_exists?(:testings, [:foo, :bar])
+ end
+
+ def test_add_index
+ connection.add_index("testings", "last_name")
+ connection.remove_index("testings", "last_name")
+
+ # Orcl nds shrt indx nms. Sybs 2.
+ # OpenBase does not have named indexes. You must specify a single column name
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", :column => ["last_name", "first_name"])
+
+ # Oracle adapter cannot have specified index name larger than 30 characters
+ # Oracle adapter is shortening index name when just column list is given
+ unless current_adapter?(:OracleAdapter)
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", :name => :index_testings_on_last_name_and_first_name)
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", "last_name_and_first_name")
+ end
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", ["last_name", "first_name"])
+
+ connection.add_index("testings", ["last_name"], :length => 10)
+ connection.remove_index("testings", "last_name")
+
+ connection.add_index("testings", ["last_name"], :length => {:last_name => 10})
+ connection.remove_index("testings", ["last_name"])
+
+ connection.add_index("testings", ["last_name", "first_name"], :length => 10)
+ connection.remove_index("testings", ["last_name", "first_name"])
+
+ connection.add_index("testings", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20})
+ connection.remove_index("testings", ["last_name", "first_name"])
+ end
+
+ # quoting
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
+ # OpenBase does not have named indexes. You must specify a single column name
+ unless current_adapter?(:OpenBaseAdapter)
+ connection.add_index("testings", ["key"], :name => "key_idx", :unique => true)
+ connection.remove_index("testings", :name => "key_idx", :unique => true)
+ end
+
+ # Sybase adapter does not support indexes on :boolean columns
+ # OpenBase does not have named indexes. You must specify a single column
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
+ connection.add_index("testings", %w(last_name first_name administrator), :name => "named_admin")
+ connection.remove_index("testings", :name => "named_admin")
+ end
+
+ # Selected adapters support index sort order
+ if current_adapter?(:SQLite3Adapter, :MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)
+ connection.add_index("testings", ["last_name"], :order => {:last_name => :desc})
+ connection.remove_index("testings", ["last_name"])
+ connection.add_index("testings", ["last_name", "first_name"], :order => {:last_name => :desc})
+ connection.remove_index("testings", ["last_name", "first_name"])
+ connection.add_index("testings", ["last_name", "first_name"], :order => {:last_name => :desc, :first_name => :asc})
+ connection.remove_index("testings", ["last_name", "first_name"])
+ connection.add_index("testings", ["last_name", "first_name"], :order => :desc)
+ connection.remove_index("testings", ["last_name", "first_name"])
+ end
+ end
+
+ def test_add_partial_index
+ skip 'only on pg' unless current_adapter?(:PostgreSQLAdapter)
+
+ connection.add_index("testings", "last_name", :where => "first_name = 'john doe'")
+ assert connection.index_exists?("testings", "last_name")
+
+ connection.remove_index("testings", "last_name")
+ assert !connection.index_exists?("testings", "last_name")
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
new file mode 100644
index 0000000000..ee0c20747e
--- /dev/null
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -0,0 +1,37 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class LoggerTest < ActiveRecord::TestCase
+ # mysql can't roll back ddl changes
+ self.use_transactional_fixtures = false
+
+ Migration = Struct.new(:name, :version) do
+ def migrate direction
+ # do nothing
+ end
+ end
+
+ def setup
+ super
+ ActiveRecord::SchemaMigration.create_table
+ ActiveRecord::SchemaMigration.delete_all
+ end
+
+ def teardown
+ super
+ ActiveRecord::SchemaMigration.drop_table
+ end
+
+ def test_migration_should_be_run_without_logger
+ previous_logger = ActiveRecord::Base.logger
+ ActiveRecord::Base.logger = nil
+ migrations = [Migration.new('a', 1), Migration.new('b', 2), Migration.new('c', 3)]
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ ensure
+ ActiveRecord::Base.logger = previous_logger
+ end
+ end
+ end
+end
+
diff --git a/activerecord/test/cases/migration/rename_column_test.rb b/activerecord/test/cases/migration/rename_column_test.rb
new file mode 100644
index 0000000000..16e09fd80e
--- /dev/null
+++ b/activerecord/test/cases/migration/rename_column_test.rb
@@ -0,0 +1,194 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class RenameColumnTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ # FIXME: this is more of an integration test with AR::Base and the
+ # schema modifications. Maybe we should move this?
+ def test_add_rename
+ add_column "test_models", "girlfriend", :string
+ TestModel.reset_column_information
+
+ TestModel.create :girlfriend => 'bobette'
+
+ rename_column "test_models", "girlfriend", "exgirlfriend"
+
+ TestModel.reset_column_information
+ bob = TestModel.find(:first)
+
+ assert_equal "bobette", bob.exgirlfriend
+ end
+
+ # FIXME: another integration test. We should decouple this from the
+ # AR::Base implementation.
+ def test_rename_column_using_symbol_arguments
+ add_column :test_models, :first_name, :string
+
+ TestModel.create :first_name => 'foo'
+
+ rename_column :test_models, :first_name, :nick_name
+ TestModel.reset_column_information
+ assert TestModel.column_names.include?("nick_name")
+ assert_equal ['foo'], TestModel.find(:all).map(&:nick_name)
+ end
+
+ # FIXME: another integration test. We should decouple this from the
+ # AR::Base implementation.
+ def test_rename_column
+ add_column "test_models", "first_name", "string"
+
+ TestModel.create :first_name => 'foo'
+
+ rename_column "test_models", "first_name", "nick_name"
+ TestModel.reset_column_information
+ assert TestModel.column_names.include?("nick_name")
+ assert_equal ['foo'], TestModel.find(:all).map(&:nick_name)
+ end
+
+ def test_rename_column_preserves_default_value_not_null
+ add_column 'test_models', 'salary', :integer, :default => 70000
+
+ default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
+ assert_equal 70000, default_before
+
+ rename_column "test_models", "salary", "anual_salary"
+
+ assert TestModel.column_names.include?("anual_salary")
+ default_after = connection.columns("test_models").find { |c| c.name == "anual_salary" }.default
+ assert_equal 70000, default_after
+ end
+
+ def test_rename_nonexistent_column
+ exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
+ ActiveRecord::StatementInvalid
+ else
+ ActiveRecord::ActiveRecordError
+ end
+ assert_raise(exception) do
+ rename_column "test_models", "nonexistent", "should_fail"
+ end
+ end
+
+ def test_rename_column_with_sql_reserved_word
+ add_column 'test_models', 'first_name', :string
+ rename_column "test_models", "first_name", "group"
+
+ assert TestModel.column_names.include?("group")
+ end
+
+ def test_rename_column_with_an_index
+ add_column "test_models", :hat_name, :string
+ add_index :test_models, :hat_name
+
+ # FIXME: we should test that the index goes away
+ rename_column "test_models", "hat_name", "name"
+ end
+
+ def test_remove_column_with_index
+ add_column "test_models", :hat_name, :string
+ add_index :test_models, :hat_name
+
+ # FIXME: we should test that the index goes away
+ remove_column("test_models", "hat_name")
+ end
+
+ def test_remove_column_with_multi_column_index
+ add_column "test_models", :hat_size, :integer
+ add_column "test_models", :hat_style, :string, :limit => 100
+ add_index "test_models", ["hat_style", "hat_size"], :unique => true
+
+ # FIXME: we should test that the index goes away
+ remove_column("test_models", "hat_size")
+ end
+
+ # FIXME: we need to test that these calls do something
+ def test_change_type_of_not_null_column
+ change_column "test_models", "updated_at", :datetime, :null => false
+ change_column "test_models", "updated_at", :datetime, :null => false
+ change_column "test_models", "updated_at", :datetime, :null => true
+ end
+
+ def test_change_column_nullability
+ add_column "test_models", "funny", :boolean
+ assert TestModel.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
+
+ change_column "test_models", "funny", :boolean, :null => false, :default => true
+
+ TestModel.reset_column_information
+ refute TestModel.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
+
+ change_column "test_models", "funny", :boolean, :null => true
+ TestModel.reset_column_information
+ assert TestModel.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
+ end
+
+ def test_change_column
+ add_column 'test_models', 'age', :integer
+ add_column 'test_models', 'approved', :boolean, :default => true
+
+ old_columns = connection.columns(TestModel.table_name)
+
+ assert old_columns.find { |c| c.name == 'age' && c.type == :integer }
+
+ change_column "test_models", "age", :string
+
+ new_columns = connection.columns(TestModel.table_name)
+
+ refute new_columns.find { |c| c.name == 'age' and c.type == :integer }
+ assert new_columns.find { |c| c.name == 'age' and c.type == :string }
+
+ old_columns = connection.columns(TestModel.table_name)
+ assert old_columns.find { |c|
+ c.name == 'approved' && c.type == :boolean && c.default == true
+ }
+
+ change_column :test_models, :approved, :boolean, :default => false
+ new_columns = connection.columns(TestModel.table_name)
+
+ refute new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
+ change_column :test_models, :approved, :boolean, :default => true
+ end
+
+ def test_change_column_with_nil_default
+ add_column "test_models", "contributor", :boolean, :default => true
+ assert TestModel.new.contributor?
+
+ change_column "test_models", "contributor", :boolean, :default => nil
+ TestModel.reset_column_information
+ refute TestModel.new.contributor?
+ assert_nil TestModel.new.contributor
+ end
+
+ def test_change_column_with_new_default
+ add_column "test_models", "administrator", :boolean, :default => true
+ assert TestModel.new.administrator?
+
+ change_column "test_models", "administrator", :boolean, :default => false
+ TestModel.reset_column_information
+ refute TestModel.new.administrator?
+ end
+
+ def test_change_column_default
+ add_column "test_models", "first_name", :string
+ connection.change_column_default "test_models", "first_name", "Tester"
+
+ assert_equal "Tester", TestModel.new.first_name
+ end
+
+ def test_change_column_default_to_null
+ add_column "test_models", "first_name", :string
+ connection.change_column_default "test_models", "first_name", nil
+ assert_nil TestModel.new.first_name
+ end
+
+ def test_remove_column_no_second_parameter_raises_exception
+ assert_raise(ArgumentError) { connection.remove_column("funny") }
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
new file mode 100644
index 0000000000..d5ff2c607f
--- /dev/null
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -0,0 +1,72 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class RenameTableTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ def setup
+ super
+ add_column 'test_models', :url, :string
+ remove_column 'test_models', :created_at
+ remove_column 'test_models', :updated_at
+ end
+
+ def test_rename_table_for_sqlite_should_work_with_reserved_words
+ renamed = false
+
+ skip "not supported" unless current_adapter?(:SQLite3Adapter)
+
+ add_column :test_models, :url, :string
+ connection.rename_table :references, :old_references
+ connection.rename_table :test_models, :references
+
+ renamed = true
+
+ # Using explicit id in insert for compatibility across all databases
+ con = connection
+ con.execute "INSERT INTO 'references' (url, created_at, updated_at) VALUES ('http://rubyonrails.com', 0, 0)"
+ assert_equal 'http://rubyonrails.com', connection.select_value("SELECT url FROM 'references' WHERE id=1")
+ ensure
+ return unless renamed
+ connection.rename_table :references, :test_models
+ connection.rename_table :old_references, :references
+ end
+
+ def test_rename_table
+ rename_table :test_models, :octopi
+
+ # Using explicit id in insert for compatibility across all databases
+ con = connection
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+
+ con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
+
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
+
+ rename_table :octopi, :test_models
+ end
+
+ def test_rename_table_with_an_index
+ add_index :test_models, :url
+
+ rename_table :test_models, :octopi
+
+ # Using explicit id in insert for compatibility across all databases
+ con = ActiveRecord::Base.connection
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+ con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
+ assert connection.indexes(:octopi).first.columns.include?("url")
+
+ rename_table :octopi, :test_models
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/table_and_index_test.rb b/activerecord/test/cases/migration/table_and_index_test.rb
new file mode 100644
index 0000000000..8fd770abd1
--- /dev/null
+++ b/activerecord/test/cases/migration/table_and_index_test.rb
@@ -0,0 +1,24 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class TableAndIndexTest < ActiveRecord::TestCase
+ def test_add_schema_info_respects_prefix_and_suffix
+ conn = ActiveRecord::Base.connection
+
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
+ # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ ActiveRecord::Base.table_name_suffix = '_s'
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
+
+ conn.initialize_schema_migrations_table
+
+ assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 49944eced9..92dc150104 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "cases/migration/helper"
require 'bigdecimal/util'
require 'models/person'
@@ -6,2273 +7,941 @@ require 'models/topic'
require 'models/developer'
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
+require MIGRATIONS_ROOT + "/rename/1_we_need_things"
+require MIGRATIONS_ROOT + "/rename/2_rename_things"
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
-if ActiveRecord::Base.connection.supports_migrations?
- class BigNumber < ActiveRecord::Base; end
+class BigNumber < ActiveRecord::Base; end
- class Reminder < ActiveRecord::Base; end
+class Reminder < ActiveRecord::Base; end
- class ActiveRecord::Migration
- class << self
- attr_accessor :message_count
- end
-
- def puts(text="")
- ActiveRecord::Migration.message_count ||= 0
- ActiveRecord::Migration.message_count += 1
- end
- end
-
- class MigrationTableAndIndexTest < ActiveRecord::TestCase
- def test_add_schema_info_respects_prefix_and_suffix
- conn = ActiveRecord::Base.connection
+class Thing < ActiveRecord::Base; end
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
- # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
- ActiveRecord::Base.table_name_prefix = 'p_'
- ActiveRecord::Base.table_name_suffix = '_s'
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
+class MigrationTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
- conn.initialize_schema_migrations_table
+ fixtures :people
- assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
+ def setup
+ super
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
+ Reminder.connection.drop_table(table) rescue nil
end
+ Reminder.reset_column_information
+ ActiveRecord::Migration.verbose = true
+ ActiveRecord::Migration.message_count = 0
end
- class MigrationTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false
-
- fixtures :people
-
- def setup
- ActiveRecord::Migration.verbose = true
- ActiveRecord::Migration.message_count = 0
- end
-
- def teardown
- ActiveRecord::Base.connection.initialize_schema_migrations_table
- ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
-
- %w(reminders people_reminders prefix_reminders_suffix).each do |table|
- Reminder.connection.drop_table(table) rescue nil
- end
- Reminder.reset_column_information
-
- %w(last_name key bio age height wealth birthday favorite_day
- moment_of_truth male administrator funny).each do |column|
- Person.connection.remove_column('people', column) rescue nil
- end
- Person.connection.remove_column("people", "first_name") rescue nil
- Person.connection.remove_column("people", "middle_name") rescue nil
- Person.connection.add_column("people", "first_name", :string, :limit => 40)
- Person.reset_column_information
- end
-
- def test_add_index
- # Limit size of last_name and key columns to support Firebird index limitations
- Person.connection.add_column "people", "last_name", :string, :limit => 100
- Person.connection.add_column "people", "key", :string, :limit => 100
- Person.connection.add_column "people", "administrator", :boolean
-
- assert_nothing_raised { Person.connection.add_index("people", "last_name") }
- assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
-
- # Orcl nds shrt indx nms. Sybs 2.
- # OpenBase does not have named indexes. You must specify a single column name
- unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
- # Oracle adapter cannot have specified index name larger than 30 characters
- # Oracle adapter is shortening index name when just column list is given
- unless current_adapter?(:OracleAdapter)
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", :name => :index_people_on_last_name_and_first_name) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
- end
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
- assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
- end
-
- # quoting
- # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
- # OpenBase does not have named indexes. You must specify a single column name
- unless current_adapter?(:OpenBaseAdapter)
- Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
- assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
- assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
- end
-
- # Sybase adapter does not support indexes on :boolean columns
- # OpenBase does not have named indexes. You must specify a single column
- unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
- assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
- assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
- end
- end
-
- def test_index_symbol_names
- assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
- assert Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
- assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
- assert !Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
- end
-
- def test_add_index_length_limit
- good_index_name = 'x' * Person.connection.index_name_length
- too_long_index_name = good_index_name + 'x'
- assert_raise(ArgumentError) { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
- assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
- assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
- assert Person.connection.index_name_exists?("people", good_index_name, false)
- Person.connection.remove_index("people", :name => good_index_name)
- end
-
- def test_remove_nonexistent_index
- # we do this by name, so OpenBase is a wash as noted above
- unless current_adapter?(:OpenBaseAdapter)
- assert_raise(ArgumentError) { Person.connection.remove_index("people", "no_such_index") }
- end
- end
-
- def test_rename_index
- unless current_adapter?(:OpenBaseAdapter)
- # keep the names short to make Oracle and similar behave
- Person.connection.add_index('people', [:first_name], :name => 'old_idx')
- assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
- # if the adapter doesn't support the indexes call, pick defaults that let the test pass
- assert !Person.connection.index_name_exists?('people', 'old_idx', false)
- assert Person.connection.index_name_exists?('people', 'new_idx', true)
- end
- end
-
- def test_double_add_index
- unless current_adapter?(:OpenBaseAdapter)
- Person.connection.add_index('people', [:first_name], :name => 'some_idx')
- assert_raise(ArgumentError) { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
- end
- end
-
- def test_index_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :string, :limit => 100
- end
- Person.connection.add_index :testings, :foo
-
- assert Person.connection.index_exists?(:testings, :foo)
- assert !Person.connection.index_exists?(:testings, :bar)
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_index_exists_on_multiple_columns
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :string, :limit => 100
- end
- Person.connection.add_index :testings, [:foo, :bar]
-
- assert Person.connection.index_exists?(:testings, [:foo, :bar])
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_unique_index_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- end
- Person.connection.add_index :testings, :foo, :unique => true
-
- assert Person.connection.index_exists?(:testings, :foo, :unique => true)
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_named_index_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- end
- Person.connection.add_index :testings, :foo, :name => "custom_index_name"
-
- assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name")
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def testing_table_with_only_foo_attribute
- Person.connection.create_table :testings, :id => false do |t|
- t.column :foo, :string
- end
+ def teardown
+ ActiveRecord::Base.connection.initialize_schema_migrations_table
+ ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
- yield Person.connection
- ensure
- Person.connection.drop_table :testings rescue nil
- end
- protected :testing_table_with_only_foo_attribute
-
- def test_create_table_without_id
- testing_table_with_only_foo_attribute do |connection|
- assert_equal connection.columns(:testings).size, 1
- end
+ %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table|
+ Thing.connection.drop_table(table) rescue nil
end
+ Thing.reset_column_information
- def test_add_column_with_primary_key_attribute
- testing_table_with_only_foo_attribute do |connection|
- assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
- assert_equal connection.columns(:testings).size, 2
- end
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
+ Reminder.connection.drop_table(table) rescue nil
end
+ Reminder.reset_column_information
- def test_create_table_adds_id
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- assert_equal %w(foo id),
- Person.connection.columns(:testings).map { |c| c.name }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
+ %w(last_name key bio age height wealth birthday favorite_day
+ moment_of_truth male administrator funny).each do |column|
+ Person.connection.remove_column('people', column) rescue nil
end
+ Person.connection.remove_column("people", "first_name") rescue nil
+ Person.connection.remove_column("people", "middle_name") rescue nil
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
+ Person.reset_column_information
+ end
- def test_create_table_with_not_null_column
- assert_nothing_raised do
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :null => false
- end
- end
-
- assert_raise(ActiveRecord::StatementInvalid) do
- Person.connection.execute "insert into testings (foo) values (NULL)"
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_create_table_with_defaults
- # MySQL doesn't allow defaults on TEXT or BLOB columns.
- mysql = current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
-
- Person.connection.create_table :testings do |t|
- t.column :one, :string, :default => "hello"
- t.column :two, :boolean, :default => true
- t.column :three, :boolean, :default => false
- t.column :four, :integer, :default => 1
- t.column :five, :text, :default => "hello" unless mysql
- end
-
- columns = Person.connection.columns(:testings)
- one = columns.detect { |c| c.name == "one" }
- two = columns.detect { |c| c.name == "two" }
- three = columns.detect { |c| c.name == "three" }
- four = columns.detect { |c| c.name == "four" }
- five = columns.detect { |c| c.name == "five" } unless mysql
-
- assert_equal "hello", one.default
- assert_equal true, two.default
- assert_equal false, three.default
- assert_equal 1, four.default
- assert_equal "hello", five.default unless mysql
-
- ensure
- Person.connection.drop_table :testings rescue nil
+ def test_create_table_with_force_true_does_not_drop_nonexisting_table
+ if Person.connection.table_exists?(:testings2)
+ Person.connection.drop_table :testings2
end
- def test_create_table_with_limits
- assert_nothing_raised do
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 255
+ # using a copy as we need the drop_table method to
+ # continue to work for the ensure block of the test
+ temp_conn = Person.connection.dup
- t.column :default_int, :integer
+ assert_not_equal temp_conn, Person.connection
- t.column :one_int, :integer, :limit => 1
- t.column :four_int, :integer, :limit => 4
- t.column :eight_int, :integer, :limit => 8
- t.column :eleven_int, :integer, :limit => 11
- end
- end
-
- columns = Person.connection.columns(:testings)
- foo = columns.detect { |c| c.name == "foo" }
- assert_equal 255, foo.limit
-
- default = columns.detect { |c| c.name == "default_int" }
- one = columns.detect { |c| c.name == "one_int" }
- four = columns.detect { |c| c.name == "four_int" }
- eight = columns.detect { |c| c.name == "eight_int" }
- eleven = columns.detect { |c| c.name == "eleven_int" }
-
- if current_adapter?(:PostgreSQLAdapter)
- assert_equal 'integer', default.sql_type
- assert_equal 'smallint', one.sql_type
- assert_equal 'integer', four.sql_type
- assert_equal 'bigint', eight.sql_type
- assert_equal 'integer', eleven.sql_type
- elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- assert_match 'int(11)', default.sql_type
- assert_match 'tinyint', one.sql_type
- assert_match 'int', four.sql_type
- assert_match 'bigint', eight.sql_type
- assert_match 'int(11)', eleven.sql_type
- elsif current_adapter?(:OracleAdapter)
- assert_equal 'NUMBER(38)', default.sql_type
- assert_equal 'NUMBER(1)', one.sql_type
- assert_equal 'NUMBER(4)', four.sql_type
- assert_equal 'NUMBER(8)', eight.sql_type
- end
- ensure
- Person.connection.drop_table :testings rescue nil
+ temp_conn.create_table :testings2, :force => true do |t|
+ t.column :foo, :string
end
+ ensure
+ Person.connection.drop_table :testings2 rescue nil
+ end
- def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
- ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
-
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- assert_equal %w(foo testing_id), Person.connection.columns(:testings).map { |c| c.name }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- ActiveRecord::Base.primary_key_prefix_type = nil
- end
+ def connection
+ ActiveRecord::Base.connection
+ end
- def test_create_table_with_primary_key_prefix_as_table_name
- ActiveRecord::Base.primary_key_prefix_type = :table_name
+ def test_migration_instance_has_connection
+ migration = Class.new(ActiveRecord::Migration).new
+ assert_equal connection, migration.connection
+ end
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
+ def test_method_missing_delegates_to_connection
+ migration = Class.new(ActiveRecord::Migration) {
+ def connection
+ Class.new {
+ def create_table; "hi mom!"; end
+ }.new
end
+ }.new
- assert_equal %w(foo testingid), Person.connection.columns(:testings).map { |c| c.name }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- ActiveRecord::Base.primary_key_prefix_type = nil
- end
-
- def test_create_table_with_force_true_does_not_drop_nonexisting_table
- if Person.connection.table_exists?(:testings2)
- Person.connection.drop_table :testings2
- end
+ assert_equal "hi mom!", migration.method_missing(:create_table)
+ end
- # using a copy as we need the drop_table method to
- # continue to work for the ensure block of the test
- temp_conn = Person.connection.dup
- temp_conn.expects(:drop_table).never
- temp_conn.create_table :testings2, :force => true do |t|
- t.column :foo, :string
- end
- ensure
- Person.connection.drop_table :testings2 rescue nil
+ def test_add_table_with_decimals
+ Person.connection.drop_table :big_numbers rescue nil
+
+ assert !BigNumber.table_exists?
+ GiveMeBigNumbers.up
+
+ assert BigNumber.create(
+ :bank_balance => 1586.43,
+ :big_bank_balance => BigDecimal("1000234000567.95"),
+ :world_population => 6000000000,
+ :my_house_population => 3,
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
+ )
+
+ b = BigNumber.find(:first)
+ assert_not_nil b
+
+ assert_not_nil b.bank_balance
+ assert_not_nil b.big_bank_balance
+ assert_not_nil b.world_population
+ assert_not_nil b.my_house_population
+ assert_not_nil b.value_of_e
+
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
+ # is_a?(Bignum)
+ assert_kind_of Integer, b.world_population
+ assert_equal 6000000000, b.world_population
+ assert_kind_of Fixnum, b.my_house_population
+ assert_equal 3, b.my_house_population
+ assert_kind_of BigDecimal, b.bank_balance
+ assert_equal BigDecimal("1586.43"), b.bank_balance
+ assert_kind_of BigDecimal, b.big_bank_balance
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
+
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
+ # precision/scale explicitly left out. By the SQL standard, numbers
+ # assigned to this field should be truncated but that's seldom respected.
+ if current_adapter?(:PostgreSQLAdapter)
+ # - PostgreSQL changes the SQL spec on columns declared simply as
+ # "decimal" to something more useful: instead of being given a scale
+ # of 0, they take on the compile-time limit for precision and scale,
+ # so the following should succeed unless you have used really wacky
+ # compilation options
+ # - SQLite2 has the default behavior of preserving all data sent in,
+ # so this happens there too
+ assert_kind_of BigDecimal, b.value_of_e
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
+ elsif current_adapter?(:SQLite3Adapter)
+ # - SQLite3 stores a float, in violation of SQL
+ assert_kind_of BigDecimal, b.value_of_e
+ assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
+ else
+ # - SQL standard is an integer
+ assert_kind_of Fixnum, b.value_of_e
+ assert_equal 2, b.value_of_e
end
- def test_create_table_with_timestamps_should_create_datetime_columns
- table_name = :testings
+ GiveMeBigNumbers.down
+ assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
+ end
- Person.connection.create_table table_name do |t|
- t.timestamps
- end
- created_columns = Person.connection.columns(table_name)
+ def test_filtering_migrations
+ assert !Person.column_methods_hash.include?(:last_name)
+ assert !Reminder.table_exists?
- created_at_column = created_columns.detect {|c| c.name == 'created_at' }
- updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+ name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
- assert !created_at_column.null
- assert !updated_at_column.null
- ensure
- Person.connection.drop_table table_name rescue nil
- end
-
- def test_create_table_with_timestamps_should_create_datetime_columns_with_options
- table_name = :testings
+ Person.reset_column_information
+ assert Person.column_methods_hash.include?(:last_name)
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- Person.connection.create_table table_name do |t|
- t.timestamps :null => false
- end
- created_columns = Person.connection.columns(table_name)
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", &name_filter)
- created_at_column = created_columns.detect {|c| c.name == 'created_at' }
- updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+ Person.reset_column_information
+ assert !Person.column_methods_hash.include?(:last_name)
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+ end
- assert !created_at_column.null
- assert !updated_at_column.null
- ensure
- Person.connection.drop_table table_name rescue nil
+ class MockMigration < ActiveRecord::Migration
+ attr_reader :went_up, :went_down
+ def initialize
+ @went_up = false
+ @went_down = false
end
- def test_create_table_without_a_block
- table_name = :testings
- Person.connection.create_table table_name
- ensure
- Person.connection.drop_table table_name rescue nil
+ def up
+ @went_up = true
+ super
end
- # Sybase, and SQLite3 will not allow you to add a NOT NULL
- # column to a table without a default value.
- unless current_adapter?(:SybaseAdapter, :SQLite3Adapter)
- def test_add_column_not_null_without_default
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
- Person.connection.add_column :testings, :bar, :string, :null => false
-
- assert_raise(ActiveRecord::StatementInvalid) do
- Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
+ def down
+ @went_down = true
+ super
end
+ end
- def test_add_column_not_null_with_default
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- con = Person.connection
- Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
- Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
- Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
- assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
-
- assert_raise(ActiveRecord::StatementInvalid) do
- unless current_adapter?(:OpenBaseAdapter)
- Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
- else
- Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
- "Testing Insert","id",2)
- end
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- # We specifically do a manual INSERT here, and then test only the SELECT
- # functionality. This allows us to more easily catch INSERT being broken,
- # but SELECT actually working fine.
- def test_native_decimal_insert_manual_vs_automatic
- correct_value = '0012345678901234567890.0123456789'.to_d
-
- Person.delete_all
- Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
- Person.reset_column_information
-
- # Do a manual insertion
- if current_adapter?(:OracleAdapter)
- Person.connection.execute "insert into people (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, 0, 0)"
- elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
- Person.connection.execute "insert into people (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)"
- elsif current_adapter?(:PostgreSQLAdapter)
- Person.connection.execute "insert into people (wealth, created_at, updated_at) values (12345678901234567890.0123456789, now(), now())"
- else
- Person.connection.execute "insert into people (wealth, created_at, updated_at) values (12345678901234567890.0123456789, 0, 0)"
- end
-
- # SELECT
- row = Person.find(:first)
- assert_kind_of BigDecimal, row.wealth
-
- # If this assert fails, that means the SELECT is broken!
- unless current_adapter?(:SQLite3Adapter)
- assert_equal correct_value, row.wealth
- end
-
- # Reset to old state
- Person.delete_all
-
- # Now use the Rails insertion
- assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
-
- # SELECT
- row = Person.find(:first)
- assert_kind_of BigDecimal, row.wealth
+ def test_instance_based_migration_up
+ migration = MockMigration.new
+ assert !migration.went_up, 'have not gone up'
+ assert !migration.went_down, 'have not gone down'
- # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
- unless current_adapter?(:SQLite3Adapter)
- assert_equal correct_value, row.wealth
- end
+ migration.migrate :up
+ assert migration.went_up, 'have gone up'
+ assert !migration.went_down, 'have not gone down'
+ end
- # Reset to old state
- Person.connection.del_column "people", "wealth" rescue nil
- Person.reset_column_information
- end
+ def test_instance_based_migration_down
+ migration = MockMigration.new
+ assert !migration.went_up, 'have not gone up'
+ assert !migration.went_down, 'have not gone down'
- def test_add_column_with_precision_and_scale
- Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
- Person.reset_column_information
+ migration.migrate :down
+ assert !migration.went_up, 'have gone up'
+ assert migration.went_down, 'have not gone down'
+ end
- wealth_column = Person.columns_hash['wealth']
- assert_equal 9, wealth_column.precision
- assert_equal 7, wealth_column.scale
+ def test_migrator_one_up_with_exception_and_rollback
+ unless ActiveRecord::Base.connection.supports_ddl_transactions?
+ skip "not supported on #{ActiveRecord::Base.connection.class}"
end
- # Test SQLite adapter specifically for decimal types with precision and scale
- # attributes, since these need to be maintained in schema but aren't actually
- # used in SQLite itself
- if current_adapter?(:SQLite3Adapter)
- def test_change_column_with_new_precision_and_scale
- Person.delete_all
- Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
- Person.reset_column_information
+ refute Person.column_methods_hash.include?(:last_name)
- Person.connection.change_column 'people', 'wealth', :decimal, :precision => 12, :scale => 8
- Person.reset_column_information
+ migration = Struct.new(:name, :version) {
+ def migrate(x); raise 'Something broke'; end
+ }.new('zomg', 100)
- wealth_column = Person.columns_hash['wealth']
- assert_equal 12, wealth_column.precision
- assert_equal 8, wealth_column.scale
- end
-
- def test_change_column_preserve_other_column_precision_and_scale
- Person.delete_all
- Person.connection.add_column 'people', 'last_name', :string
- Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
- Person.reset_column_information
-
- wealth_column = Person.columns_hash['wealth']
- assert_equal 9, wealth_column.precision
- assert_equal 7, wealth_column.scale
-
- Person.connection.change_column 'people', 'last_name', :string, :null => false
- Person.reset_column_information
-
- wealth_column = Person.columns_hash['wealth']
- assert_equal 9, wealth_column.precision
- assert_equal 7, wealth_column.scale
- end
- end
+ migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
- def test_native_types
- Person.delete_all
- Person.connection.add_column "people", "last_name", :string
- Person.connection.add_column "people", "bio", :text
- Person.connection.add_column "people", "age", :integer
- Person.connection.add_column "people", "height", :float
- Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
- Person.connection.add_column "people", "birthday", :datetime
- Person.connection.add_column "people", "favorite_day", :date
- Person.connection.add_column "people", "moment_of_truth", :datetime
- Person.connection.add_column "people", "male", :boolean
- Person.reset_column_information
-
- assert_nothing_raised do
- Person.create :first_name => 'bob', :last_name => 'bobsen',
- :bio => "I was born ....", :age => 18, :height => 1.78,
- :wealth => BigDecimal.new("12345678901234567890.0123456789"),
- :birthday => 18.years.ago, :favorite_day => 10.days.ago,
- :moment_of_truth => "1782-10-10 21:40:18", :male => true
- end
+ e = assert_raise(StandardError) { migrator.migrate }
- bob = Person.find(:first)
- assert_equal 'bob', bob.first_name
- assert_equal 'bobsen', bob.last_name
- assert_equal "I was born ....", bob.bio
- assert_equal 18, bob.age
+ assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
- # Test for 30 significant digits (beyond the 16 of float), 10 of them
- # after the decimal place.
-
- unless current_adapter?(:SQLite3Adapter)
- assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
- end
-
- assert_equal true, bob.male?
+ Person.reset_column_information
+ refute Person.column_methods_hash.include?(:last_name)
+ end
- assert_equal String, bob.first_name.class
- assert_equal String, bob.last_name.class
- assert_equal String, bob.bio.class
- assert_equal Fixnum, bob.age.class
- assert_equal Time, bob.birthday.class
+ def test_schema_migrations_table_name
+ ActiveRecord::Base.table_name_prefix = "prefix_"
+ ActiveRecord::Base.table_name_suffix = "_suffix"
+ Reminder.reset_table_name
+ assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ Reminder.reset_table_name
+ assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ end
- if current_adapter?(:OracleAdapter, :SybaseAdapter)
- # Sybase, and Oracle don't differentiate between date/time
- assert_equal Time, bob.favorite_day.class
- else
- assert_equal Date, bob.favorite_day.class
- end
+ def test_proper_table_name
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
+ assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
+ Reminder.reset_table_name
+ assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
+
+ # Use the model's own prefix/suffix if a model is given
+ ActiveRecord::Base.table_name_prefix = "ARprefix_"
+ ActiveRecord::Base.table_name_suffix = "_ARsuffix"
+ Reminder.table_name_prefix = 'prefix_'
+ Reminder.table_name_suffix = '_suffix'
+ Reminder.reset_table_name
+ assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
+ Reminder.table_name_prefix = ''
+ Reminder.table_name_suffix = ''
+ Reminder.reset_table_name
+
+ # Use AR::Base's prefix/suffix if string or symbol is given
+ ActiveRecord::Base.table_name_prefix = "prefix_"
+ ActiveRecord::Base.table_name_suffix = "_suffix"
+ Reminder.reset_table_name
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ Reminder.reset_table_name
+ end
- # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
- # therefore no timezone change is done afterwards when default timezone is changed
- unless current_adapter?(:OracleAdapter)
- # Test DateTime column and defaults, including timezone.
- # FIXME: moment of truth may be Time on 64-bit platforms.
- if bob.moment_of_truth.is_a?(DateTime)
-
- with_env_tz 'US/Eastern' do
- bob.reload
- assert_equal DateTime.local_offset, bob.moment_of_truth.offset
- assert_not_equal 0, bob.moment_of_truth.offset
- assert_not_equal "Z", bob.moment_of_truth.zone
- # US/Eastern is -5 hours from GMT
- assert_equal Rational(-5, 24), bob.moment_of_truth.offset
- assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
- assert_equal DateTime::ITALY, bob.moment_of_truth.start
- end
- end
- end
+ def test_rename_table_with_prefix_and_suffix
+ assert !Thing.table_exists?
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
+ ActiveRecord::Base.table_name_suffix = '_suffix'
+ Thing.reset_table_name
+ Thing.reset_sequence_name
+ WeNeedThings.up
+
+ assert Thing.create("content" => "hello world")
+ assert_equal "hello world", Thing.find(:first).content
+
+ RenameThings.up
+ Thing.table_name = "prefix_awesome_things_suffix"
+
+ assert_equal "hello world", Thing.find(:first).content
+ ensure
+ ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_suffix = ''
+ Thing.reset_table_name
+ Thing.reset_sequence_name
+ end
- assert_instance_of TrueClass, bob.male?
- assert_kind_of BigDecimal, bob.wealth
- end
+ def test_add_drop_table_with_prefix_and_suffix
+ assert !Reminder.table_exists?
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
+ ActiveRecord::Base.table_name_suffix = '_suffix'
+ Reminder.reset_table_name
+ Reminder.reset_sequence_name
+ WeNeedReminders.up
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+ assert_equal "hello world", Reminder.find(:first).content
+
+ WeNeedReminders.down
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+ ensure
+ ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_suffix = ''
+ Reminder.reset_table_name
+ Reminder.reset_sequence_name
+ end
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- def test_unabstracted_database_dependent_types
- Person.delete_all
+ def test_create_table_with_binary_column
+ Person.connection.drop_table :binary_testings rescue nil
- ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
- Person.reset_column_information
- assert_match(/tinyint/, Person.columns_hash['intelligence_quotient'].sql_type)
- ensure
- ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
+ assert_nothing_raised {
+ Person.connection.create_table :binary_testings do |t|
+ t.column "data", :binary, :null => false
end
- end
-
- def test_add_remove_single_field_using_string_arguments
- assert !Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.add_column 'people', 'last_name', :string
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.remove_column 'people', 'last_name'
-
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- end
+ }
- def test_add_remove_single_field_using_symbol_arguments
- assert !Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.add_column :people, :last_name, :string
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.remove_column :people, :last_name
-
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- end
+ columns = Person.connection.columns(:binary_testings)
+ data_column = columns.detect { |c| c.name == "data" }
if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- def testing_table_for_positioning
- Person.connection.create_table :testings, :id => false do |t|
- t.column :first, :integer
- t.column :second, :integer
- t.column :third, :integer
- end
-
- yield Person.connection
- ensure
- Person.connection.drop_table :testings rescue nil
- end
- protected :testing_table_for_positioning
-
- def test_column_positioning
- testing_table_for_positioning do |conn|
- assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
- end
- end
-
- def test_add_column_with_positioning
- testing_table_for_positioning do |conn|
- conn.add_column :testings, :new_col, :integer
- assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
- end
- testing_table_for_positioning do |conn|
- conn.add_column :testings, :new_col, :integer, :first => true
- assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
- end
- testing_table_for_positioning do |conn|
- conn.add_column :testings, :new_col, :integer, :after => :first
- assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
- end
- end
-
- def test_change_column_with_positioning
- testing_table_for_positioning do |conn|
- conn.change_column :testings, :second, :integer, :first => true
- assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
- end
- testing_table_for_positioning do |conn|
- conn.change_column :testings, :second, :integer, :after => :third
- assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
- end
- end
- end
-
- def test_add_rename
- Person.delete_all
-
- begin
- Person.connection.add_column "people", "girlfriend", :string
- Person.reset_column_information
- Person.create :girlfriend => 'bobette'
-
- Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
-
- Person.reset_column_information
- bob = Person.find(:first)
-
- assert_equal "bobette", bob.exgirlfriend
- ensure
- Person.connection.remove_column("people", "girlfriend") rescue nil
- Person.connection.remove_column("people", "exgirlfriend") rescue nil
- end
-
- end
-
- def test_rename_column_using_symbol_arguments
- begin
- names_before = Person.find(:all).map(&:first_name)
- Person.connection.rename_column :people, :first_name, :nick_name
- Person.reset_column_information
- assert Person.column_names.include?("nick_name")
- assert_equal names_before, Person.find(:all).map(&:nick_name)
- ensure
- Person.connection.remove_column("people","nick_name")
- Person.connection.add_column("people","first_name", :string)
- end
- end
-
- def test_rename_column
- begin
- names_before = Person.find(:all).map(&:first_name)
- Person.connection.rename_column "people", "first_name", "nick_name"
- Person.reset_column_information
- assert Person.column_names.include?("nick_name")
- assert_equal names_before, Person.find(:all).map(&:nick_name)
- ensure
- Person.connection.remove_column("people","nick_name")
- Person.connection.add_column("people","first_name", :string)
- end
- end
-
- def test_rename_column_preserves_default_value_not_null
- begin
- default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
- assert_equal 70000, default_before
- Developer.connection.rename_column "developers", "salary", "anual_salary"
- Developer.reset_column_information
- assert Developer.column_names.include?("anual_salary")
- default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
- assert_equal 70000, default_after
- ensure
- Developer.connection.rename_column "developers", "anual_salary", "salary"
- Developer.reset_column_information
- end
- end
-
- def test_rename_nonexistent_column
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :default => nil
- end
- exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
- ActiveRecord::StatementInvalid
- else
- ActiveRecord::ActiveRecordError
- end
- assert_raise(exception) do
- Person.connection.rename_column "hats", "nonexistent", "should_fail"
- end
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_rename_column_with_sql_reserved_word
- begin
- assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
- Person.reset_column_information
- assert Person.column_names.include?("group")
- ensure
- Person.connection.remove_column("people", "group") rescue nil
- Person.connection.add_column("people", "first_name", :string) rescue nil
- end
- end
-
- def test_rename_column_with_an_index
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :limit => 100
- table.column :hat_size, :integer
- end
- Person.connection.add_index :hats, :hat_name
- assert_nothing_raised do
- Person.connection.rename_column "hats", "hat_name", "name"
- end
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_remove_column_with_index
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :limit => 100
- table.column :hat_size, :integer
- end
- ActiveRecord::Base.connection.add_index "hats", "hat_size"
-
- assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_remove_column_with_multi_column_index
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :limit => 100
- table.column :hat_size, :integer
- table.column :hat_style, :string, :limit => 100
- end
- ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
-
- assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_remove_column_no_second_parameter_raises_exception
- assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
+ assert_equal '', data_column.default
+ else
+ assert_nil data_column.default
end
- def test_change_type_of_not_null_column
- assert_nothing_raised do
- Topic.connection.change_column "topics", "written_on", :datetime, :null => false
- Topic.reset_column_information
-
- Topic.connection.change_column "topics", "written_on", :datetime, :null => false
- Topic.reset_column_information
+ Person.connection.drop_table :binary_testings rescue nil
+ end
- Topic.connection.change_column "topics", "written_on", :datetime, :null => true
- Topic.reset_column_information
- end
- end
+ def test_create_table_with_custom_sequence_name
+ skip "not supported" unless current_adapter? :OracleAdapter
- if current_adapter?(:SQLite3Adapter)
- def test_rename_table_for_sqlite_should_work_with_reserved_words
- begin
- assert_nothing_raised do
- ActiveRecord::Base.connection.rename_table :references, :old_references
- ActiveRecord::Base.connection.create_table :octopuses do |t|
- t.column :url, :string
- end
- end
-
- assert_nothing_raised { ActiveRecord::Base.connection.rename_table :octopuses, :references }
-
- # Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- assert_nothing_raised do
- con.execute "INSERT INTO 'references' (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://rubyonrails.com')"
- end
- assert_equal 'http://rubyonrails.com', ActiveRecord::Base.connection.select_value("SELECT url FROM 'references' WHERE id=1")
-
- ensure
- ActiveRecord::Base.connection.drop_table :references
- ActiveRecord::Base.connection.rename_table :old_references, :references
- end
- end
- end
-
- def test_rename_table
+ # table name is 29 chars, the standard sequence name will
+ # be 33 chars and should be shortened
+ assert_nothing_raised do
begin
- ActiveRecord::Base.connection.create_table :octopuses do |t|
- t.column :url, :string
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
+ t.column :foo, :string, :null => false
end
- ActiveRecord::Base.connection.rename_table :octopuses, :octopi
-
- # Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
- assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
- con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
-
- assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
-
ensure
- ActiveRecord::Base.connection.drop_table :octopuses rescue nil
- ActiveRecord::Base.connection.drop_table :octopi rescue nil
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
end
end
- def test_change_column_nullability
- Person.delete_all
- Person.connection.add_column "people", "funny", :boolean
- Person.reset_column_information
- assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
- Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
- Person.reset_column_information
- assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
- Person.connection.change_column "people", "funny", :boolean, :null => true
- Person.reset_column_information
- assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
- end
-
- def test_rename_table_with_an_index
+ # should be all good w/ a custom sequence name
+ assert_nothing_raised do
begin
- ActiveRecord::Base.connection.create_table :octopuses do |t|
- t.column :url, :string
+ Person.connection.create_table :table_with_name_thats_just_ok,
+ :sequence_name => 'suitably_short_seq' do |t|
+ t.column :foo, :string, :null => false
end
- ActiveRecord::Base.connection.add_index :octopuses, :url
- ActiveRecord::Base.connection.rename_table :octopuses, :octopi
-
- # Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
- assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
- con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
- assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
- assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
ensure
- ActiveRecord::Base.connection.drop_table :octopuses rescue nil
- ActiveRecord::Base.connection.drop_table :octopi rescue nil
- end
- end
-
- def test_change_column
- Person.connection.add_column 'people', 'age', :integer
- label = "test_change_column Columns"
- old_columns = Person.connection.columns(Person.table_name, label)
- assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
-
- assert_nothing_raised { Person.connection.change_column "people", "age", :string }
-
- new_columns = Person.connection.columns(Person.table_name, label)
- assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
- assert new_columns.find { |c| c.name == 'age' and c.type == :string }
-
- old_columns = Topic.connection.columns(Topic.table_name, label)
- assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
- assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
- new_columns = Topic.connection.columns(Topic.table_name, label)
- assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
- assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
- assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
- end
-
- def test_change_column_with_nil_default
- Person.connection.add_column "people", "contributor", :boolean, :default => true
- Person.reset_column_information
- assert Person.new.contributor?
-
- assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
- Person.reset_column_information
- assert !Person.new.contributor?
- assert_nil Person.new.contributor
- ensure
- Person.connection.remove_column("people", "contributor") rescue nil
- end
-
- def test_change_column_with_new_default
- Person.connection.add_column "people", "administrator", :boolean, :default => true
- Person.reset_column_information
- assert Person.new.administrator?
-
- assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
- Person.reset_column_information
- assert !Person.new.administrator?
- ensure
- Person.connection.remove_column("people", "administrator") rescue nil
- end
-
- def test_change_column_default
- Person.connection.change_column_default "people", "first_name", "Tester"
- Person.reset_column_information
- assert_equal "Tester", Person.new.first_name
- end
-
- def test_change_column_quotes_column_names
- Person.connection.create_table :testings do |t|
- t.column :select, :string
- end
-
- assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
-
- # Oracle needs primary key value from sequence
- if current_adapter?(:OracleAdapter)
- assert_nothing_raised { Person.connection.execute "insert into testings (id, #{Person.connection.quote_column_name('select')}) values (testings_seq.nextval, '7 chars')" }
- else
- assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_keeping_default_and_notnull_constaint_on_change
- Person.connection.create_table :testings do |t|
- t.column :title, :string
- end
- person_klass = Class.new(Person)
- person_klass.set_table_name 'testings'
-
- person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
- person_klass.reset_column_information
- assert_equal 99, person_klass.columns_hash["wealth"].default
- assert_equal false, person_klass.columns_hash["wealth"].null
- # Oracle needs primary key value from sequence
- if current_adapter?(:OracleAdapter)
- assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
- else
- assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
- end
-
- # change column default to see that column doesn't lose its not null definition
- person_klass.connection.change_column_default "testings", "wealth", 100
- person_klass.reset_column_information
- assert_equal 100, person_klass.columns_hash["wealth"].default
- assert_equal false, person_klass.columns_hash["wealth"].null
-
- # rename column to see that column doesn't lose its not null and/or default definition
- person_klass.connection.rename_column "testings", "wealth", "money"
- person_klass.reset_column_information
- assert_nil person_klass.columns_hash["wealth"]
- assert_equal 100, person_klass.columns_hash["money"].default
- assert_equal false, person_klass.columns_hash["money"].null
-
- # change column
- person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
- person_klass.reset_column_information
- assert_equal 1000, person_klass.columns_hash["money"].default
- assert_equal false, person_klass.columns_hash["money"].null
-
- # change column, make it nullable and clear default
- person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
- person_klass.reset_column_information
- assert_nil person_klass.columns_hash["money"].default
- assert_equal true, person_klass.columns_hash["money"].null
-
- # change_column_null, make it not nullable and set null values to a default value
- person_klass.connection.execute('UPDATE testings SET money = NULL')
- person_klass.connection.change_column_null "testings", "money", false, 2000
- person_klass.reset_column_information
- assert_nil person_klass.columns_hash["money"].default
- assert_equal false, person_klass.columns_hash["money"].null
- assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_change_column_default_to_null
- Person.connection.change_column_default "people", "first_name", nil
- Person.reset_column_information
- assert_nil Person.new.first_name
- end
-
- def test_column_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
+ Person.connection.drop_table :table_with_name_thats_just_ok,
+ :sequence_name => 'suitably_short_seq' rescue nil
end
-
- assert Person.connection.column_exists?(:testings, :foo)
- assert !Person.connection.column_exists?(:testings, :bar)
- ensure
- Person.connection.drop_table :testings rescue nil
end
- def test_column_exists_with_type
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- t.column :bar, :decimal, :precision => 8, :scale => 2
- end
-
- assert Person.connection.column_exists?(:testings, :foo, :string)
- assert !Person.connection.column_exists?(:testings, :foo, :integer)
- assert Person.connection.column_exists?(:testings, :bar, :decimal)
- assert !Person.connection.column_exists?(:testings, :bar, :integer)
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_column_exists_with_definition
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :decimal, :precision => 8, :scale => 2
- end
-
- assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100)
- assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50)
- assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
- assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
- ensure
- Person.connection.drop_table :testings rescue nil
+ # confirm the custom sequence got dropped
+ assert_raise(ActiveRecord::StatementInvalid) do
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
end
+ end
- def test_column_exists_on_table_with_no_options_parameter_supplied
- Person.connection.create_table :testings do |t|
- t.string :foo
- end
- Person.connection.change_table :testings do |t|
- assert t.column_exists?(:foo)
- assert !(t.column_exists?(:bar))
- end
+ protected
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_add_table
- assert !Reminder.table_exists?
-
- WeNeedReminders.up
-
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
-
- WeNeedReminders.down
- assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- end
-
- def test_add_table_with_decimals
- Person.connection.drop_table :big_numbers rescue nil
-
- assert !BigNumber.table_exists?
- GiveMeBigNumbers.up
-
- assert BigNumber.create(
- :bank_balance => 1586.43,
- :big_bank_balance => BigDecimal("1000234000567.95"),
- :world_population => 6000000000,
- :my_house_population => 3,
- :value_of_e => BigDecimal("2.7182818284590452353602875")
- )
-
- b = BigNumber.find(:first)
- assert_not_nil b
-
- assert_not_nil b.bank_balance
- assert_not_nil b.big_bank_balance
- assert_not_nil b.world_population
- assert_not_nil b.my_house_population
- assert_not_nil b.value_of_e
-
- # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
- # is_a?(Bignum)
- assert_kind_of Integer, b.world_population
- assert_equal 6000000000, b.world_population
- assert_kind_of Fixnum, b.my_house_population
- assert_equal 3, b.my_house_population
- assert_kind_of BigDecimal, b.bank_balance
- assert_equal BigDecimal("1586.43"), b.bank_balance
- assert_kind_of BigDecimal, b.big_bank_balance
- assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
-
- # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
- # precision/scale explicitly left out. By the SQL standard, numbers
- # assigned to this field should be truncated but that's seldom respected.
- if current_adapter?(:PostgreSQLAdapter)
- # - PostgreSQL changes the SQL spec on columns declared simply as
- # "decimal" to something more useful: instead of being given a scale
- # of 0, they take on the compile-time limit for precision and scale,
- # so the following should succeed unless you have used really wacky
- # compilation options
- # - SQLite2 has the default behavior of preserving all data sent in,
- # so this happens there too
- assert_kind_of BigDecimal, b.value_of_e
- assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
- elsif current_adapter?(:SQLite3Adapter)
- # - SQLite3 stores a float, in violation of SQL
- assert_kind_of BigDecimal, b.value_of_e
- assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
- else
- # - SQL standard is an integer
- assert_kind_of Fixnum, b.value_of_e
- assert_equal 2, b.value_of_e
- end
-
- GiveMeBigNumbers.down
- assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
- end
-
- def test_migrator
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
-
- assert_equal 3, ActiveRecord::Migrator.current_version
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
-
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
-
- assert_equal 0, ActiveRecord::Migrator.current_version
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- end
-
- class MockMigration < ActiveRecord::Migration
- attr_reader :went_up, :went_down
- def initialize
- @went_up = false
- @went_down = false
- end
-
- def up
- @went_up = true
- super
- end
-
- def down
- @went_down = true
- super
- end
- end
-
- def test_instance_based_migration_up
- migration = MockMigration.new
- assert !migration.went_up, 'have not gone up'
- assert !migration.went_down, 'have not gone down'
-
- migration.migrate :up
- assert migration.went_up, 'have gone up'
- assert !migration.went_down, 'have not gone down'
- end
-
- def test_instance_based_migration_down
- migration = MockMigration.new
- assert !migration.went_up, 'have not gone up'
- assert !migration.went_down, 'have not gone down'
-
- migration.migrate :down
- assert !migration.went_up, 'have gone up'
- assert migration.went_down, 'have not gone down'
- end
-
- def test_migrator_one_up
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
-
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
- end
-
- def test_migrator_one_down
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
-
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
end
+end
- def test_migrator_one_up_one_down
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
-
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
- end
-
- def test_migrator_double_up
- assert_equal(0, ActiveRecord::Migrator.current_version)
- ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
- assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
- assert_equal(1, ActiveRecord::Migrator.current_version)
- end
-
- def test_migrator_double_down
- assert_equal(0, ActiveRecord::Migrator.current_version)
- ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
- ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
- assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
- assert_equal(0, ActiveRecord::Migrator.current_version)
- end
-
- if ActiveRecord::Base.connection.supports_ddl_transactions?
- def test_migrator_one_up_with_exception_and_rollback
- assert !Person.column_methods_hash.include?(:last_name)
-
- e = assert_raise(StandardError) do
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
- end
-
- assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
-
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- end
+class ReservedWordsMigrationTest < ActiveRecord::TestCase
+ def test_drop_index_from_table_named_values
+ connection = Person.connection
+ connection.create_table :values, :force => true do |t|
+ t.integer :value
end
- def test_finds_migrations
- migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
-
- [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
- assert_equal migrations[i].version, pair.first
- assert_equal migrations[i].name, pair.last
- end
+ assert_nothing_raised do
+ connection.add_index :values, :value
+ connection.remove_index :values, :column => :value
end
- def test_finds_migrations_from_two_directories
- directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
- migrations = ActiveRecord::Migrator.new(:up, directories).migrations
-
- [[20090101010101, "PeopleHaveHobbies"],
- [20090101010202, "PeopleHaveDescriptions"],
- [20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
- [20100201010101, "ValidWithTimestampsWeNeedReminders"],
- [20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
- assert_equal pair.first, migrations[i].version
- assert_equal pair.last, migrations[i].name
- end
- end
+ connection.drop_table :values rescue nil
+ end
+end
- def test_finds_pending_migrations
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
- migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
- assert_equal 1, migrations.size
- assert_equal migrations[0].version, 3
- assert_equal migrations[0].name, 'InterleavedInnocentJointable'
+class ChangeTableMigrationsTest < ActiveRecord::TestCase
+ def setup
+ @connection = Person.connection
+ @connection.create_table :delete_me, :force => true do |t|
end
+ end
- def test_relative_migrations
- list = Dir.chdir(MIGRATIONS_ROOT) do
- ActiveRecord::Migrator.up("valid/", 1)
- end
+ def teardown
+ Person.connection.drop_table :delete_me rescue nil
+ end
- migration_proxy = list.find { |item|
- item.name == 'ValidPeopleHaveLastNames'
- }
- assert migration_proxy, 'should find pending migration'
+ def test_references_column_type_adds_id
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
+ t.references :customer
end
+ end
- def test_only_loads_pending_migrations
- # migrate up to 1
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
-
- proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
-
- names = proxies.map(&:name)
- assert !names.include?('ValidPeopleHaveLastNames')
- assert names.include?('WeNeedReminders')
- assert names.include?('InnocentJointable')
+ def test_remove_references_column_type_removes_id
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
+ t.remove_references :customer
end
+ end
- def test_target_version_zero_should_run_only_once
- # migrate up to 1
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
-
- # migrate down to 0
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
-
- # migrate down to 0 again
- proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
- assert_equal [], proxies
+ def test_add_belongs_to_works_like_add_references
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
+ t.belongs_to :customer
end
+ end
- def test_migrator_db_has_no_schema_migrations_table
- # Oracle adapter raises error if semicolon is present as last character
- if current_adapter?(:OracleAdapter)
- ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations")
- else
- ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
- end
- assert_nothing_raised do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
- end
+ def test_remove_belongs_to_works_like_remove_references
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
+ t.remove_belongs_to :customer
end
+ end
- def test_migrator_verbosity
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- assert_not_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migration.message_count = 0
-
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
- assert_not_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migration.message_count = 0
+ def test_references_column_type_with_polymorphic_adds_type
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
+ t.references :taggable, :polymorphic => true
end
+ end
- def test_migrator_verbosity_off
- ActiveRecord::Migration.verbose = false
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- assert_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
- assert_equal 0, ActiveRecord::Migration.message_count
+ def test_remove_references_column_type_with_polymorphic_removes_type
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
+ t.remove_references :taggable, :polymorphic => true
end
+ end
- def test_migrator_going_down_due_to_version_target
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
-
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
+ t.references :taggable, :polymorphic => true, :null => false
end
+ end
- def test_migrator_rollback
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- assert_equal(3, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(2, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(1, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(0, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
+ t.remove_references :taggable, :polymorphic => true, :null => false
end
+ end
- def test_migrator_forward
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
- assert_equal(1, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid", 2)
- assert_equal(3, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
- assert_equal(3, ActiveRecord::Migrator.current_version)
+ def test_timestamps_creates_updated_at_and_created_at
+ with_change_table do |t|
+ @connection.expects(:add_timestamps).with(:delete_me)
+ t.timestamps
end
+ end
- def test_get_all_versions
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal([1], ActiveRecord::Migrator.get_all_versions)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal([], ActiveRecord::Migrator.get_all_versions)
+ def test_remove_timestamps_creates_updated_at_and_created_at
+ with_change_table do |t|
+ @connection.expects(:remove_timestamps).with(:delete_me)
+ t.remove_timestamps
end
+ end
- def test_schema_migrations_table_name
- ActiveRecord::Base.table_name_prefix = "prefix_"
- ActiveRecord::Base.table_name_suffix = "_suffix"
- Reminder.reset_table_name
- assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- Reminder.reset_table_name
- assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- end
-
- def test_proper_table_name
- assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
- assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
- assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
- Reminder.reset_table_name
- assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
-
- # Use the model's own prefix/suffix if a model is given
- ActiveRecord::Base.table_name_prefix = "ARprefix_"
- ActiveRecord::Base.table_name_suffix = "_ARsuffix"
- Reminder.table_name_prefix = 'prefix_'
- Reminder.table_name_suffix = '_suffix'
- Reminder.reset_table_name
- assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
- Reminder.table_name_prefix = ''
- Reminder.table_name_suffix = ''
- Reminder.reset_table_name
-
- # Use AR::Base's prefix/suffix if string or symbol is given
- ActiveRecord::Base.table_name_prefix = "prefix_"
- ActiveRecord::Base.table_name_suffix = "_suffix"
- Reminder.reset_table_name
- assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
- assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- Reminder.reset_table_name
- end
-
- def test_add_drop_table_with_prefix_and_suffix
- assert !Reminder.table_exists?
- ActiveRecord::Base.table_name_prefix = 'prefix_'
- ActiveRecord::Base.table_name_suffix = '_suffix'
- Reminder.reset_table_name
- Reminder.reset_sequence_name
- WeNeedReminders.up
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
-
- WeNeedReminders.down
- assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- ensure
- ActiveRecord::Base.table_name_prefix = ''
- ActiveRecord::Base.table_name_suffix = ''
- Reminder.reset_table_name
- Reminder.reset_sequence_name
+ def string_column
+ if current_adapter?(:PostgreSQLAdapter)
+ "character varying(255)"
+ elsif current_adapter?(:OracleAdapter)
+ 'VARCHAR2(255)'
+ else
+ 'varchar(255)'
end
+ end
- def test_create_table_with_binary_column
- Person.connection.drop_table :binary_testings rescue nil
-
- assert_nothing_raised {
- Person.connection.create_table :binary_testings do |t|
- t.column "data", :binary, :null => false
- end
- }
-
- columns = Person.connection.columns(:binary_testings)
- data_column = columns.detect { |c| c.name == "data" }
-
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- assert_equal '', data_column.default
- else
- assert_nil data_column.default
- end
-
- Person.connection.drop_table :binary_testings rescue nil
+ def integer_column
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
+ 'int(11)'
+ elsif current_adapter?(:OracleAdapter)
+ 'NUMBER(38)'
+ else
+ 'integer'
end
+ end
- def test_migrator_with_duplicates
- assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
- end
+ def test_integer_creates_integer_column
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
+ @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
+ t.integer :foo, :bar
end
+ end
- def test_migrator_with_duplicate_names
- assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
- end
+ def test_string_creates_string_column
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
+ @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
+ t.string :foo, :bar
end
+ end
- def test_migrator_with_missing_version_numbers
- assert_raise(ActiveRecord::UnknownMigrationVersionError) do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
- end
+ def test_column_creates_column
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
+ t.column :bar, :integer
end
+ end
- def test_create_table_with_custom_sequence_name
- return unless current_adapter? :OracleAdapter
-
- # table name is 29 chars, the standard sequence name will
- # be 33 chars and should be shortened
- assert_nothing_raised do
- begin
- Person.connection.create_table :table_with_name_thats_just_ok do |t|
- t.column :foo, :string, :null => false
- end
- ensure
- Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
- end
- end
-
- # should be all good w/ a custom sequence name
- assert_nothing_raised do
- begin
- Person.connection.create_table :table_with_name_thats_just_ok,
- :sequence_name => 'suitably_short_seq' do |t|
- t.column :foo, :string, :null => false
- end
-
- Person.connection.execute("select suitably_short_seq.nextval from dual")
-
- ensure
- Person.connection.drop_table :table_with_name_thats_just_ok,
- :sequence_name => 'suitably_short_seq' rescue nil
- end
- end
-
- # confirm the custom sequence got dropped
- assert_raise(ActiveRecord::StatementInvalid) do
- Person.connection.execute("select suitably_short_seq.nextval from dual")
- end
+ def test_column_creates_column_with_options
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
+ t.column :bar, :integer, :null => false
end
-
- protected
- def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
- yield
- ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
- end
-
end
- class SexyMigrationsTest < ActiveRecord::TestCase
- def test_references_column_type_adds_id
- with_new_table do |t|
- t.expects(:column).with('customer_id', :integer, {})
- t.references :customer
- end
+ def test_index_creates_index
+ with_change_table do |t|
+ @connection.expects(:add_index).with(:delete_me, :bar, {})
+ t.index :bar
end
+ end
- def test_references_column_type_with_polymorphic_adds_type
- with_new_table do |t|
- t.expects(:column).with('taggable_type', :string, {})
- t.expects(:column).with('taggable_id', :integer, {})
- t.references :taggable, :polymorphic => true
- end
+ def test_index_creates_index_with_options
+ with_change_table do |t|
+ @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
+ t.index :bar, :unique => true
end
+ end
- def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
- with_new_table do |t|
- t.expects(:column).with('taggable_type', :string, {:null => false})
- t.expects(:column).with('taggable_id', :integer, {:null => false})
- t.references :taggable, :polymorphic => true, :null => false
- end
+ def test_index_exists
+ with_change_table do |t|
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {})
+ t.index_exists?(:bar)
end
+ end
- def test_belongs_to_works_like_references
- with_new_table do |t|
- t.expects(:column).with('customer_id', :integer, {})
- t.belongs_to :customer
- end
+ def test_index_exists_with_options
+ with_change_table do |t|
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
+ t.index_exists?(:bar, :unique => true)
end
+ end
- def test_timestamps_creates_updated_at_and_created_at
- with_new_table do |t|
- t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
- t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
- t.timestamps
- end
+ def test_change_changes_column
+ with_change_table do |t|
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
+ t.change :bar, :string
end
+ end
- def test_integer_creates_integer_column
- with_new_table do |t|
- t.expects(:column).with(:foo, 'integer', {})
- t.expects(:column).with(:bar, 'integer', {})
- t.integer :foo, :bar
- end
+ def test_change_changes_column_with_options
+ with_change_table do |t|
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
+ t.change :bar, :string, :null => true
end
+ end
- def test_string_creates_string_column
- with_new_table do |t|
- t.expects(:column).with(:foo, 'string', {})
- t.expects(:column).with(:bar, 'string', {})
- t.string :foo, :bar
- end
+ def test_change_default_changes_column
+ with_change_table do |t|
+ @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
+ t.change_default :bar, :string
end
+ end
- if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
- def test_xml_creates_xml_column
- type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text
-
- with_new_table do |t|
- t.expects(:column).with(:data, type, {})
- t.xml :data
- end
- end
- else
- def test_xml_creates_xml_column
- with_new_table do |t|
- assert_raises(NotImplementedError) do
- t.xml :data
- end
- end
- end
+ def test_remove_drops_single_column
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, [:bar])
+ t.remove :bar
end
+ end
- protected
- def with_new_table
- Person.connection.create_table :delete_me, :force => true do |t|
- yield t
- end
- ensure
- Person.connection.drop_table :delete_me rescue nil
+ def test_remove_drops_multiple_columns
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
+ t.remove :bar, :baz
end
+ end
- end # SexyMigrationsTest
-
- class MigrationLoggerTest < ActiveRecord::TestCase
- def test_migration_should_be_run_without_logger
- previous_logger = ActiveRecord::Base.logger
- ActiveRecord::Base.logger = nil
- assert_nothing_raised do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- end
- ensure
- ActiveRecord::Base.logger = previous_logger
+ def test_remove_index_removes_index_with_options
+ with_change_table do |t|
+ @connection.expects(:remove_index).with(:delete_me, {:unique => true})
+ t.remove_index :unique => true
end
end
- class InterleavedMigrationsTest < ActiveRecord::TestCase
- def test_migrator_interleaved_migrations
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
-
- assert_nothing_raised do
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
- end
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- assert_nothing_raised do
- proxies = ActiveRecord::Migrator.down(
- MIGRATIONS_ROOT + "/interleaved/pass_3")
- names = proxies.map(&:name)
- assert names.include?('InterleavedPeopleHaveLastNames')
- assert names.include?('InterleavedInnocentJointable')
- end
+ def test_rename_renames_column
+ with_change_table do |t|
+ @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
+ t.rename :bar, :baz
end
end
- class ReservedWordsMigrationTest < ActiveRecord::TestCase
- def test_drop_index_from_table_named_values
- connection = Person.connection
- connection.create_table :values, :force => true do |t|
- t.integer :value
- end
-
- assert_nothing_raised do
- connection.add_index :values, :value
- connection.remove_index :values, :column => :value
- end
-
- connection.drop_table :values rescue nil
+ protected
+ def with_change_table
+ Person.connection.change_table :delete_me do |t|
+ yield t
end
end
+end
-
- class ChangeTableMigrationsTest < ActiveRecord::TestCase
+if ActiveRecord::Base.connection.supports_bulk_alter?
+ class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
def setup
@connection = Person.connection
- @connection.create_table :delete_me, :force => true do |t|
- end
+ @connection.create_table(:delete_me, :force => true) {|t| }
end
def teardown
- Person.connection.drop_table :delete_me rescue nil
+ Person.connection.drop_table(:delete_me) rescue nil
end
- def test_references_column_type_adds_id
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
- t.references :customer
+ def test_adding_multiple_columns
+ assert_queries(1) do
+ with_bulk_change_table do |t|
+ t.column :name, :string
+ t.string :qualification, :experience
+ t.integer :age, :default => 0
+ t.date :birthdate
+ t.timestamps
+ end
end
- end
- def test_remove_references_column_type_removes_id
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'customer_id')
- t.remove_references :customer
- end
+ assert_equal 8, columns.size
+ [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
+ assert_equal 0, column(:age).default
end
- def test_add_belongs_to_works_like_add_references
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
- t.belongs_to :customer
+ def test_removing_columns
+ with_bulk_change_table do |t|
+ t.string :qualification, :experience
end
- end
- def test_remove_belongs_to_works_like_remove_references
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'customer_id')
- t.remove_belongs_to :customer
- end
- end
-
- def test_references_column_type_with_polymorphic_adds_type
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
- @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
- t.references :taggable, :polymorphic => true
- end
- end
+ [:qualification, :experience].each {|c| assert column(c) }
- def test_remove_references_column_type_with_polymorphic_removes_type
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
- @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
- t.remove_references :taggable, :polymorphic => true
+ assert_queries(1) do
+ with_bulk_change_table do |t|
+ t.remove :qualification, :experience
+ t.string :qualification_experience
+ end
end
- end
- def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
- @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
- t.references :taggable, :polymorphic => true, :null => false
- end
+ [:qualification, :experience].each {|c| assert ! column(c) }
+ assert column(:qualification_experience)
end
- def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
- @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
- t.remove_references :taggable, :polymorphic => true, :null => false
+ def test_adding_indexes
+ with_bulk_change_table do |t|
+ t.string :username
+ t.string :name
+ t.integer :age
end
- end
- def test_timestamps_creates_updated_at_and_created_at
- with_change_table do |t|
- @connection.expects(:add_timestamps).with(:delete_me)
- t.timestamps
+ # Adding an index fires a query every time to check if an index already exists or not
+ assert_queries(3) do
+ with_bulk_change_table do |t|
+ t.index :username, :unique => true, :name => :awesome_username_index
+ t.index [:name, :age]
+ end
end
- end
- def test_remove_timestamps_creates_updated_at_and_created_at
- with_change_table do |t|
- @connection.expects(:remove_timestamps).with(:delete_me)
- t.remove_timestamps
- end
- end
+ assert_equal 2, indexes.size
- def string_column
- if current_adapter?(:PostgreSQLAdapter)
- "character varying(255)"
- elsif current_adapter?(:OracleAdapter)
- 'VARCHAR2(255)'
- else
- 'varchar(255)'
- end
- end
+ name_age_index = index(:index_delete_me_on_name_and_age)
+ assert_equal ['name', 'age'].sort, name_age_index.columns.sort
+ assert ! name_age_index.unique
- def integer_column
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- 'int(11)'
- elsif current_adapter?(:OracleAdapter)
- 'NUMBER(38)'
- else
- 'integer'
- end
+ assert index(:awesome_username_index).unique
end
- def test_integer_creates_integer_column
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
- @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
- t.integer :foo, :bar
+ def test_removing_index
+ with_bulk_change_table do |t|
+ t.string :name
+ t.index :name
end
- end
- def test_string_creates_string_column
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
- @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
- t.string :foo, :bar
- end
- end
+ assert index(:index_delete_me_on_name)
- def test_column_creates_column
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
- t.column :bar, :integer
+ assert_queries(3) do
+ with_bulk_change_table do |t|
+ t.remove_index :name
+ t.index :name, :name => :new_name_index, :unique => true
+ end
end
- end
- def test_column_creates_column_with_options
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
- t.column :bar, :integer, :null => false
- end
- end
+ assert ! index(:index_delete_me_on_name)
- def test_index_creates_index
- with_change_table do |t|
- @connection.expects(:add_index).with(:delete_me, :bar, {})
- t.index :bar
- end
+ new_name_index = index(:new_name_index)
+ assert new_name_index.unique
end
- def test_index_creates_index_with_options
- with_change_table do |t|
- @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
- t.index :bar, :unique => true
+ def test_changing_columns
+ with_bulk_change_table do |t|
+ t.string :name
+ t.date :birthdate
end
- end
- def test_index_exists
- with_change_table do |t|
- @connection.expects(:index_exists?).with(:delete_me, :bar, {})
- t.index_exists?(:bar)
- end
- end
+ assert ! column(:name).default
+ assert_equal :date, column(:birthdate).type
- def test_index_exists_with_options
- with_change_table do |t|
- @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
- t.index_exists?(:bar, :unique => true)
+ # One query for columns (delete_me table)
+ # One query for primary key (delete_me table)
+ # One query to do the bulk change
+ assert_queries(3) do
+ with_bulk_change_table do |t|
+ t.change :name, :string, :default => 'NONAME'
+ t.change :birthdate, :datetime
+ end
end
- end
- def test_change_changes_column
- with_change_table do |t|
- @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
- t.change :bar, :string
- end
+ assert_equal 'NONAME', column(:name).default
+ assert_equal :datetime, column(:birthdate).type
end
- def test_change_changes_column_with_options
- with_change_table do |t|
- @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
- t.change :bar, :string, :null => true
- end
- end
+ protected
- def test_change_default_changes_column
- with_change_table do |t|
- @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
- t.change_default :bar, :string
- end
- end
+ def with_bulk_change_table
+ # Reset columns/indexes cache as we're changing the table
+ @columns = @indexes = nil
- def test_remove_drops_single_column
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, [:bar])
- t.remove :bar
+ Person.connection.change_table(:delete_me, :bulk => true) do |t|
+ yield t
end
end
- def test_remove_drops_multiple_columns
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
- t.remove :bar, :baz
- end
+ def column(name)
+ columns.detect {|c| c.name == name.to_s }
end
- def test_remove_index_removes_index_with_options
- with_change_table do |t|
- @connection.expects(:remove_index).with(:delete_me, {:unique => true})
- t.remove_index :unique => true
- end
+ def columns
+ @columns ||= Person.connection.columns('delete_me')
end
- def test_rename_renames_column
- with_change_table do |t|
- @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
- t.rename :bar, :baz
- end
+ def index(name)
+ indexes.detect {|i| i.name == name.to_s }
end
- protected
- def with_change_table
- Person.connection.change_table :delete_me do |t|
- yield t
- end
+ def indexes
+ @indexes ||= Person.connection.indexes('delete_me')
end
- end
-
- if ActiveRecord::Base.connection.supports_bulk_alter?
- class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
- def setup
- @connection = Person.connection
- @connection.create_table(:delete_me, :force => true) {|t| }
- end
-
- def teardown
- Person.connection.drop_table(:delete_me) rescue nil
- end
-
- def test_adding_multiple_columns
- assert_queries(1) do
- with_bulk_change_table do |t|
- t.column :name, :string
- t.string :qualification, :experience
- t.integer :age, :default => 0
- t.date :birthdate
- t.timestamps
- end
- end
-
- assert_equal 8, columns.size
- [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
- assert_equal 0, column(:age).default
- end
-
- def test_removing_columns
- with_bulk_change_table do |t|
- t.string :qualification, :experience
- end
-
- [:qualification, :experience].each {|c| assert column(c) }
-
- assert_queries(1) do
- with_bulk_change_table do |t|
- t.remove :qualification, :experience
- t.string :qualification_experience
- end
- end
-
- [:qualification, :experience].each {|c| assert ! column(c) }
- assert column(:qualification_experience)
- end
-
- def test_adding_indexes
- with_bulk_change_table do |t|
- t.string :username
- t.string :name
- t.integer :age
- end
-
- # Adding an index fires a query every time to check if an index already exists or not
- assert_queries(3) do
- with_bulk_change_table do |t|
- t.index :username, :unique => true, :name => :awesome_username_index
- t.index [:name, :age]
- end
- end
-
- assert_equal 2, indexes.size
-
- name_age_index = index(:index_delete_me_on_name_and_age)
- assert_equal ['name', 'age'].sort, name_age_index.columns.sort
- assert ! name_age_index.unique
-
- assert index(:awesome_username_index).unique
- end
-
- def test_removing_index
- with_bulk_change_table do |t|
- t.string :name
- t.index :name
- end
-
- assert index(:index_delete_me_on_name)
-
- assert_queries(3) do
- with_bulk_change_table do |t|
- t.remove_index :name
- t.index :name, :name => :new_name_index, :unique => true
- end
- end
-
- assert ! index(:index_delete_me_on_name)
-
- new_name_index = index(:new_name_index)
- assert new_name_index.unique
- end
-
- def test_changing_columns
- with_bulk_change_table do |t|
- t.string :name
- t.date :birthdate
- end
-
- assert ! column(:name).default
- assert_equal :date, column(:birthdate).type
-
- # One query for columns (delete_me table)
- # One query for primary key (delete_me table)
- # One query to do the bulk change
- assert_queries(3) do
- with_bulk_change_table do |t|
- t.change :name, :string, :default => 'NONAME'
- t.change :birthdate, :datetime
- end
- end
-
- assert_equal 'NONAME', column(:name).default
- assert_equal :datetime, column(:birthdate).type
- end
-
- protected
-
- def with_bulk_change_table
- # Reset columns/indexes cache as we're changing the table
- @columns = @indexes = nil
-
- Person.connection.change_table(:delete_me, :bulk => true) do |t|
- yield t
- end
- end
+ end # AlterTableMigrationsTest
- def column(name)
- columns.detect {|c| c.name == name.to_s }
- end
-
- def columns
- @columns ||= Person.connection.columns('delete_me')
- end
-
- def index(name)
- indexes.detect {|i| i.name == name.to_s }
- end
+end
- def indexes
- @indexes ||= Person.connection.indexes('delete_me')
- end
- end # AlterTableMigrationsTest
+class CopyMigrationsTest < ActiveRecord::TestCase
+ def setup
+ end
+ def clear
+ ActiveRecord::Base.timestamped_migrations = true
+ to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
+ File.delete(*to_delete)
end
- class CopyMigrationsTest < ActiveRecord::TestCase
- def setup
- end
+ def test_copying_migrations_without_timestamps
+ ActiveRecord::Base.timestamped_migrations = false
+ @migrations_path = MIGRATIONS_ROOT + "/valid"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
+ assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied.map(&:filename)
+
+ expected = "# This migration comes from bukkits (originally 1)"
+ assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[0].chomp
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
+ ensure
+ clear
+ end
- def clear
- ActiveRecord::Base.timestamped_migrations = true
- to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
- File.delete(*to_delete)
- end
+ def test_copying_migrations_without_timestamps_from_2_sources
+ ActiveRecord::Base.timestamped_migrations = false
+ @migrations_path = MIGRATIONS_ROOT + "/valid"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ sources = {}
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/6_create_articles.omg.rb")
+ assert File.exists?(@migrations_path + "/7_create_comments.omg.rb")
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ ensure
+ clear
+ end
- def test_copying_migrations_without_timestamps
- ActiveRecord::Base.timestamped_migrations = false
- @migrations_path = MIGRATIONS_ROOT + "/valid"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_copying_migrations_with_timestamps
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
- assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
- assert_equal [@migrations_path + "/4_people_have_hobbies.rb", @migrations_path + "/5_people_have_descriptions.rb"], copied.map(&:filename)
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
+ @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
+ assert_equal expected, copied.map(&:filename)
files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
assert copied.empty?
- ensure
- clear
end
+ ensure
+ clear
+ end
- def test_copying_migrations_without_timestamps_from_2_sources
- ActiveRecord::Base.timestamped_migrations = false
- @migrations_path = MIGRATIONS_ROOT + "/valid"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_copying_migrations_with_timestamps_from_2_sources
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
- sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
- ActiveRecord::Migration.copy(@migrations_path, sources)
- assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
- assert File.exists?(@migrations_path + "/6_create_articles.rb")
- assert File.exists?(@migrations_path + "/7_create_comments.rb")
+ sources = {}
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
+
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101012_create_articles.omg.rb")
+ assert File.exists?(@migrations_path + "/20100726101013_create_comments.omg.rb")
+ assert_equal 4, copied.length
files_count = Dir[@migrations_path + "/*.rb"].length
ActiveRecord::Migration.copy(@migrations_path, sources)
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- ensure
- clear
end
+ ensure
+ clear
+ end
- def test_copying_migrations_with_timestamps
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- expected = [@migrations_path + "/20100726101010_people_have_hobbies.rb",
- @migrations_path + "/20100726101011_people_have_descriptions.rb"]
- assert_equal expected, copied.map(&:filename)
+ Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
+ ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
- files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
- end
- ensure
- clear
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
end
+ ensure
+ clear
+ end
- def test_copying_migrations_with_timestamps_from_2_sources
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
-
- sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
- sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
-
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, sources)
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- assert File.exists?(@migrations_path + "/20100726101012_create_articles.rb")
- assert File.exists?(@migrations_path + "/20100726101013_create_comments.rb")
- assert_equal 4, copied.length
-
- files_count = Dir[@migrations_path + "/*.rb"].length
- ActiveRecord::Migration.copy(@migrations_path, sources)
- assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- end
- ensure
- clear
- end
+ def test_skipping_migrations
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ sources = {}
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
+
+ skipped = []
+ on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ assert_equal 2, copied.length
+
+ assert_equal 1, skipped.length
+ assert_equal ["omg PeopleHaveHobbies"], skipped
+ ensure
+ clear
+ end
- def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_skip_is_not_called_if_migrations_are_from_the_same_plugin
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
- ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.rb")
+ sources = {}
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
- files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
- end
- ensure
- clear
- end
+ skipped = []
+ on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
- def test_skipping_migrations
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ assert_equal 2, copied.length
+ assert_equal 0, skipped.length
+ ensure
+ clear
+ end
- sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ def test_copying_migrations_to_non_existing_directory
+ @migrations_path = MIGRATIONS_ROOT + "/non_existing"
+ @existing_migrations = []
- skipped = []
- on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
- copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
assert_equal 2, copied.length
-
- assert_equal 2, skipped.length
- assert_equal ["bukkits PeopleHaveHobbies", "bukkits PeopleHaveDescriptions"], skipped
- ensure
- clear
- end
-
- def test_copying_migrations_to_non_existing_directory
- @migrations_path = MIGRATIONS_ROOT + "/non_existing"
- @existing_migrations = []
-
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- assert_equal 2, copied.length
- end
- ensure
- clear
- Dir.delete(@migrations_path)
end
+ ensure
+ clear
+ Dir.delete(@migrations_path)
+ end
- def test_copying_migrations_to_empty_directory
- @migrations_path = MIGRATIONS_ROOT + "/empty"
- @existing_migrations = []
+ def test_copying_migrations_to_empty_directory
+ @migrations_path = MIGRATIONS_ROOT + "/empty"
+ @existing_migrations = []
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- assert_equal 2, copied.length
- end
- ensure
- clear
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ assert_equal 2, copied.length
end
+ ensure
+ clear
end
end
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
new file mode 100644
index 0000000000..1e16addcf3
--- /dev/null
+++ b/activerecord/test/cases/migrator_test.rb
@@ -0,0 +1,377 @@
+require "cases/helper"
+require "cases/migration/helper"
+
+module ActiveRecord
+ class MigratorTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ # Use this class to sense if migrations have gone
+ # up or down.
+ class Sensor < ActiveRecord::Migration
+ attr_reader :went_up, :went_down
+
+ def initialize name = self.class.name, version = nil
+ super
+ @went_up = false
+ @went_down = false
+ end
+
+ def up; @went_up = true; end
+ def down; @went_down = true; end
+ end
+
+ def setup
+ super
+ ActiveRecord::SchemaMigration.create_table
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ end
+
+ def teardown
+ super
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ end
+
+ def test_migrator_with_duplicate_names
+ assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
+ list = [Migration.new('Chunky'), Migration.new('Chunky')]
+ ActiveRecord::Migrator.new(:up, list)
+ end
+ end
+
+ def test_migrator_with_duplicate_versions
+ assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
+ list = [Migration.new('Foo', 1), Migration.new('Bar', 1)]
+ ActiveRecord::Migrator.new(:up, list)
+ end
+ end
+
+ def test_migrator_with_missing_version_numbers
+ assert_raises(ActiveRecord::UnknownMigrationVersionError) do
+ list = [Migration.new('Foo', 1), Migration.new('Bar', 2)]
+ ActiveRecord::Migrator.new(:up, list, 3).run
+ end
+ end
+
+ def test_finds_migrations
+ migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid")
+
+ [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
+ assert_equal migrations[i].version, pair.first
+ assert_equal migrations[i].name, pair.last
+ end
+ end
+
+ def test_finds_migrations_in_subdirectories
+ migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid_with_subdirectories")
+
+ [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
+ assert_equal migrations[i].version, pair.first
+ assert_equal migrations[i].name, pair.last
+ end
+ end
+
+ def test_finds_migrations_from_two_directories
+ directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
+ migrations = ActiveRecord::Migrator.migrations directories
+
+ [[20090101010101, "PeopleHaveHobbies"],
+ [20090101010202, "PeopleHaveDescriptions"],
+ [20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
+ [20100201010101, "ValidWithTimestampsWeNeedReminders"],
+ [20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
+ assert_equal pair.first, migrations[i].version
+ assert_equal pair.last, migrations[i].name
+ end
+ end
+
+ def test_deprecated_constructor
+ assert_deprecated do
+ ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid")
+ end
+ end
+
+ def test_relative_migrations
+ list = Dir.chdir(MIGRATIONS_ROOT) do
+ ActiveRecord::Migrator.migrations("valid/")
+ end
+
+ migration_proxy = list.find { |item|
+ item.name == 'ValidPeopleHaveLastNames'
+ }
+ assert migration_proxy, 'should find pending migration'
+ end
+
+ def test_finds_pending_migrations
+ ActiveRecord::SchemaMigration.create!(:version => '1')
+ migration_list = [ Migration.new('foo', 1), Migration.new('bar', 3) ]
+ migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
+
+ assert_equal 1, migrations.size
+ assert_equal migration_list.last, migrations.first
+ end
+
+ def test_migrator_interleaved_migrations
+ pass_one = [Sensor.new('One', 1)]
+
+ ActiveRecord::Migrator.new(:up, pass_one).migrate
+ assert pass_one.first.went_up
+ refute pass_one.first.went_down
+
+ pass_two = [Sensor.new('One', 1), Sensor.new('Three', 3)]
+ ActiveRecord::Migrator.new(:up, pass_two).migrate
+ refute pass_two[0].went_up
+ assert pass_two[1].went_up
+ assert pass_two.all? { |x| !x.went_down }
+
+ pass_three = [Sensor.new('One', 1),
+ Sensor.new('Two', 2),
+ Sensor.new('Three', 3)]
+
+ ActiveRecord::Migrator.new(:down, pass_three).migrate
+ assert pass_three[0].went_down
+ refute pass_three[1].went_down
+ assert pass_three[2].went_down
+ end
+
+ def test_up_calls_up
+ migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ assert migrations.all? { |m| m.went_up }
+ assert migrations.all? { |m| !m.went_down }
+ assert_equal 2, ActiveRecord::Migrator.current_version
+ end
+
+ def test_down_calls_down
+ test_up_calls_up
+
+ migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
+ ActiveRecord::Migrator.new(:down, migrations).migrate
+ assert migrations.all? { |m| !m.went_up }
+ assert migrations.all? { |m| m.went_down }
+ assert_equal 0, ActiveRecord::Migrator.current_version
+ end
+
+ def test_current_version
+ ActiveRecord::SchemaMigration.create!(:version => '1000')
+ assert_equal 1000, ActiveRecord::Migrator.current_version
+ end
+
+ def test_migrator_one_up
+ calls, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:up, migrations, 2).migrate
+ assert_equal [[:up, 2]], calls
+ end
+
+ def test_migrator_one_down
+ calls, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 1).migrate
+
+ assert_equal [[:down, 3], [:down, 2]], calls
+ end
+
+ def test_migrator_one_up_one_down
+ calls, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal [[:down, 1]], calls
+ end
+
+ def test_migrator_double_up
+ calls, migrations = sensors(3)
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [], calls
+ end
+
+ def test_migrator_double_down
+ calls, migrations = sensors(3)
+
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).run
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 1).run
+ assert_equal [[:down, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 1).run
+ assert_equal [], calls
+
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+ end
+
+ def test_migrator_verbosity
+ _, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_not_equal 0, ActiveRecord::Migration.message_count
+
+ ActiveRecord::Migration.message_count = 0
+
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_not_equal 0, ActiveRecord::Migration.message_count
+ ActiveRecord::Migration.message_count = 0
+ end
+
+ def test_migrator_verbosity_off
+ _, migrations = sensors(3)
+
+ ActiveRecord::Migration.message_count = 0
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal 0, ActiveRecord::Migration.message_count
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal 0, ActiveRecord::Migration.message_count
+ end
+
+ def test_target_version_zero_should_run_only_once
+ calls, migrations = sensors(3)
+
+ # migrate up to 1
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ # migrate down to 0
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal [[:down, 1]], calls
+ calls.clear
+
+ # migrate down to 0 again
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal [], calls
+ end
+
+ def test_migrator_going_down_due_to_version_target
+ calls, migrator = migrator_class(3)
+
+ migrator.up("valid", 1)
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ migrator.migrate("valid", 0)
+ assert_equal [[:down, 1]], calls
+ calls.clear
+
+ migrator.migrate("valid")
+ assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
+ end
+
+ def test_migrator_rollback
+ _, migrator = migrator_class(3)
+
+ migrator.migrate("valid")
+ assert_equal(3, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(2, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(1, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+ end
+
+ def test_migrator_db_has_no_schema_migrations_table
+ _, migrator = migrator_class(3)
+
+ ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations")
+ refute ActiveRecord::Base.connection.table_exists?('schema_migrations')
+ migrator.migrate("valid", 1)
+ assert ActiveRecord::Base.connection.table_exists?('schema_migrations')
+ end
+
+ def test_migrator_forward
+ _, migrator = migrator_class(3)
+ migrator.migrate("/valid", 1)
+ assert_equal(1, ActiveRecord::Migrator.current_version)
+
+ migrator.forward("/valid", 2)
+ assert_equal(3, ActiveRecord::Migrator.current_version)
+
+ migrator.forward("/valid")
+ assert_equal(3, ActiveRecord::Migrator.current_version)
+ end
+
+ def test_only_loads_pending_migrations
+ # migrate up to 1
+ ActiveRecord::SchemaMigration.create!(:version => '1')
+
+ calls, migrator = migrator_class(3)
+ migrator.migrate("valid", nil)
+
+ assert_equal [[:up, 2], [:up, 3]], calls
+ end
+
+ def test_get_all_versions
+ _, migrator = migrator_class(3)
+
+ migrator.migrate("valid")
+ assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
+
+ migrator.rollback("valid")
+ assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
+
+ migrator.rollback("valid")
+ assert_equal([1], ActiveRecord::Migrator.get_all_versions)
+
+ migrator.rollback("valid")
+ assert_equal([], ActiveRecord::Migrator.get_all_versions)
+ end
+
+ private
+ def m(name, version, &block)
+ x = Sensor.new name, version
+ x.extend(Module.new {
+ define_method(:up) { block.call(:up, x); super() }
+ define_method(:down) { block.call(:down, x); super() }
+ }) if block_given?
+ end
+
+ def sensors(count)
+ calls = []
+ migrations = count.times.map { |i|
+ m(nil, i + 1) { |c,migration|
+ calls << [c, migration.version]
+ }
+ }
+ [calls, migrations]
+ end
+
+ def migrator_class(count)
+ calls, migrations = sensors(count)
+
+ migrator = Class.new(Migrator).extend(Module.new {
+ define_method(:migrations) { |paths|
+ migrations
+ }
+ })
+ [calls, migrator]
+ end
+ end
+end
diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb
index a2041af16a..f7a5d05582 100644
--- a/activerecord/test/cases/modules_test.rb
+++ b/activerecord/test/cases/modules_test.rb
@@ -72,7 +72,7 @@ class ModulesTest < ActiveRecord::TestCase
clients = []
assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do
- clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL')
+ clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL', :references => :accounts)
clients << MyApplication::Business::Client.find(3, :include => {:firm => :account})
end
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index bd51388e05..a802cfbf31 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -10,6 +10,7 @@ class MultipleDbTest < ActiveRecord::TestCase
def setup
@courses = create_fixtures("courses") { Course.retrieve_connection }
+ @colleges = create_fixtures("colleges") { College.retrieve_connection }
@entrants = create_fixtures("entrants")
end
@@ -85,7 +86,17 @@ class MultipleDbTest < ActiveRecord::TestCase
end
def test_arel_table_engines
- assert_not_equal Entrant.arel_engine, Course.arel_engine
assert_equal Entrant.arel_engine, Bird.arel_engine
end
+
+ def test_associations_should_work_when_model_has_no_connection
+ begin
+ ActiveRecord::Model.remove_connection
+ assert_nothing_raised ActiveRecord::ConnectionNotEstablished do
+ College.first.courses.first
+ end
+ ensure
+ ActiveRecord::Model.establish_connection 'arunit'
+ end
+ end
end
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 4a09a87322..e17ba76437 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require 'active_support/core_ext/array/random_access'
require 'models/post'
require 'models/topic'
require 'models/comment'
@@ -337,6 +336,11 @@ class NamedScopeTest < ActiveRecord::TestCase
end
end
+ def test_should_not_duplicates_where_values
+ where_values = Topic.where("1=1").scope_with_lambda.where_values
+ assert_equal ["1=1"], where_values
+ end
+
def test_chaining_with_duplicate_joins
join = "INNER JOIN comments ON comments.post_id = posts.id"
post = Post.find(1)
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index 434b8a677a..fba3006ebe 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -7,17 +7,17 @@ class PooledConnectionsTest < ActiveRecord::TestCase
def setup
@per_test_teardown = []
- @connection = ActiveRecord::Base.remove_connection
+ @connection = ActiveRecord::Model.remove_connection
end
def teardown
- ActiveRecord::Base.clear_all_connections!
- ActiveRecord::Base.establish_connection(@connection)
+ ActiveRecord::Model.clear_all_connections!
+ ActiveRecord::Model.establish_connection(@connection)
@per_test_teardown.each {|td| td.call }
end
def checkout_connections
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
+ ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
@connections = []
@timed_out = 0
@@ -33,24 +33,16 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
# Will deadlock due to lack of Monitor timeouts in 1.9
- if RUBY_VERSION < '1.9'
- def test_pooled_connection_checkout
- checkout_connections
- assert_equal 2, @connections.length
- assert_equal 2, @timed_out
- end
- end
-
def checkout_checkin_connections(pool_size, threads)
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
+ ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
@connection_count = 0
@timed_out = 0
threads.times do
Thread.new do
begin
- conn = ActiveRecord::Base.connection_pool.checkout
+ conn = ActiveRecord::Model.connection_pool.checkout
sleep 0.1
- ActiveRecord::Base.connection_pool.checkin conn
+ ActiveRecord::Model.connection_pool.checkin conn
@connection_count += 1
rescue ActiveRecord::ConnectionTimeoutError
@timed_out += 1
@@ -63,85 +55,13 @@ class PooledConnectionsTest < ActiveRecord::TestCase
checkout_checkin_connections 1, 2
assert_equal 2, @connection_count
assert_equal 0, @timed_out
- assert_equal 1, ActiveRecord::Base.connection_pool.connections.size
+ assert_equal 1, ActiveRecord::Model.connection_pool.connections.size
end
- def test_pooled_connection_checkin_two
- checkout_checkin_connections 2, 3
- assert_equal 3, @connection_count
- assert_equal 0, @timed_out
- assert_equal 1, ActiveRecord::Base.connection_pool.connections.size
- end
-
- def test_pooled_connection_checkout_existing_first
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1}))
- conn_pool = ActiveRecord::Base.connection_pool
- conn = conn_pool.checkout
- conn_pool.checkin(conn)
- conn = conn_pool.checkout
- assert ActiveRecord::ConnectionAdapters::AbstractAdapter === conn
- conn_pool.checkin(conn)
- end
-
- def test_not_connected_defined_connection_returns_false
- ActiveRecord::Base.establish_connection(@connection)
- assert ! ActiveRecord::Base.connected?
- end
-
- def test_undefined_connection_returns_false
- old_handler = ActiveRecord::Base.connection_handler
- ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
- assert ! ActiveRecord::Base.connected?
- ensure
- ActiveRecord::Base.connection_handler = old_handler
- end
-
- def test_connection_config
- ActiveRecord::Base.establish_connection(@connection)
- assert_equal @connection, ActiveRecord::Base.connection_config
- 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
- add_record('three')
- end
-
- after_count = Project.count
- assert_equal 3, after_count - before_count
- end
-
- def test_connection_pool_callbacks
- checked_out, checked_in = false, false
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
- set_callback(:checkout, :after) { checked_out = true }
- set_callback(:checkin, :before) { checked_in = true }
- end
- @per_test_teardown << proc do
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
- reset_callbacks :checkout
- reset_callbacks :checkin
- end
- end
- checkout_checkin_connections 1, 1
- assert checked_out
- assert checked_in
- end
private
def add_record(name)
- ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name }
+ ActiveRecord::Model.connection_pool.with_connection { Project.create! :name => name }
end
end unless current_adapter?(:FrontBase) || in_memory_db?
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 4bb5752096..e6e50a4cd4 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -23,6 +23,11 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_equal keyboard.to_key, [keyboard.id]
end
+ def test_read_attribute_with_custom_primary_key
+ keyboard = Keyboard.create!
+ assert_equal keyboard.key_number, keyboard.read_attribute(:id)
+ end
+
def test_to_key_with_primary_key_after_destroy
topic = Topic.find(1)
topic.destroy
@@ -142,8 +147,38 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_equal k.connection.quote_column_name("id"), k.quoted_primary_key
k.primary_key = "foo"
assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key
- k.set_primary_key "bar"
- assert_equal k.connection.quote_column_name("bar"), k.quoted_primary_key
+ end
+
+ def test_two_models_with_same_table_but_different_primary_key
+ k1 = Class.new(ActiveRecord::Base)
+ k1.table_name = 'posts'
+ k1.primary_key = 'id'
+
+ k2 = Class.new(ActiveRecord::Base)
+ k2.table_name = 'posts'
+ k2.primary_key = 'title'
+
+ assert k1.columns.find { |c| c.name == 'id' }.primary
+ assert !k1.columns.find { |c| c.name == 'title' }.primary
+ assert k1.columns_hash['id'].primary
+ assert !k1.columns_hash['title'].primary
+
+ assert !k2.columns.find { |c| c.name == 'id' }.primary
+ assert k2.columns.find { |c| c.name == 'title' }.primary
+ assert !k2.columns_hash['id'].primary
+ assert k2.columns_hash['title'].primary
+ end
+
+ def test_models_with_same_table_have_different_columns
+ k1 = Class.new(ActiveRecord::Base)
+ k1.table_name = 'posts'
+
+ k2 = Class.new(ActiveRecord::Base)
+ k2.table_name = 'posts'
+
+ k1.columns.zip(k2.columns).each do |col1, col2|
+ assert !col1.equal?(col2)
+ end
end
end
@@ -153,16 +188,31 @@ class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
def test_set_primary_key_with_no_connection
return skip("disconnect wipes in-memory db") if in_memory_db?
- connection = ActiveRecord::Base.remove_connection
+ connection = ActiveRecord::Model.remove_connection
- model = Class.new(ActiveRecord::Base) do
- set_primary_key 'foo'
- end
+ model = Class.new(ActiveRecord::Base)
+ model.primary_key = 'foo'
assert_equal 'foo', model.primary_key
- ActiveRecord::Base.establish_connection(connection)
+ ActiveRecord::Model.establish_connection(connection)
assert_equal 'foo', model.primary_key
end
end
+
+if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
+ class PrimaryKeyWithAnsiQuotesTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def test_primaery_key_method_with_ansi_quotes
+ con = ActiveRecord::Base.connection
+ con.execute("SET SESSION sql_mode='ANSI_QUOTES'")
+ assert_equal "id", con.primary_key("topics")
+ ensure
+ con.reconnect!
+ end
+
+ end
+end
+
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 9554386dcf..f36f4237b1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -3,7 +3,7 @@ require 'models/topic'
require 'models/task'
require 'models/category'
require 'models/post'
-
+require 'rack'
class QueryCacheTest < ActiveRecord::TestCase
fixtures :tasks, :topics, :categories, :posts, :categories_posts
@@ -43,6 +43,7 @@ class QueryCacheTest < ActiveRecord::TestCase
called = false
mw = ActiveRecord::QueryCache.new lambda { |env|
called = true
+ [200, {}, nil]
}
mw.call({})
assert called, 'middleware should delegate'
@@ -53,6 +54,7 @@ class QueryCacheTest < ActiveRecord::TestCase
Task.find 1
Task.find 1
assert_equal 1, ActiveRecord::Base.connection.query_cache.length
+ [200, {}, nil]
}
mw.call({})
end
@@ -62,6 +64,7 @@ class QueryCacheTest < ActiveRecord::TestCase
mw = ActiveRecord::QueryCache.new lambda { |env|
assert ActiveRecord::Base.connection.query_cache_enabled, 'cache on'
+ [200, {}, nil]
}
mw.call({})
end
@@ -83,7 +86,7 @@ class QueryCacheTest < ActiveRecord::TestCase
end
def test_cache_off_after_close
- mw = ActiveRecord::QueryCache.new lambda { |env| }
+ mw = ActiveRecord::QueryCache.new lambda { |env| [200, {}, nil] }
body = mw.call({}).last
assert ActiveRecord::Base.connection.query_cache_enabled, 'cache enabled'
@@ -94,6 +97,7 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_cache_clear_after_close
mw = ActiveRecord::QueryCache.new lambda { |env|
Post.find(:first)
+ [200, {}, nil]
}
body = mw.call({}).last
@@ -244,14 +248,3 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
end
end
end
-
-class QueryCacheBodyProxyTest < ActiveRecord::TestCase
-
- test "is polite to it's body and responds to it" do
- body = Class.new(String) { def to_path; "/path"; end }.new
- proxy = ActiveRecord::QueryCache::BodyProxy.new(nil, body, ActiveRecord::Base.connection_id)
- assert proxy.respond_to?(:to_path)
- assert_equal proxy.to_path, "/path"
- end
-
-end
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
new file mode 100644
index 0000000000..576ab60090
--- /dev/null
+++ b/activerecord/test/cases/reaper_test.rb
@@ -0,0 +1,81 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ReaperTest < ActiveRecord::TestCase
+ attr_reader :pool
+
+ def setup
+ super
+ @pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ end
+
+ def teardown
+ super
+ @pool.connections.each(&:close)
+ end
+
+ class FakePool
+ attr_reader :reaped
+
+ def initialize
+ @reaped = false
+ end
+
+ def reap
+ @reaped = true
+ end
+ end
+
+ # A reaper with nil time should never reap connections
+ def test_nil_time
+ fp = FakePool.new
+ assert !fp.reaped
+ reaper = ConnectionPool::Reaper.new(fp, nil)
+ reaper.run
+ assert !fp.reaped
+ end
+
+ def test_some_time
+ fp = FakePool.new
+ assert !fp.reaped
+
+ reaper = ConnectionPool::Reaper.new(fp, 0.0001)
+ reaper.run
+ until fp.reaped
+ Thread.pass
+ end
+ assert fp.reaped
+ end
+
+ def test_pool_has_reaper
+ assert pool.reaper
+ end
+
+ def test_reaping_frequency_configuration
+ spec = ActiveRecord::Base.connection_pool.spec.dup
+ spec.config[:reaping_frequency] = 100
+ pool = ConnectionPool.new spec
+ assert_equal 100, pool.reaper.frequency
+ end
+
+ def test_connection_pool_starts_reaper
+ spec = ActiveRecord::Base.connection_pool.spec.dup
+ spec.config[:reaping_frequency] = 0.0001
+
+ pool = ConnectionPool.new spec
+ pool.timeout = 0
+
+ conn = pool.checkout
+ count = pool.connections.length
+
+ conn.extend(Module.new { def active?; false; end; })
+
+ while count == pool.connections.length
+ Thread.pass
+ end
+ assert_equal(count - 1, pool.connections.length)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 69e9fc8d61..16f05f2198 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -36,25 +36,25 @@ class ReflectionTest < ActiveRecord::TestCase
def test_read_attribute_names
assert_equal(
- %w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type created_at updated_at ).sort,
+ %w( id title author_name author_email_address bonus_time written_on last_read content important group approved replies_count parent_id parent_title type created_at updated_at ).sort,
@first.attribute_names.sort
)
end
def test_columns
- assert_equal 16, Topic.columns.length
+ assert_equal 17, Topic.columns.length
end
def test_columns_are_returned_in_the_order_they_were_declared
column_names = Topic.columns.map { |column| column.name }
- assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type group created_at updated_at), column_names
+ assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content important approved replies_count parent_id parent_title type group created_at updated_at), column_names
end
def test_content_columns
content_columns = Topic.content_columns
content_column_names = content_columns.map {|column| column.name}
- assert_equal 12, content_columns.length
- assert_equal %w(title author_name author_email_address written_on bonus_time last_read content group approved parent_title created_at updated_at).sort, content_column_names.sort
+ assert_equal 13, content_columns.length
+ assert_equal %w(title author_name author_email_address written_on bonus_time last_read content important group approved parent_title created_at updated_at).sort, content_column_names.sort
end
def test_column_string_type_and_limit
@@ -189,8 +189,8 @@ class ReflectionTest < ActiveRecord::TestCase
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
- assert_equal 36, Firm.reflect_on_all_associations.size
- assert_equal 26, Firm.reflect_on_all_associations(:has_many).size
+ assert_equal 39, Firm.reflect_on_all_associations.size
+ assert_equal 29, Firm.reflect_on_all_associations(:has_many).size
assert_equal 10, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
@@ -319,6 +319,16 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s
end
+ def test_through_reflection_conditions_do_not_modify_other_reflections
+ orig_conds = Post.reflect_on_association(:first_blue_tags_2).conditions.inspect
+ Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions
+ assert_equal orig_conds, Post.reflect_on_association(:first_blue_tags_2).conditions.inspect
+ end
+
+ def test_symbol_for_class_name
+ assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass
+ end
+
private
def assert_reflection(klass, association, options)
assert reflection = klass.reflect_on_association(association)
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index 1e2093273e..edf38cb7a3 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -106,7 +106,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scoped_find_include
# with the include, will retrieve only developers for the given project
scoped_developers = Developer.includes(:projects).scoping do
- Developer.where('projects.id = 2').all
+ Developer.where('projects.id' => 2).all
end
assert scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
@@ -421,6 +421,12 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
+ def test_order_after_reorder_combines_orders
+ expected = Developer.order('name DESC, id DESC').collect { |dev| [dev.name, dev.id] }
+ received = Developer.order('name ASC').reorder('name DESC').order('id DESC').collect { |dev| [dev.name, dev.id] }
+ assert_equal expected, received
+ end
+
def test_nested_exclusive_scope
expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary }
received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index b23ead6feb..ac6dee3c6a 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -20,7 +20,7 @@ module ActiveRecord
end
def test_single_values
- assert_equal [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order].map(&:to_s).sort,
+ assert_equal [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq].map(&:to_s).sort,
Relation::SINGLE_VALUE_METHODS.map(&:to_s).sort
end
@@ -44,7 +44,7 @@ module ActiveRecord
end
def test_multi_value_methods
- assert_equal [:select, :group, :order, :joins, :where, :having, :bind].map(&:to_s).sort,
+ assert_equal [:select, :group, :order, :joins, :where, :having, :bind, :references].map(&:to_s).sort,
Relation::MULTI_VALUE_METHODS.map(&:to_s).sort
end
@@ -135,5 +135,24 @@ module ActiveRecord
relation.eager_load_values << :b
assert relation.eager_loading?
end
+
+ def test_references_values
+ relation = Relation.new :a, :b
+ assert_equal [], relation.references_values
+ relation = relation.references(:foo).references(:omg, :lol)
+ assert_equal ['foo', 'omg', 'lol'], relation.references_values
+ end
+
+ def test_references_values_dont_duplicate
+ relation = Relation.new :a, :b
+ relation = relation.references(:foo).references(:foo)
+ assert_equal ['foo'], relation.references_values
+ end
+
+ def test_apply_finder_options_takes_references
+ relation = Relation.new :a, :b
+ relation = relation.apply_finder_options(:references => :foo)
+ assert_equal ['foo'], relation.references_values
+ end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 95408a5f29..7b1d65c6db 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -4,11 +4,11 @@ require 'models/tagging'
require 'models/post'
require 'models/topic'
require 'models/comment'
-require 'models/reply'
require 'models/author'
require 'models/comment'
require 'models/entrant'
require 'models/developer'
+require 'models/reply'
require 'models/company'
require 'models/bird'
require 'models/car'
@@ -184,12 +184,12 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_complex_order_and_limit
- tags = Tag.includes(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").limit(1).to_a
+ tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").limit(1).to_a
assert_equal 1, tags.length
end
def test_finding_with_complex_order
- tags = Tag.includes(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a
+ tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a
assert_equal 3, tags.length
end
@@ -215,6 +215,19 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [2, 4, 6, 8, 10], even_ids.sort
end
+ def test_none
+ assert_no_queries do
+ assert_equal [], Developer.none
+ assert_equal [], Developer.scoped.none
+ end
+ end
+
+ def test_none_chainable
+ assert_no_queries do
+ assert_equal [], Developer.none.where(:name => 'David')
+ end
+ end
+
def test_joins_with_nil_argument
assert_nothing_raised { DependentFirm.joins(nil).first }
end
@@ -1104,7 +1117,9 @@ class RelationTest < ActiveRecord::TestCase
)
)
- assert scope.eager_loading?
+ assert_deprecated do
+ assert scope.eager_loading?
+ end
end
def test_ordering_with_extra_spaces
@@ -1148,4 +1163,65 @@ class RelationTest < ActiveRecord::TestCase
assert_equal posts(:thinking), comments(:more_greetings).post
assert_equal posts(:welcome), comments(:greetings).post
end
+
+ def test_uniq
+ tag1 = Tag.create(:name => 'Foo')
+ tag2 = Tag.create(:name => 'Foo')
+
+ query = Tag.select(:name).where(:id => [tag1.id, tag2.id])
+
+ assert_equal ['Foo', 'Foo'], query.map(&:name)
+ assert_sql(/DISTINCT/) do
+ assert_equal ['Foo'], query.uniq.map(&:name)
+ end
+ assert_sql(/DISTINCT/) do
+ assert_equal ['Foo'], query.uniq(true).map(&:name)
+ end
+ assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+ end
+
+ def test_references_triggers_eager_loading
+ scope = Post.includes(:comments)
+ assert !scope.eager_loading?
+ assert scope.references(:comments).eager_loading?
+ end
+
+ def test_references_doesnt_trigger_eager_loading_if_reference_not_included
+ scope = Post.references(:comments)
+ assert !scope.eager_loading?
+ end
+
+ def test_automatically_added_where_references
+ scope = Post.where(:comments => { :body => "Bla" })
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.where('comments.body' => 'Bla')
+ assert_equal ['comments'], scope.references_values
+ end
+
+ def test_automatically_added_having_references
+ scope = Post.having(:comments => { :body => "Bla" })
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.having('comments.body' => 'Bla')
+ assert_equal ['comments'], scope.references_values
+ end
+
+ def test_automatically_added_order_references
+ scope = Post.order('comments.body')
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.order('comments.body', 'yaks.body')
+ assert_equal ['comments', 'yaks'], scope.references_values
+
+ # Don't infer yaks, let's not go down that road again...
+ scope = Post.order('comments.body, yaks.body')
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.order('comments.body asc')
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.order('foo(comments.body)')
+ assert_equal [], scope.references_values
+ end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 71ff727b7f..3314013cd4 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -2,7 +2,13 @@ require "cases/helper"
class SchemaDumperTest < ActiveRecord::TestCase
+ def initialize(*)
+ super
+ ActiveRecord::SchemaMigration.create_table
+ end
+
def setup
+ super
@stream = StringIO.new
end
@@ -13,10 +19,18 @@ class SchemaDumperTest < ActiveRecord::TestCase
@stream.string
end
- if "string".encoding_aware?
- def test_magic_comment
- assert_match "# encoding: #{@stream.external_encoding.name}", standard_dump
+ def test_dump_schema_information_outputs_lexically_ordered_versions
+ versions = %w{ 20100101010101 20100201010101 20100301010101 }
+ versions.reverse.each do |v|
+ ActiveRecord::SchemaMigration.create!(:version => v)
end
+
+ schema_info = ActiveRecord::Base.connection.dump_schema_information
+ assert_match(/20100201010101.*20100301010101/m, schema_info)
+ end
+
+ def test_magic_comment
+ assert_match "# encoding: #{@stream.external_encoding.name}", standard_dump
end
def test_schema_dump
@@ -171,6 +185,15 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition
end
+ def test_schema_dumps_partial_indices
+ index_definition = standard_dump.split(/\n/).grep(/add_index.*company_partial_index/).first.strip
+ if current_adapter?(:PostgreSQLAdapter)
+ assert_equal 'add_index "companies", ["firm_id", "type"], :name => "company_partial_index", :where => "(rating > 10)"', index_definition
+ else
+ assert_equal 'add_index "companies", ["firm_id", "type"], :name => "company_partial_index"', index_definition
+ end
+ end
+
def test_schema_dump_should_honor_nonstandard_primary_keys
output = standard_dump
match = output.match(%r{create_table "movies"(.*)do})
@@ -213,6 +236,13 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
+ def test_schema_dump_includes_hstores_shorthand_definition
+ output = standard_dump
+ if %r{create_table "postgresql_hstores"} =~ output
+ assert_match %r{t.hstore "hash_store", default => ""}, output
+ end
+ end
+
def test_schema_dump_includes_tsvector_shorthand_definition
output = standard_dump
if %r{create_table "postgresql_tsvectors"} =~ output
@@ -238,4 +268,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r(:id => false), match[1], "no table id not preserved"
assert_match %r{t.string[[:space:]]+"id",[[:space:]]+:null => false$}, match[2], "non-primary key id column not preserved"
end
+
+ def test_schema_dump_keeps_id_false_when_id_is_false_and_unique_not_null_column_added
+ output = standard_dump
+ assert_match %r{create_table "subscribers", :id => false}, output
+ end
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 61b04b3e37..a4c065e667 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -13,7 +13,8 @@ class SerializationTest < ActiveRecord::TestCase
:created_at => Time.utc(2006, 8, 1),
:awesome => false,
:preferences => { :gem => '<strong>ruby</strong>' },
- :alternative_id => nil
+ :alternative_id => nil,
+ :id => nil
}
end
@@ -24,7 +25,7 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialize_should_be_reversible
- for format in FORMATS
+ FORMATS.each do |format|
@serialized = Contact.new.send("to_#{format}")
contact = Contact.new.send("from_#{format}", @serialized)
@@ -33,7 +34,7 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialize_should_allow_attribute_only_filtering
- for format in FORMATS
+ FORMATS.each do |format|
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
contact = Contact.new.send("from_#{format}", @serialized)
assert_equal @contact_attributes[:name], contact.name, "For #{format}"
@@ -42,7 +43,7 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialize_should_allow_attribute_except_filtering
- for format in FORMATS
+ FORMATS.each do |format|
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
contact = Contact.new.send("from_#{format}", @serialized)
assert_nil contact.name, "For #{format}"
diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb
index 258cee7aba..a3b8ab74d9 100644
--- a/activerecord/test/cases/session_store/session_test.rb
+++ b/activerecord/test/cases/session_store/session_test.rb
@@ -5,11 +5,15 @@ require 'active_record/session_store'
module ActiveRecord
class SessionStore
class SessionTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false unless supports_savepoints? && ActiveRecord::Base.connection.supports_ddl_transactions?
+ self.use_transactional_fixtures = false
+
+ attr_reader :session_klass
def setup
super
+ ActiveRecord::Base.connection.schema_cache.clear!
Session.drop_table! if Session.table_exists?
+ @session_klass = Class.new(Session)
end
def test_data_column_name
@@ -60,8 +64,8 @@ module ActiveRecord
def test_find_by_session_id
Session.create_table!
session_id = "10"
- s = Session.create!(:data => 'world', :session_id => session_id)
- t = Session.find_by_session_id(session_id)
+ s = session_klass.create!(:data => 'world', :session_id => session_id)
+ t = session_klass.find_by_session_id(session_id)
assert_equal s, t
assert_equal s.data, t.data
Session.drop_table!
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 074fd39e65..40520d6da2 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -4,14 +4,14 @@ require 'models/admin/user'
class StoreTest < ActiveRecord::TestCase
setup do
- @john = Admin::User.create(:name => 'John Doe', :color => 'black')
+ @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true)
end
test "reading store attributes through accessors" do
assert_equal 'black', @john.color
assert_nil @john.homepage
end
-
+
test "writing store attributes through accessors" do
@john.color = 'red'
@john.homepage = '37signals.com'
@@ -24,11 +24,20 @@ class StoreTest < ActiveRecord::TestCase
@john.settings[:icecream] = 'graeters'
@john.save
- assert 'graeters', @john.reload.settings[:icecream]
+ assert_equal 'graeters', @john.reload.settings[:icecream]
end
-
+
test "updating the store will mark it as changed" do
@john.color = 'red'
assert @john.settings_changed?
end
+
+ test "object initialization with not nullable column" do
+ assert_equal true, @john.remember_login
+ end
+
+ test "writing with not nullable column" do
+ @john.remember_login = false
+ assert_equal false, @john.remember_login
+ end
end
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
new file mode 100644
index 0000000000..94a13d386c
--- /dev/null
+++ b/activerecord/test/cases/test_case.rb
@@ -0,0 +1,10 @@
+require 'active_support/deprecation'
+ActiveSupport::Deprecation.silence do
+ require 'active_record/test_case'
+end
+
+ActiveRecord::TestCase.class_eval do
+ def sqlite3? connection
+ connection.class.name.split('::').last == "SQLite3Adapter"
+ end
+end
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 85f222bca2..f8b3e01a49 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -6,7 +6,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
fixtures :topics
class TopicWithCallbacks < ActiveRecord::Base
- set_table_name :topics
+ self.table_name = :topics
after_commit{|record| record.send(:do_after_commit, nil)}
after_commit(:on => :create){|record| record.send(:do_after_commit, :create)}
@@ -252,7 +252,7 @@ class TransactionObserverCallbacksTest < ActiveRecord::TestCase
fixtures :topics
class TopicWithObserverAttached < ActiveRecord::Base
- set_table_name :topics
+ self.table_name = :topics
def history
@history ||= []
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 110a18772f..203dd054f1 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -563,6 +563,7 @@ if current_adapter?(:PostgreSQLAdapter)
topic.approved = !topic.approved?
topic.save!
end
+ Topic.connection.close
end
end
@@ -598,6 +599,7 @@ if current_adapter?(:PostgreSQLAdapter)
dev = Developer.find(1)
assert_equal original_salary, dev.salary
end
+ Developer.connection.close
end
end
@@ -610,6 +612,7 @@ if current_adapter?(:PostgreSQLAdapter)
assert_equal original_salary, Developer.find(1).salary
end
end
+ Developer.connection.close
end
threads.each { |t| t.join }
diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb
index e82ca3f93d..5a69054445 100644
--- a/activerecord/test/cases/unconnected_test.rb
+++ b/activerecord/test/cases/unconnected_test.rb
@@ -7,13 +7,13 @@ class TestUnconnectedAdapter < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
- @underlying = ActiveRecord::Base.connection
- @specification = ActiveRecord::Base.remove_connection
+ @underlying = ActiveRecord::Model.connection
+ @specification = ActiveRecord::Model.remove_connection
end
def teardown
@underlying = nil
- ActiveRecord::Base.establish_connection(@specification)
+ ActiveRecord::Model.establish_connection(@specification)
load_schema if in_memory_db?
end
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index 56e345990f..c72b7b35cd 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -61,6 +61,16 @@ class AssociationValidationTest < ActiveRecord::TestCase
assert r.valid?
end
+ def test_validates_associated_marked_for_destruction
+ Topic.validates_associated(:replies)
+ Reply.validates_presence_of(:content)
+ t = Topic.new
+ t.replies << Reply.new
+ assert t.invalid?
+ t.replies.first.mark_for_destruction
+ assert t.valid?
+ end
+
def test_validates_associated_with_custom_message_using_quotes
Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
Topic.validates_presence_of :content
@@ -81,14 +91,12 @@ class AssociationValidationTest < ActiveRecord::TestCase
end
def test_validates_size_of_association_utf8
- with_kcode('UTF8') do
- assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
- o = Owner.new('name' => 'あいうえおかきくけこ')
- assert !o.save
- assert o.errors[:pets].any?
- o.pets.build('name' => 'あいうえおかきくけこ')
- assert o.valid?
- end
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
+ o = Owner.new('name' => 'あいうえおかきくけこ')
+ assert !o.save
+ assert o.errors[:pets].any?
+ o.pets.build('name' => 'あいうえおかきくけこ')
+ assert o.valid?
end
def test_validates_presence_of_belongs_to_association__parent_is_new_record
@@ -110,4 +118,21 @@ class AssociationValidationTest < ActiveRecord::TestCase
end
end
+ def test_validates_associated_models_in_the_same_context
+ Topic.validates_presence_of :title, :on => :custom_context
+ Topic.validates_associated :replies
+ Reply.validates_presence_of :title, :on => :custom_context
+
+ t = Topic.new('title' => '')
+ r = t.replies.new('title' => '')
+
+ assert t.valid?
+ assert !t.valid?(:custom_context)
+
+ t.title = "Longer"
+ assert !t.valid?(:custom_context), "Should NOT be valid if the associated object is not valid in the same context."
+
+ r.title = "Longer"
+ assert t.valid?(:custom_context), "Should be valid if the associated object is not valid in the same context."
+ end
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 0f1b3667cc..79442d68b0 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -45,6 +45,18 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert t2.save, "Should now save t2 as unique"
end
+ def test_validates_uniqueness_with_nil_value
+ Topic.validates_uniqueness_of(:title)
+
+ t = Topic.new("title" => nil)
+ assert t.save, "Should save t as unique"
+
+ t2 = Topic.new("title" => nil)
+ assert !t2.valid?, "Shouldn't be valid"
+ assert !t2.save, "Shouldn't save t2 as unique"
+ assert_equal ["has already been taken"], t2.errors[:title]
+ end
+
def test_validates_uniqueness_with_validates
Topic.validates :title, :uniqueness => true
Topic.create!('title' => 'abc')
@@ -80,6 +92,30 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert r3.valid?, "Saving r3"
end
+ def test_validate_uniqueness_with_object_scope
+ Reply.validates_uniqueness_of(:content, :scope => :topic)
+
+ t = Topic.create("title" => "I'm unique!")
+
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
+ def test_validate_uniqueness_with_object_arg
+ Reply.validates_uniqueness_of(:topic)
+
+ t = Topic.create("title" => "I'm unique!")
+
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
def test_validate_uniqueness_scoped_to_defining_class
t = Topic.create("title" => "What, me worry?")
@@ -149,16 +185,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert t2.valid?, "should validate with nil"
assert t2.save, "should save with nil"
- with_kcode('UTF8') do
- t_utf8 = Topic.new("title" => "Я тоже уникальный!")
- assert t_utf8.save, "Should save t_utf8 as unique"
+ t_utf8 = Topic.new("title" => "Я тоже уникальный!")
+ assert t_utf8.save, "Should save t_utf8 as unique"
- # If database hasn't UTF-8 character set, this test fails
- if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
- t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
- assert !t2_utf8.valid?, "Shouldn't be valid"
- assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
- end
+ # If database hasn't UTF-8 character set, this test fails
+ if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
+ t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
+ assert !t2_utf8.valid?, "Shouldn't be valid"
+ assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
end
end
@@ -256,13 +290,11 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_limit_and_utf8
- with_kcode('UTF8') do
- # Event.title is limited to 5 characters
- e1 = Event.create(:title => "一二三四五")
- assert e1.valid?, "Could not create an event with a unique, 5 character title"
- e2 = Event.create(:title => "一二三四五六七八")
- assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
- end
+ # Event.title is limited to 5 characters
+ e1 = Event.create(:title => "一二三四五")
+ assert e1.valid?, "Could not create an event with a unique, 5 character title"
+ e2 = Event.create(:title => "一二三四五六七八")
+ assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
end
def test_validate_straight_inheritance_uniqueness
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index c3e494866b..e575a98170 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -8,7 +8,7 @@ require 'models/parrot'
require 'models/company'
class ProtectedPerson < ActiveRecord::Base
- set_table_name 'people'
+ self.table_name = 'people'
attr_accessor :addon
attr_protected :first_name
end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 0b54c309d1..302913e095 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -1,14 +1,20 @@
-require "cases/helper"
+require 'cases/helper'
require 'models/topic'
class YamlSerializationTest < ActiveRecord::TestCase
fixtures :topics
def test_to_yaml_with_time_with_zone_should_not_raise_exception
+ tz = Time.zone
Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
ActiveRecord::Base.time_zone_aware_attributes = true
+
topic = Topic.new(:written_on => DateTime.now)
assert_nothing_raised { topic.to_yaml }
+
+ ensure
+ Time.zone = tz
+ ActiveRecord::Base.time_zone_aware_attributes = false
end
def test_roundtrip
@@ -18,6 +24,11 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal topic, t
end
+ def test_roundtrip_serialized_column
+ topic = Topic.new(:content => {:omg=>:lol})
+ assert_equal({:omg=>:lol}, YAML.load(YAML.dump(topic)).content)
+ end
+
def test_encode_with_coder
topic = Topic.first
coder = {}
@@ -25,22 +36,17 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal({'attributes' => topic.attributes}, coder)
end
- begin
- require 'psych'
-
- def test_psych_roundtrip
- topic = Topic.first
- assert topic
- t = Psych.load Psych.dump topic
- assert_equal topic, t
- end
-
- def test_psych_roundtrip_new_object
- topic = Topic.new
- assert topic
- t = Psych.load Psych.dump topic
- assert_equal topic.attributes, t.attributes
- end
- rescue LoadError
+ def test_psych_roundtrip
+ topic = Topic.first
+ assert topic
+ t = Psych.load Psych.dump topic
+ assert_equal topic, t
+ end
+
+ def test_psych_roundtrip_new_object
+ topic = Topic.new
+ assert topic
+ t = Psych.load Psych.dump topic
+ assert_equal topic.attributes, t.attributes
end
end
diff --git a/activerecord/test/fixtures/admin/randomly_named_a9.yml b/activerecord/test/fixtures/admin/randomly_named_a9.yml
new file mode 100644
index 0000000000..bc51c83112
--- /dev/null
+++ b/activerecord/test/fixtures/admin/randomly_named_a9.yml
@@ -0,0 +1,7 @@
+first_instance:
+ some_attribute: AAA
+ another_attribute: 000
+
+second_instance:
+ some_attribute: BBB
+ another_attribute: 999
diff --git a/activerecord/test/fixtures/admin/randomly_named_b0.yml b/activerecord/test/fixtures/admin/randomly_named_b0.yml
new file mode 100644
index 0000000000..bc51c83112
--- /dev/null
+++ b/activerecord/test/fixtures/admin/randomly_named_b0.yml
@@ -0,0 +1,7 @@
+first_instance:
+ some_attribute: AAA
+ another_attribute: 000
+
+second_instance:
+ some_attribute: BBB
+ another_attribute: 999
diff --git a/activerecord/test/fixtures/colleges.yml b/activerecord/test/fixtures/colleges.yml
new file mode 100644
index 0000000000..27591e0c2c
--- /dev/null
+++ b/activerecord/test/fixtures/colleges.yml
@@ -0,0 +1,3 @@
+FIU:
+ id: 1
+ name: Florida International University
diff --git a/activerecord/test/fixtures/courses.yml b/activerecord/test/fixtures/courses.yml
index 5ee1916003..de3a4a97e5 100644
--- a/activerecord/test/fixtures/courses.yml
+++ b/activerecord/test/fixtures/courses.yml
@@ -1,6 +1,7 @@
ruby:
id: 1
name: Ruby Development
+ college: FIU
java:
id: 2
diff --git a/activerecord/test/fixtures/developers.yml b/activerecord/test/fixtures/developers.yml
index 308bf75de2..3656564f63 100644
--- a/activerecord/test/fixtures/developers.yml
+++ b/activerecord/test/fixtures/developers.yml
@@ -8,7 +8,7 @@ jamis:
name: Jamis
salary: 150000
-<% for digit in 3..10 %>
+<% (3..10).each do |digit| %>
dev_<%= digit %>:
id: <%= digit %>
name: fixture_<%= digit %>
diff --git a/activerecord/test/fixtures/dog_lovers.yml b/activerecord/test/fixtures/dog_lovers.yml
new file mode 100644
index 0000000000..d3e5e4a1aa
--- /dev/null
+++ b/activerecord/test/fixtures/dog_lovers.yml
@@ -0,0 +1,4 @@
+david:
+ id: 1
+ bred_dogs_count: 0
+ trained_dogs_count: 1
diff --git a/activerecord/test/fixtures/dogs.yml b/activerecord/test/fixtures/dogs.yml
new file mode 100644
index 0000000000..16d19be2c5
--- /dev/null
+++ b/activerecord/test/fixtures/dogs.yml
@@ -0,0 +1,3 @@
+sophie:
+ id: 1
+ trainer_id: 1
diff --git a/activerecord/test/fixtures/other_topics.yml b/activerecord/test/fixtures/other_topics.yml
new file mode 100644
index 0000000000..93f48aedc4
--- /dev/null
+++ b/activerecord/test/fixtures/other_topics.yml
@@ -0,0 +1,42 @@
+first:
+ id: 1
+ title: The First Topic
+ author_name: David
+ author_email_address: david@loudthinking.com
+ written_on: 2003-07-16t15:28:11.2233+01:00
+ last_read: 2004-04-15
+ bonus_time: 2005-01-30t15:28:00.00+01:00
+ content: Have a nice day
+ approved: false
+ replies_count: 1
+
+second:
+ id: 2
+ title: The Second Topic of the day
+ author_name: Mary
+ written_on: 2004-07-15t15:28:00.0099+01:00
+ content: Have a nice day
+ approved: true
+ replies_count: 0
+ parent_id: 1
+ type: Reply
+
+third:
+ id: 3
+ title: The Third Topic of the day
+ author_name: Carl
+ written_on: 2005-07-15t15:28:00.0099+01:00
+ content: I'm a troll
+ approved: true
+ replies_count: 1
+
+fourth:
+ id: 4
+ title: The Fourth Topic of the day
+ author_name: Carl
+ written_on: 2006-07-15t15:28:00.0099+01:00
+ content: Why not?
+ approved: true
+ type: Reply
+ parent_id: 3
+
diff --git a/activerecord/test/fixtures/randomly_named_a9.yml b/activerecord/test/fixtures/randomly_named_a9.yml
new file mode 100644
index 0000000000..bc51c83112
--- /dev/null
+++ b/activerecord/test/fixtures/randomly_named_a9.yml
@@ -0,0 +1,7 @@
+first_instance:
+ some_attribute: AAA
+ another_attribute: 000
+
+second_instance:
+ some_attribute: BBB
+ another_attribute: 999
diff --git a/activerecord/test/fixtures/teapots.yml b/activerecord/test/fixtures/teapots.yml
new file mode 100644
index 0000000000..ff515beb45
--- /dev/null
+++ b/activerecord/test/fixtures/teapots.yml
@@ -0,0 +1,3 @@
+bob:
+ id: 1
+ name: Bob
diff --git a/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb b/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb
deleted file mode 100644
index ffb224dad9..0000000000
--- a/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class MigrationThatRaisesException < ActiveRecord::Migration
- def self.up
- add_column "people", "last_name", :string
- raise 'Something broke'
- end
-
- def self.down
- remove_column "people", "last_name"
- end
-end
diff --git a/activerecord/test/migrations/duplicate/3_foo.rb b/activerecord/test/migrations/duplicate/3_foo.rb
deleted file mode 100644
index 916fe580fa..0000000000
--- a/activerecord/test/migrations/duplicate/3_foo.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Foo < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- end
-end \ No newline at end of file
diff --git a/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb b/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb
deleted file mode 100644
index 5fe5089e18..0000000000
--- a/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Chunky < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- end
-end
diff --git a/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb b/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb
deleted file mode 100644
index 5fe5089e18..0000000000
--- a/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Chunky < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb
deleted file mode 100644
index bf912fbfc8..0000000000
--- a/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class InterleavedInnocentJointable < ActiveRecord::Migration
- def self.up
- create_table("people_reminders", :id => false) do |t|
- t.column :reminder_id, :integer
- t.column :person_id, :integer
- end
- end
-
- def self.down
- drop_table "people_reminders"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb
deleted file mode 100644
index c6c94213a0..0000000000
--- a/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class InterleavedPeopleHaveLastNames < ActiveRecord::Migration
- def self.up
- add_column "people", "last_name", :string
- end
-
- def self.down
- remove_column "people", "last_name"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb
deleted file mode 100644
index bf912fbfc8..0000000000
--- a/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class InterleavedInnocentJointable < ActiveRecord::Migration
- def self.up
- create_table("people_reminders", :id => false) do |t|
- t.column :reminder_id, :integer
- t.column :person_id, :integer
- end
- end
-
- def self.down
- drop_table "people_reminders"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb
deleted file mode 100644
index c6c94213a0..0000000000
--- a/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class InterleavedPeopleHaveLastNames < ActiveRecord::Migration
- def self.up
- add_column "people", "last_name", :string
- end
-
- def self.down
- remove_column "people", "last_name"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb b/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb
deleted file mode 100644
index 6849995f5e..0000000000
--- a/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class InterleavedIRaiseOnDown < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- raise
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb
deleted file mode 100644
index bf912fbfc8..0000000000
--- a/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class InterleavedInnocentJointable < ActiveRecord::Migration
- def self.up
- create_table("people_reminders", :id => false) do |t|
- t.column :reminder_id, :integer
- t.column :person_id, :integer
- end
- end
-
- def self.down
- drop_table "people_reminders"
- end
-end
diff --git a/activerecord/test/migrations/rename/1_we_need_things.rb b/activerecord/test/migrations/rename/1_we_need_things.rb
new file mode 100644
index 0000000000..cdbe0b1679
--- /dev/null
+++ b/activerecord/test/migrations/rename/1_we_need_things.rb
@@ -0,0 +1,11 @@
+class WeNeedThings < ActiveRecord::Migration
+ def self.up
+ create_table("things") do |t|
+ t.column :content, :text
+ end
+ end
+
+ def self.down
+ drop_table "things"
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/migrations/rename/2_rename_things.rb b/activerecord/test/migrations/rename/2_rename_things.rb
new file mode 100644
index 0000000000..d441b71fc9
--- /dev/null
+++ b/activerecord/test/migrations/rename/2_rename_things.rb
@@ -0,0 +1,9 @@
+class RenameThings < ActiveRecord::Migration
+ def self.up
+ rename_table "things", "awesome_things"
+ end
+
+ def self.down
+ rename_table "awesome_things", "things"
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
new file mode 100644
index 0000000000..e438cf5999
--- /dev/null
+++ b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "hobbies", :string
+ end
+
+ def self.down
+ remove_column "people", "hobbies"
+ end
+end
diff --git a/activerecord/test/migrations/duplicate/1_people_have_last_names.rb b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
index 81af5fef5e..06cb911117 100644
--- a/activerecord/test/migrations/duplicate/1_people_have_last_names.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
@@ -1,4 +1,4 @@
-class PeopleHaveLastNames < ActiveRecord::Migration
+class ValidPeopleHaveLastNames < ActiveRecord::Migration
def self.up
add_column "people", "last_name", :string
end
@@ -6,4 +6,4 @@ class PeopleHaveLastNames < ActiveRecord::Migration
def self.down
remove_column "people", "last_name"
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/migrations/duplicate/2_we_need_reminders.rb b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
index d5e71ce8ef..d5e71ce8ef 100644
--- a/activerecord/test/migrations/duplicate/2_we_need_reminders.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
diff --git a/activerecord/test/migrations/duplicate/3_innocent_jointable.rb b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
index 21c9ca5328..21c9ca5328 100644
--- a/activerecord/test/migrations/duplicate/3_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
diff --git a/activerecord/test/models/admin/randomly_named_c1.rb b/activerecord/test/models/admin/randomly_named_c1.rb
new file mode 100644
index 0000000000..2f81d5b831
--- /dev/null
+++ b/activerecord/test/models/admin/randomly_named_c1.rb
@@ -0,0 +1,3 @@
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table
+end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index c12c88e195..d0e628bd50 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,4 +1,5 @@
class Admin::User < ActiveRecord::Base
belongs_to :account
store :settings, :accessors => [ :color, :homepage ]
+ store :preferences, :accessors => [ :remember_login ]
end
diff --git a/activerecord/test/models/arunit2_model.rb b/activerecord/test/models/arunit2_model.rb
new file mode 100644
index 0000000000..04b8b15d3d
--- /dev/null
+++ b/activerecord/test/models/arunit2_model.rb
@@ -0,0 +1,3 @@
+class ARUnit2Model < ActiveRecord::Base
+ self.abstract_class = true
+end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 23db5650d4..d50e11d6c9 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -49,12 +49,12 @@ class Author < ActiveRecord::Base
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
- has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0"
+ has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => { 'comments.post_id' => 0 }
has_many :nonexistant_comments, :through => :posts
has_many :hello_posts, :class_name => "Post", :conditions => "posts.body = 'hello'"
has_many :hello_post_comments, :through => :hello_posts, :source => :comments
- has_many :posts_with_no_comments, :class_name => 'Post', :conditions => 'comments.id is null', :include => :comments
+ has_many :posts_with_no_comments, :class_name => 'Post', :conditions => { 'comments.id' => nil }, :include => :comments
has_many :hello_posts_with_hash_conditions, :class_name => "Post",
:conditions => {:body => 'hello'}
@@ -128,7 +128,6 @@ class Author < ActiveRecord::Base
belongs_to :author_address, :dependent => :destroy
belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
- has_many :post_categories, :through => :posts, :source => :categories
has_many :category_post_comments, :through => :categories, :source => :post_comments
has_many :misc_posts, :class_name => 'Post',
diff --git a/activerecord/test/models/bird.rb b/activerecord/test/models/bird.rb
index e61d48e6a5..dff099c1fb 100644
--- a/activerecord/test/models/bird.rb
+++ b/activerecord/test/models/bird.rb
@@ -1,9 +1,12 @@
class Bird < ActiveRecord::Base
+ belongs_to :pirate
validates_presence_of :name
+ accepts_nested_attributes_for :pirate
+
attr_accessor :cancel_save_from_callback
before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
def cancel_save_callback_method
false
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index b9c2e8ec9a..6ff1329d8e 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -9,7 +9,7 @@ class Car < ActiveRecord::Base
has_many :tyres
has_many :engines, :dependent => :destroy
- has_many :wheels, :as => :wheelable
+ has_many :wheels, :as => :wheelable, :dependent => :destroy
scope :incl_tyres, includes(:tyres)
scope :incl_engines, includes(:engines)
diff --git a/activerecord/test/models/college.rb b/activerecord/test/models/college.rb
new file mode 100644
index 0000000000..c7495d7deb
--- /dev/null
+++ b/activerecord/test/models/college.rb
@@ -0,0 +1,5 @@
+require_dependency 'models/arunit2_model'
+
+class College < ARUnit2Model
+ has_many :courses
+end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index c1f7a4171a..fbdfaa2c29 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -4,7 +4,7 @@ end
class Company < AbstractCompany
attr_protected :rating
- set_sequence_name :companies_nonstd_seq
+ self.sequence_name = :companies_nonstd_seq
validates_presence_of :name
@@ -42,8 +42,10 @@ class Firm < Company
:before_remove => :log_before_remove,
:after_remove => :log_after_remove
has_many :unsorted_clients, :class_name => "Client"
+ has_many :unsorted_clients_with_symbol, :class_name => :Client
has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
+ has_many :clients_ordered_by_name, :order => "name", :class_name => "Client"
has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
@@ -87,6 +89,8 @@ class Firm < Company
has_many :accounts
has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+ has_many :association_with_references, :class_name => 'Client', :references => :foo
+
def log
@log ||= []
end
diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb
index 15e3a1de0b..7db9a4e731 100644
--- a/activerecord/test/models/country.rb
+++ b/activerecord/test/models/country.rb
@@ -1,6 +1,6 @@
class Country < ActiveRecord::Base
- set_primary_key :country_id
+ self.primary_key = :country_id
has_and_belongs_to_many :treaties
diff --git a/activerecord/test/models/course.rb b/activerecord/test/models/course.rb
index 8a40fa740d..f3d0e05ff7 100644
--- a/activerecord/test/models/course.rb
+++ b/activerecord/test/models/course.rb
@@ -1,3 +1,6 @@
-class Course < ActiveRecord::Base
+require_dependency 'models/arunit2_model'
+
+class Course < ARUnit2Model
+ belongs_to :college
has_many :entrants
end
diff --git a/activerecord/test/models/dashboard.rb b/activerecord/test/models/dashboard.rb
index a8a25834b1..1b3b54545f 100644
--- a/activerecord/test/models/dashboard.rb
+++ b/activerecord/test/models/dashboard.rb
@@ -1,3 +1,3 @@
class Dashboard < ActiveRecord::Base
- set_primary_key :dashboard_id
-end \ No newline at end of file
+ self.primary_key = :dashboard_id
+end
diff --git a/activerecord/test/models/dog.rb b/activerecord/test/models/dog.rb
new file mode 100644
index 0000000000..72b7d33a86
--- /dev/null
+++ b/activerecord/test/models/dog.rb
@@ -0,0 +1,4 @@
+class Dog < ActiveRecord::Base
+ belongs_to :breeder, :class_name => "DogLover", :counter_cache => :bred_dogs_count
+ belongs_to :trainer, :class_name => "DogLover", :counter_cache => :trained_dogs_count
+end
diff --git a/activerecord/test/models/dog_lover.rb b/activerecord/test/models/dog_lover.rb
new file mode 100644
index 0000000000..a33dc575c5
--- /dev/null
+++ b/activerecord/test/models/dog_lover.rb
@@ -0,0 +1,4 @@
+class DogLover < ActiveRecord::Base
+ has_many :trained_dogs, :class_name => "Dog", :foreign_key => :trainer_id
+ has_many :bred_dogs, :class_name => "Dog", :foreign_key => :breeder_id
+end
diff --git a/activerecord/test/models/joke.rb b/activerecord/test/models/joke.rb
index d7f01e59e6..edda4655dc 100644
--- a/activerecord/test/models/joke.rb
+++ b/activerecord/test/models/joke.rb
@@ -1,7 +1,7 @@
class Joke < ActiveRecord::Base
- set_table_name 'funny_jokes'
+ self.table_name = 'funny_jokes'
end
class GoodJoke < ActiveRecord::Base
- set_table_name 'funny_jokes'
+ self.table_name = 'funny_jokes'
end
diff --git a/activerecord/test/models/keyboard.rb b/activerecord/test/models/keyboard.rb
index 32a4a7fad0..39347e274e 100644
--- a/activerecord/test/models/keyboard.rb
+++ b/activerecord/test/models/keyboard.rb
@@ -1,3 +1,3 @@
class Keyboard < ActiveRecord::Base
- set_primary_key 'key_number'
+ self.primary_key = 'key_number'
end
diff --git a/activerecord/test/models/legacy_thing.rb b/activerecord/test/models/legacy_thing.rb
index eaeb642d12..eead181a0e 100644
--- a/activerecord/test/models/legacy_thing.rb
+++ b/activerecord/test/models/legacy_thing.rb
@@ -1,3 +1,3 @@
class LegacyThing < ActiveRecord::Base
- set_locking_column :version
+ self.locking_column = :version
end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
index b96c054f6c..3fcd5e4b69 100644
--- a/activerecord/test/models/liquid.rb
+++ b/activerecord/test/models/liquid.rb
@@ -1,5 +1,5 @@
class Liquid < ActiveRecord::Base
- set_table_name :liquid
+ self.table_name = :liquid
has_many :molecules, :uniq => true
end
diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb
index 830cdb5796..4fe79720ad 100644
--- a/activerecord/test/models/minivan.rb
+++ b/activerecord/test/models/minivan.rb
@@ -1,5 +1,5 @@
class Minivan < ActiveRecord::Base
- set_primary_key :minivan_id
+ self.primary_key = :minivan_id
belongs_to :speedometer
has_one :dashboard, :through => :speedometer
diff --git a/activerecord/test/models/mixed_case_monkey.rb b/activerecord/test/models/mixed_case_monkey.rb
index 853f2682b3..763baefd91 100644
--- a/activerecord/test/models/mixed_case_monkey.rb
+++ b/activerecord/test/models/mixed_case_monkey.rb
@@ -1,3 +1,3 @@
class MixedCaseMonkey < ActiveRecord::Base
- set_primary_key 'monkeyID'
+ self.primary_key = 'monkeyID'
end
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
index 5760b991ec..fea55f4535 100644
--- a/activerecord/test/models/owner.rb
+++ b/activerecord/test/models/owner.rb
@@ -1,5 +1,5 @@
class Owner < ActiveRecord::Base
- set_primary_key :owner_id
+ self.primary_key = :owner_id
has_many :pets
has_many :toys, :through => :pets
end
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index 737ef9131b..c4ee2bd19d 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -1,5 +1,6 @@
class Parrot < ActiveRecord::Base
- set_inheritance_column :parrot_sti_class
+ self.inheritance_column = :parrot_sti_class
+
has_and_belongs_to_many :pirates
has_and_belongs_to_many :treasures
has_many :loots, :as => :looter
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 967a3625aa..d2a0c6b40c 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -3,7 +3,8 @@ class Person < ActiveRecord::Base
has_one :reader
has_many :posts, :through => :readers
- has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null'
+ has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments,
+ :conditions => 'comments.id is null', :references => :comments
has_many :references
has_many :bad_references
@@ -54,7 +55,7 @@ class LoosePerson < ActiveRecord::Base
self.table_name = 'people'
self.abstract_class = true
- attr_protected :comments
+ attr_protected :comments, :best_friend_id, :best_friend_of_id
attr_protected :as => :admin
has_one :best_friend, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
@@ -81,4 +82,4 @@ class TightPerson < ActiveRecord::Base
accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends
end
-class TightDescendant < TightPerson; end \ No newline at end of file
+class TightDescendant < TightPerson; end
diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb
index 113826756a..3cd5bceed5 100644
--- a/activerecord/test/models/pet.rb
+++ b/activerecord/test/models/pet.rb
@@ -2,7 +2,7 @@ class Pet < ActiveRecord::Base
attr_accessor :current_user
- set_primary_key :pet_id
+ self.primary_key = :pet_id
belongs_to :owner, :touch => true
has_many :toys
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 198a963cbc..1cab78d8c7 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -24,6 +24,10 @@ class Post < ActiveRecord::Base
belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
+ def first_comment
+ super.body
+ end
+ has_one :first_comment, :class_name => 'Comment', :order => 'id ASC'
has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} }
@@ -40,6 +44,10 @@ class Post < ActiveRecord::Base
def newest
created.last
end
+
+ def the_association
+ proxy_association
+ end
end
has_many :author_favorites, :through => :author
@@ -181,4 +189,4 @@ end
class SpecialPostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope where(:id => [1, 5,6])
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/randomly_named_c1.rb b/activerecord/test/models/randomly_named_c1.rb
new file mode 100644
index 0000000000..18a86c4989
--- /dev/null
+++ b/activerecord/test/models/randomly_named_c1.rb
@@ -0,0 +1,3 @@
+class ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table
+end
diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb
index 94743eff8e..0a7d38d8ec 100644
--- a/activerecord/test/models/speedometer.rb
+++ b/activerecord/test/models/speedometer.rb
@@ -1,4 +1,4 @@
class Speedometer < ActiveRecord::Base
- set_primary_key :speedometer_id
+ self.primary_key = :speedometer_id
belongs_to :dashboard
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/string_key_object.rb b/activerecord/test/models/string_key_object.rb
index f8d4c6e0e4..f084ec1bdc 100644
--- a/activerecord/test/models/string_key_object.rb
+++ b/activerecord/test/models/string_key_object.rb
@@ -1,3 +1,3 @@
class StringKeyObject < ActiveRecord::Base
- set_primary_key :id
+ self.primary_key = :id
end
diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb
index 5b78014e6f..76e85a0cd3 100644
--- a/activerecord/test/models/subscriber.rb
+++ b/activerecord/test/models/subscriber.rb
@@ -1,5 +1,5 @@
class Subscriber < ActiveRecord::Base
- set_primary_key 'nick'
+ self.primary_key = 'nick'
has_many :subscriptions
has_many :books, :through => :subscriptions
end
diff --git a/activerecord/test/models/teapot.rb b/activerecord/test/models/teapot.rb
new file mode 100644
index 0000000000..b035b18c1b
--- /dev/null
+++ b/activerecord/test/models/teapot.rb
@@ -0,0 +1,35 @@
+class Teapot
+ # I'm a little teapot,
+ # Short and stout,
+ # Here is my handle
+ # Here is my spout
+ # When I get all steamed up,
+ # Hear me shout,
+ # Tip me over and pour me out!
+ #
+ # HELL YEAH TEAPOT SONG
+
+ include ActiveRecord::Model
+end
+
+class OtherTeapot < Teapot
+end
+
+class OMFGIMATEAPOT
+ def aaahhh
+ "mmm"
+ end
+end
+
+class CoolTeapot < OMFGIMATEAPOT
+ include ActiveRecord::Model
+ self.table_name = "teapots"
+end
+
+class Ceiling
+ include ActiveRecord::Model
+
+ class Teapot
+ include ActiveRecord::Model
+ end
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index fe424e61b2..8bcb9df8a8 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -8,6 +8,8 @@ class Topic < ActiveRecord::Base
scope :approved, :conditions => {:approved => true}
scope :rejected, :conditions => {:approved => false}
+ scope :scope_with_lambda, lambda { scoped }
+
scope :by_lifo, :conditions => {:author_name => 'lifo'}
scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}}
@@ -78,6 +80,11 @@ class Topic < ActiveRecord::Base
after_initialize :set_email_address
+ class_attribute :after_initialize_called
+ after_initialize do
+ self.class.after_initialize_called = true
+ end
+
def approved=(val)
@custom_approved = val
write_attribute(:approved, val)
@@ -106,6 +113,10 @@ class Topic < ActiveRecord::Base
def after_create_for_transaction; end
end
+class ImportantTopic < Topic
+ serialize :important, Hash
+end
+
module Web
class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
diff --git a/activerecord/test/models/toy.rb b/activerecord/test/models/toy.rb
index 6c45e99671..0377e50011 100644
--- a/activerecord/test/models/toy.rb
+++ b/activerecord/test/models/toy.rb
@@ -1,5 +1,5 @@
class Toy < ActiveRecord::Base
- set_primary_key :toy_id
+ self.primary_key = :toy_id
belongs_to :pet
scope :with_pet, joins(:pet)
diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb
index b46537f0d2..41fd1350f3 100644
--- a/activerecord/test/models/treaty.rb
+++ b/activerecord/test/models/treaty.rb
@@ -1,6 +1,6 @@
class Treaty < ActiveRecord::Base
- set_primary_key :treaty_id
+ self.primary_key = :treaty_id
has_and_belongs_to_many :countries
diff --git a/activerecord/test/models/warehouse_thing.rb b/activerecord/test/models/warehouse_thing.rb
index 6ace1183cc..f20bd1a245 100644
--- a/activerecord/test/models/warehouse_thing.rb
+++ b/activerecord/test/models/warehouse_thing.rb
@@ -1,5 +1,5 @@
class WarehouseThing < ActiveRecord::Base
- set_table_name "warehouse-things"
+ self.table_name = "warehouse-things"
validates_uniqueness_of :value
-end \ No newline at end of file
+end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 5cf9a207f3..25b416a906 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,6 +1,6 @@
ActiveRecord::Schema.define do
- %w(postgresql_tsvectors postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings
+ %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings
postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones).each do |table_name|
execute "DROP TABLE IF EXISTS #{quote_table_name table_name}"
end
@@ -63,6 +63,15 @@ _SQL
);
_SQL
+ if 't' == select_value("select 'hstore'=ANY(select typname from pg_type)")
+ execute <<_SQL
+ CREATE TABLE postgresql_hstores (
+ id SERIAL PRIMARY KEY,
+ hash_store hstore default ''::hstore
+ );
+_SQL
+ end
+
execute <<_SQL
CREATE TABLE postgresql_moneys (
id SERIAL PRIMARY KEY,
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index bb08f5c181..428a85ab4e 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -37,7 +37,8 @@ ActiveRecord::Schema.define do
create_table :admin_users, :force => true do |t|
t.string :name
- t.text :settings
+ t.text :settings, :null => true
+ t.text :preferences, :null => false, :default => ""
t.references :account
end
@@ -107,6 +108,7 @@ ActiveRecord::Schema.define do
t.string :name
t.integer :engines_count
t.integer :wheels_count
+ t.column :lock_version, :integer, :null => false, :default => 0
end
create_table :categories, :force => true do |t|
@@ -173,6 +175,7 @@ ActiveRecord::Schema.define do
end
add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index"
+ add_index :companies, [:firm_id, :type], :name => "company_partial_index", :where => "rating > 10"
create_table :computers, :force => true do |t|
t.integer :developer, :null => false
@@ -212,6 +215,16 @@ ActiveRecord::Schema.define do
t.integer :access_level, :default => 1
end
+ create_table :dog_lovers, :force => true do |t|
+ t.integer :trained_dogs_count, :default => 0
+ t.integer :bred_dogs_count, :default => 0
+ end
+
+ create_table :dogs, :force => true do |t|
+ t.integer :trainer_id
+ t.integer :breeder_id
+ end
+
create_table :edges, :force => true, :id => false do |t|
t.column :source_id, :integer, :null => false
t.column :sink_id, :integer, :null => false
@@ -505,6 +518,11 @@ ActiveRecord::Schema.define do
t.string :type
end
+ create_table :randomly_named_table, :force => true do |t|
+ t.string :some_attribute
+ t.integer :another_attribute
+ end
+
create_table :ratings, :force => true do |t|
t.integer :comment_id
t.integer :value
@@ -596,6 +614,12 @@ ActiveRecord::Schema.define do
t.datetime :ending
end
+ create_table :teapots, :force => true do |t|
+ t.string :name
+ t.string :type
+ t.timestamps
+ end
+
create_table :topics, :force => true do |t|
t.string :title
t.string :author_name
@@ -607,8 +631,10 @@ ActiveRecord::Schema.define do
# Oracle SELECT WHERE clause which causes many unit test failures
if current_adapter?(:OracleAdapter)
t.string :content, :limit => 4000
+ t.string :important, :limit => 4000
else
t.text :content
+ t.text :important
end
t.boolean :approved, :default => true
t.integer :replies_count, :default => 0
@@ -700,8 +726,6 @@ ActiveRecord::Schema.define do
create_table :countries_treaties, :force => true, :id => false do |t|
t.string :country_id, :null => false
t.string :treaty_id, :null => false
- t.datetime :created_at
- t.datetime :updated_at
end
create_table :liquid, :force => true do |t|
@@ -736,4 +760,9 @@ end
Course.connection.create_table :courses, :force => true do |t|
t.column :name, :string, :null => false
+ t.column :college_id, :integer
+end
+
+College.connection.create_table :colleges, :force => true do |t|
+ t.column :name, :string, :null => false
end
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index a39794fa39..11154c3797 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -1,4 +1,5 @@
-require 'logger'
+require 'active_support/logger'
+require_dependency 'models/college'
require_dependency 'models/course'
module ARTest
@@ -12,9 +13,9 @@ module ARTest
def self.connect
puts "Using #{connection_name} with Identity Map #{ActiveRecord::IdentityMap.enabled? ? 'on' : 'off'}"
- ActiveRecord::Base.logger = Logger.new("debug.log")
- ActiveRecord::Base.configurations = connection_config
- ActiveRecord::Base.establish_connection 'arunit'
- Course.establish_connection 'arunit2'
+ ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log")
+ ActiveRecord::Model.configurations = connection_config
+ ActiveRecord::Model.establish_connection 'arunit'
+ ARUnit2Model.establish_connection 'arunit2'
end
end
diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG
deleted file mode 100644
index bd496a1263..0000000000
--- a/activeresource/CHANGELOG
+++ /dev/null
@@ -1,337 +0,0 @@
-*Rails 3.2.0 (unreleased)*
-
-* Redirect responses: 303 See Other and 307 Temporary Redirect now behave like
- 301 Moved Permanently and 302 Found. GH #3302.
-
- [Jim Herz]
-
-
-*Rails 3.1.1 (October 7, 2011)*
-
-* No changes.
-
-
-*Rails 3.1.0 (August 30, 2011)*
-
-* The default format has been changed to JSON for all requests. If you want to continue to use XML you will need to set `self.format = :xml` in the class. eg.
-
-class User < ActiveResource::Base
- self.format = :xml
-end
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.6 (April 5, 2011)
-
-* No changes.
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* No changes.
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* No changes
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* No Changes, just a version bump.
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. [Santiago Pastorino]
-
-* Add support for errors in JSON format. #1956 [Fabien Jakimowicz]
-
-* Recognizes 410 as Resource Gone. #2316 [Jordan Brough, Jatinder Singh]
-
-* More thorough SSL support. #2370 [Roy Nicholson]
-
-* HTTP proxy support. #2133 [Marshall Huss, Sébastien Dabet]
-
-
-*2.3.2 [Final] (March 15, 2009)*
-
-* Nothing new, just included in 2.3.2
-
-
-*2.2.1 [RC2] (November 14th, 2008)*
-
-* Fixed that ActiveResource#post would post an empty string when it shouldn't be posting anything #525 [Paolo Angelini]
-
-
-*2.2.0 [RC1] (October 24th, 2008)*
-
-* Add ActiveResource::Base#to_xml and ActiveResource::Base#to_json. #1011 [Rasik Pandey, Cody Fauser]
-
-* Add ActiveResource::Base.find(:last). [#754 state:resolved] (Adrian Mugnolo)
-
-* Fixed problems with the logger used if the logging string included %'s [#840 state:resolved] (Jamis Buck)
-
-* Fixed Base#exists? to check status code as integer [#299 state:resolved] (Wes Oldenbeuving)
-
-
-*2.1.0 (May 31st, 2008)*
-
-* Fixed response logging to use length instead of the entire thing (seangeo) [#27]
-
-* Fixed that to_param should be used and honored instead of hardcoding the id #11406 [gspiers]
-
-* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-
-* Use HEAD instead of GET in exists? [bscofield]
-
-* Fix small documentation typo. Closes #10670 [Luca Guidi]
-
-* find_or_create_resource_for handles module nesting. #10646 [xavier]
-
-* Allow setting ActiveResource::Base#format before #site. [Rick Olson]
-
-* Support agnostic formats when calling custom methods. Closes #10635 [joerichsen]
-
-* Document custom methods. #10589 [Cheah Chu Yeow]
-
-* Ruby 1.9 compatibility. [Jeremy Kemper]
-
-
-*2.0.2 (December 16th, 2007)*
-
-* Added more specific exceptions for 400, 401, and 403 (all descending from ClientError so existing rescues will work) #10326 [trek]
-
-* Correct empty response handling. #10445 [seangeo]
-
-
-*2.0.1 (December 7th, 2007)*
-
-* Don't cache net/http object so that ActiveResource is more thread-safe. Closes #10142 [kou]
-
-* Update XML documentation examples to include explicit type attributes. Closes #9754 [Josh Susser]
-
-* Added one-off declarations of mock behavior [David Heinemeier Hansson]. Example:
-
- Before:
- ActiveResource::HttpMock.respond_to do |mock|
- mock.get "/people/1.xml", {}, "<person><name>David</name></person>"
- end
-
- Now:
- ActiveResource::HttpMock.respond_to.get "/people/1.xml", {}, "<person><name>David</name></person>"
-
-* Added ActiveResource.format= which defaults to :xml but can also be set to :json [David Heinemeier Hansson]. Example:
-
- class Person < ActiveResource::Base
- self.site = "http://app/"
- self.format = :json
- end
-
- person = Person.find(1) # => GET http://app/people/1.json
- person.name = "David"
- person.save # => PUT http://app/people/1.json {name: "David"}
-
- Person.format = :xml
- person.name = "Mary"
- person.save # => PUT http://app/people/1.json <person><name>Mary</name></person>
-
-* Fix reload error when path prefix is used. #8727 [Ian Warshak]
-
-* Remove ActiveResource::Struct because it hasn't proven very useful. Creating a new ActiveResource::Base subclass is often less code and always clearer. #8612 [Josh Peek]
-
-* Fix query methods on resources. [Cody Fauser]
-
-* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [Eloy Duran]
-
-* Recognize and raise an exception on 405 Method Not Allowed responses. #7692 [Josh Peek]
-
-* Handle string and symbol param keys when splitting params into prefix params and query params.
-
- Comment.find(:all, :params => { :article_id => 5, :page => 2 }) or Comment.find(:all, :params => { 'article_id' => 5, :page => 2 })
-
-* Added find-one with symbol [David Heinemeier Hansson]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
-
-* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [David Heinemeier Hansson]. Changes:
-
- Person.find(:all, :title => "CEO") ...becomes: Person.find(:all, :params => { :title => "CEO" })
- Person.find(:managers) ...becomes: Person.find(:all, :from => :managers)
- Person.find("/companies/1/manager.xml") ...becomes: Person.find(:one, :from => "/companies/1/manager.xml")
-
-* Add support for setting custom headers per Active Resource model [Rick Olson]
-
- class Project
- headers['X-Token'] = 'foo'
- end
-
- # makes the GET request with the custom X-Token header
- Project.find(:all)
-
-* Added find-by-path options to ActiveResource::Base.find [David Heinemeier Hansson]. Examples:
-
- employees = Person.find(:all, :from => "/companies/1/people.xml") # => GET /companies/1/people.xml
- manager = Person.find("/companies/1/manager.xml") # => GET /companies/1/manager.xml
-
-
-* Added support for using classes from within a single nested module [David Heinemeier Hansson]. Example:
-
- module Highrise
- class Note < ActiveResource::Base
- self.site = "http://37s.sunrise.i:3000"
- end
-
- class Comment < ActiveResource::Base
- self.site = "http://37s.sunrise.i:3000"
- end
- end
-
- assert_kind_of Highrise::Comment, Note.find(1).comments.first
-
-
-* Added load_attributes_from_response as a way of loading attributes from other responses than just create [David Heinemeier Hansson]
-
- class Highrise::Task < ActiveResource::Base
- def complete
- load_attributes_from_response(post(:complete))
- end
- end
-
- ...will set "done_at" when complete is called.
-
-
-* Added support for calling custom methods #6979 [rwdaigle]
-
- Person.find(:managers) # => GET /people/managers.xml
- Kase.find(1).post(:close) # => POST /kases/1/close.xml
-
-* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick Olson]
- ActiveResource splits the prefix_options from it automatically.
-
-* Allow ActiveResource::Base.delete with custom prefix. [Rick Olson]
-
-* Add ActiveResource::Base#dup [Rick Olson]
-
-* Fixed constant warning when fetching the same object multiple times [David Heinemeier Hansson]
-
-* Added that saves which get a body response (and not just a 201) will use that response to update themselves [David Heinemeier Hansson]
-
-* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [David Heinemeier Hansson]
-
-* Allow array and hash query parameters. #7756 [Greg Spurrier]
-
-* Loading a resource preserves its prefix_options. #7353 [Ryan Daigle]
-
-* Carry over the convenience of #create from ActiveRecord. Closes #7340. [Ryan Daigle]
-
-* Increase ActiveResource::Base test coverage. Closes #7173, #7174 [Rich Collins]
-
-* Interpret 422 Unprocessable Entity as ResourceInvalid. #7097 [dkubb]
-
-* Mega documentation patches. #7025, #7069 [rwdaigle]
-
-* Base.exists?(id, options) and Base#exists? check whether the resource is found. #6970 [rwdaigle]
-
-* Query string support. [untext, Jeremy Kemper]
- # GET /forums/1/topics.xml?sort=created_at
- Topic.find(:all, :forum_id => 1, :sort => 'created_at')
-
-* Base#==, eql?, and hash methods. == returns true if its argument is identical to self or if it's an instance of the same class, is not new?, and has the same id. eql? is an alias for ==. hash delegates to id. [Jeremy Kemper]
-
-* Allow subclassed resources to share the site info [Rick Olson, Jeremy Kemper]
-d
- class BeastResource < ActiveResource::Base
- self.site = 'http://beast.caboo.se'
- end
-
- class Forum < BeastResource
- # taken from BeastResource
- # self.site = 'http://beast.caboo.se'
- end
-
- class Topic < BeastResource
- self.site += '/forums/:forum_id'
- end
-
-* Fix issues with ActiveResource collection handling. Closes #6291. [bmilekic]
-
-* Use attr_accessor_with_default to dry up attribute initialization. References #6538. [Stuart Halloway]
-
-* Add basic logging support for logging outgoing requests. [Jamis Buck]
-
-* Add Base.delete for deleting resources without having to instantiate them first. [Jamis Buck]
-
-* Make #save behavior mimic AR::Base#save (true on success, false on failure). [Jamis Buck]
-
-* Add Basic HTTP Authentication to ActiveResource (closes #6305). [jonathan]
-
-* Extracted #id_from_response as an entry point for customizing how a created resource gets its own ID.
- By default, it extracts from the Location response header.
-
-* Optimistic locking: raise ActiveResource::ResourceConflict on 409 Conflict response. [Jeremy Kemper]
-
- # Example controller action
- def update
- @person.save!
- rescue ActiveRecord::StaleObjectError
- render :xml => @person.reload.to_xml, :status => '409 Conflict'
- end
-
-* Basic validation support [Rick Olson]
-
- Parses the xml response of ActiveRecord::Errors#to_xml with a similar interface to ActiveRecord::Errors.
-
- render :xml => @person.errors.to_xml, :status => '400 Validation Error'
-
-* Deep hashes are converted into collections of resources. [Jeremy Kemper]
- Person.new :name => 'Bob',
- :address => { :id => 1, :city => 'Portland' },
- :contacts => [{ :id => 1 }, { :id => 2 }]
- Looks for Address and Contact resources and creates them if unavailable.
- So clients can fetch a complex resource in a single request if you e.g.
- render :xml => @person.to_xml(:include => [:address, :contacts])
- in your controller action.
-
-* Major updates [Rick Olson]
-
- * Add full support for find/create/update/destroy
- * Add support for specifying prefixes.
- * Allow overriding of element_name, collection_name, and primary key
- * Provide simpler HTTP mock interface for testing
-
- # rails routing code
- map.resources :posts do |post|
- post.resources :comments
- end
-
- # ActiveResources
- class Post < ActiveResource::Base
- self.site = "http://37s.sunrise.i:3000/"
- end
-
- class Comment < ActiveResource::Base
- self.site = "http://37s.sunrise.i:3000/posts/:post_id/"
- end
-
- @post = Post.find 5
- @comments = Comment.find :all, :post_id => @post.id
-
- @comment = Comment.new({:body => 'hello world'}, {:post_id => @post.id})
- @comment.save
-
-* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. [Jeremy Kemper]
-
-* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [David Heinemeier Hansson]
diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md
new file mode 100644
index 0000000000..a69bfd0d73
--- /dev/null
+++ b/activeresource/CHANGELOG.md
@@ -0,0 +1,334 @@
+## Rails 3.2.0 (January 20, 2012) ##
+
+* Redirect responses: 303 See Other and 307 Temporary Redirect now behave like
+ 301 Moved Permanently and 302 Found. GH #3302.
+
+ *Jim Herz*
+
+
+## Rails 3.1.1 (October 7, 2011) ##
+
+* No changes.
+
+
+## Rails 3.1.0 (August 30, 2011) ##
+
+* The default format has been changed to JSON for all requests. If you want to continue to use XML you will need to set `self.format = :xml` in the class. eg.
+
+ class User < ActiveResource::Base self.format = :xml
+ end
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* No changes.
+
+
+* Rails 3.0.6 (April 5, 2011)
+
+* No changes.
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* No changes.
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* No changes
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* No Changes, just a version bump.
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. *Santiago Pastorino*
+
+* Add support for errors in JSON format. #1956 *Fabien Jakimowicz*
+
+* Recognizes 410 as Resource Gone. #2316 *Jordan Brough, Jatinder Singh*
+
+* More thorough SSL support. #2370 *Roy Nicholson*
+
+* HTTP proxy support. #2133 *Marshall Huss, Sébastien Dabet*
+
+
+## 2.3.2 Final (March 15, 2009) ##
+
+* Nothing new, just included in 2.3.2
+
+
+## 2.2.1 RC2 (November 14th, 2008) ##
+
+* Fixed that ActiveResource#post would post an empty string when it shouldn't be posting anything #525 *Paolo Angelini*
+
+
+## 2.2.0 RC1 (October 24th, 2008) ##
+
+* Add ActiveResource::Base#to_xml and ActiveResource::Base#to_json. #1011 *Rasik Pandey, Cody Fauser*
+
+* Add ActiveResource::Base.find(:last). [#754 state:resolved] (Adrian Mugnolo)
+
+* Fixed problems with the logger used if the logging string included %'s [#840 state:resolved] (Jamis Buck)
+
+* Fixed Base#exists? to check status code as integer [#299 state:resolved] (Wes Oldenbeuving)
+
+
+## 2.1.0 (May 31st, 2008) ##
+
+* Fixed response logging to use length instead of the entire thing (seangeo) *#27*
+
+* Fixed that to_param should be used and honored instead of hardcoding the id #11406 *gspiers*
+
+* Improve documentation. *Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert*
+
+* Use HEAD instead of GET in exists? *bscofield*
+
+* Fix small documentation typo. Closes #10670 *Luca Guidi*
+
+* find_or_create_resource_for handles module nesting. #10646 *xavier*
+
+* Allow setting ActiveResource::Base#format before #site. *Rick Olson*
+
+* Support agnostic formats when calling custom methods. Closes #10635 *joerichsen*
+
+* Document custom methods. #10589 *Cheah Chu Yeow*
+
+* Ruby 1.9 compatibility. *Jeremy Kemper*
+
+
+## 2.0.2 (December 16th, 2007) ##
+
+* Added more specific exceptions for 400, 401, and 403 (all descending from ClientError so existing rescues will work) #10326 *trek*
+
+* Correct empty response handling. #10445 *seangeo*
+
+
+## 2.0.1 (December 7th, 2007) ##
+
+* Don't cache net/http object so that ActiveResource is more thread-safe. Closes #10142 *kou*
+
+* Update XML documentation examples to include explicit type attributes. Closes #9754 *Josh Susser*
+
+* Added one-off declarations of mock behavior [David Heinemeier Hansson]. Example:
+
+ Before:
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, "<person><name>David</name></person>"
+ end
+
+ Now:
+ ActiveResource::HttpMock.respond_to.get "/people/1.xml", {}, "<person><name>David</name></person>"
+
+* Added ActiveResource.format= which defaults to :xml but can also be set to :json [David Heinemeier Hansson]. Example:
+
+ class Person < ActiveResource::Base
+ self.site = "http://app/"
+ self.format = :json
+ end
+
+ person = Person.find(1) # => GET http://app/people/1.json
+ person.name = "David"
+ person.save # => PUT http://app/people/1.json {name: "David"}
+
+ Person.format = :xml
+ person.name = "Mary"
+ person.save # => PUT http://app/people/1.json <person><name>Mary</name></person>
+
+* Fix reload error when path prefix is used. #8727 *Ian Warshak*
+
+* Remove ActiveResource::Struct because it hasn't proven very useful. Creating a new ActiveResource::Base subclass is often less code and always clearer. #8612 *Josh Peek*
+
+* Fix query methods on resources. *Cody Fauser*
+
+* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 *Eloy Duran*
+
+* Recognize and raise an exception on 405 Method Not Allowed responses. #7692 *Josh Peek*
+
+* Handle string and symbol param keys when splitting params into prefix params and query params.
+
+ Comment.find(:all, :params => { :article_id => 5, :page => 2 }) or Comment.find(:all, :params => { 'article_id' => 5, :page => 2 })
+
+* Added find-one with symbol [David Heinemeier Hansson]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
+
+* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [David Heinemeier Hansson]. Changes:
+
+ Person.find(:all, :title => "CEO") ...becomes: Person.find(:all, :params => { :title => "CEO" })
+ Person.find(:managers) ...becomes: Person.find(:all, :from => :managers)
+ Person.find("/companies/1/manager.xml") ...becomes: Person.find(:one, :from => "/companies/1/manager.xml")
+
+* Add support for setting custom headers per Active Resource model *Rick Olson*
+
+ class Project
+ headers['X-Token'] = 'foo'
+ end
+
+ \# makes the GET request with the custom X-Token header
+ Project.find(:all)
+
+* Added find-by-path options to ActiveResource::Base.find [David Heinemeier Hansson]. Examples:
+
+ employees = Person.find(:all, :from => "/companies/1/people.xml") # => GET /companies/1/people.xml
+ manager = Person.find("/companies/1/manager.xml") # => GET /companies/1/manager.xml
+
+
+* Added support for using classes from within a single nested module [David Heinemeier Hansson]. Example:
+
+ module Highrise
+ class Note < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+
+ class Comment < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+ end
+
+ assert_kind_of Highrise::Comment, Note.find(1).comments.first
+
+
+* Added load_attributes_from_response as a way of loading attributes from other responses than just create *David Heinemeier Hansson*
+
+ class Highrise::Task < ActiveResource::Base
+ def complete
+ load_attributes_from_response(post(:complete))
+ end
+ end
+
+ ...will set "done_at" when complete is called.
+
+
+* Added support for calling custom methods #6979 *rwdaigle*
+
+ Person.find(:managers) # => GET /people/managers.xml
+ Kase.find(1).post(:close) # => POST /kases/1/close.xml
+
+* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. *Rick Olson*
+ ActiveResource splits the prefix_options from it automatically.
+
+* Allow ActiveResource::Base.delete with custom prefix. *Rick Olson*
+
+* Add ActiveResource::Base#dup *Rick Olson*
+
+* Fixed constant warning when fetching the same object multiple times *David Heinemeier Hansson*
+
+* Added that saves which get a body response (and not just a 201) will use that response to update themselves *David Heinemeier Hansson*
+
+* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" *David Heinemeier Hansson*
+
+* Allow array and hash query parameters. #7756 *Greg Spurrier*
+
+* Loading a resource preserves its prefix_options. #7353 *Ryan Daigle*
+
+* Carry over the convenience of #create from ActiveRecord. Closes #7340. *Ryan Daigle*
+
+* Increase ActiveResource::Base test coverage. Closes #7173, #7174 *Rich Collins*
+
+* Interpret 422 Unprocessable Entity as ResourceInvalid. #7097 *dkubb*
+
+* Mega documentation patches. #7025, #7069 *rwdaigle*
+
+* Base.exists?(id, options) and Base#exists? check whether the resource is found. #6970 *rwdaigle*
+
+* Query string support. *untext, Jeremy Kemper*
+ # GET /forums/1/topics.xml?sort=created_at
+ Topic.find(:all, :forum_id => 1, :sort => 'created_at')
+
+* Base#==, eql?, and hash methods. == returns true if its argument is identical to self or if it's an instance of the same class, is not new?, and has the same id. eql? is an alias for ==. hash delegates to id. *Jeremy Kemper*
+
+* Allow subclassed resources to share the site info *Rick Olson, Jeremy Kemper*
+ d class BeastResource < ActiveResource::Base
+ self.site = 'http://beast.caboo.se'
+ end
+
+ class Forum < BeastResource
+ # taken from BeastResource
+ # self.site = 'http://beast.caboo.se'
+ end
+
+ class Topic < BeastResource
+ self.site += '/forums/:forum_id'
+ end
+
+* Fix issues with ActiveResource collection handling. Closes #6291. *bmilekic*
+
+* Use attr_accessor_with_default to dry up attribute initialization. References #6538. *Stuart Halloway*
+
+* Add basic logging support for logging outgoing requests. *Jamis Buck*
+
+* Add Base.delete for deleting resources without having to instantiate them first. *Jamis Buck*
+
+* Make #save behavior mimic AR::Base#save (true on success, false on failure). *Jamis Buck*
+
+* Add Basic HTTP Authentication to ActiveResource (closes #6305). *jonathan*
+
+* Extracted #id_from_response as an entry point for customizing how a created resource gets its own ID.
+ By default, it extracts from the Location response header.
+
+* Optimistic locking: raise ActiveResource::ResourceConflict on 409 Conflict response. *Jeremy Kemper*
+
+ # Example controller action
+ def update
+ @person.save!
+ rescue ActiveRecord::StaleObjectError
+ render :xml => @person.reload.to_xml, :status => '409 Conflict'
+ end
+
+* Basic validation support *Rick Olson*
+
+ Parses the xml response of ActiveRecord::Errors#to_xml with a similar interface to ActiveRecord::Errors.
+
+ render :xml => @person.errors.to_xml, :status => '400 Validation Error'
+
+* Deep hashes are converted into collections of resources. *Jeremy Kemper*
+ Person.new :name => 'Bob',
+ :address => { :id => 1, :city => 'Portland' },
+ :contacts => [{ :id => 1 }, { :id => 2 }]
+ Looks for Address and Contact resources and creates them if unavailable.
+ So clients can fetch a complex resource in a single request if you e.g.
+ render :xml => @person.to_xml(:include => [:address, :contacts])
+ in your controller action.
+
+* Major updates *Rick Olson*
+
+ * Add full support for find/create/update/destroy
+ * Add support for specifying prefixes.
+ * Allow overriding of element_name, collection_name, and primary key
+ * Provide simpler HTTP mock interface for testing
+
+ # rails routing code
+ map.resources :posts do |post|
+ post.resources :comments
+ end
+
+ # ActiveResources
+ class Post < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000/"
+ end
+
+ class Comment < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000/posts/:post_id/"
+ end
+
+ @post = Post.find 5
+ @comments = Comment.find :all, :post_id => @post.id
+
+ @comment = Comment.new({:body => 'hello world'}, {:post_id => @post.id})
+ @comment.save
+
+* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. *Jeremy Kemper*
+
+* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. *David Heinemeier Hansson*
diff --git a/activeresource/MIT-LICENSE b/activeresource/MIT-LICENSE
index 216b6e5ba0..187e748f83 100644
--- a/activeresource/MIT-LICENSE
+++ b/activeresource/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2006-2011 David Heinemeier Hansson
+Copyright (c) 2006-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activeresource/README.rdoc b/activeresource/README.rdoc
index c86289c5fe..8170f29973 100644
--- a/activeresource/README.rdoc
+++ b/activeresource/README.rdoc
@@ -172,7 +172,9 @@ Destruction of a resource can be invoked as a class and instance method of the r
== License
-Active Support is released under the MIT license.
+Active Support is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/activeresource/Rakefile b/activeresource/Rakefile
index b1c18ff189..b85d8c7eb5 100755
--- a/activeresource/Rakefile
+++ b/activeresource/Rakefile
@@ -12,6 +12,7 @@ Rake::TestTask.new { |t|
t.libs << "test"
t.pattern = 'test/**/*_test.rb'
t.warning = true
+ t.verbose = true
}
namespace :test do
@@ -33,7 +34,7 @@ end
task :lines do
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
- for file_name in FileList["lib/active_resource/**/*.rb"]
+ FileList["lib/active_resource/**/*.rb"].each do |file_name|
next if file_name =~ /vendor/
f = File.open(file_name)
diff --git a/activeresource/activeresource.gemspec b/activeresource/activeresource.gemspec
index a8772ecf8c..ae1972a7d7 100644
--- a/activeresource/activeresource.gemspec
+++ b/activeresource/activeresource.gemspec
@@ -7,13 +7,13 @@ Gem::Specification.new do |s|
s.summary = 'REST modeling framework (part of Rails).'
s.description = 'REST on Rails. Wrap your RESTful web app with Ruby classes and work with them like Active Record models.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']
+ s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']
s.require_path = 'lib'
s.extra_rdoc_files = %w( README.rdoc )
diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb
index 0794a1a800..ab06086631 100644
--- a/activeresource/lib/active_resource.rb
+++ b/activeresource/lib/active_resource.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2006-2011 David Heinemeier Hansson
+# Copyright (c) 2006-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 10cc727bd9..5ef50b6e03 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -25,29 +25,29 @@ module ActiveResource
#
# == Automated mapping
#
- # Active Resource objects represent your RESTful resources as manipulatable Ruby objects. To map resources
+ # Active Resource objects represent your RESTful resources as manipulatable Ruby objects. To map resources
# to Ruby objects, Active Resource only needs a class name that corresponds to the resource name (e.g., the class
# Person maps to the resources people, very similarly to Active Record) and a +site+ value, which holds the
# URI of the resources.
#
# class Person < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
+ # self.site = "https://api.people.com"
# end
#
- # Now the Person class is mapped to RESTful resources located at <tt>http://api.people.com:3000/people/</tt>, and
+ # Now the Person class is mapped to RESTful resources located at <tt>https://api.people.com/people/</tt>, and
# you can now use Active Resource's life cycle methods to manipulate resources. In the case where you already have
# an existing model with the same name as the desired RESTful resource you can set the +element_name+ value.
#
# class PersonResource < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
+ # self.site = "https://api.people.com"
# self.element_name = "person"
# end
#
# If your Active Resource object is required to use an HTTP proxy you can set the +proxy+ value which holds a URI.
#
# class PersonResource < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
- # self.proxy = "http://user:password@proxy.people.com:8080"
+ # self.site = "https://api.people.com"
+ # self.proxy = "https://user:password@proxy.people.com:8080"
# end
#
#
@@ -103,7 +103,7 @@ module ActiveResource
# You can validate resources client side by overriding validation methods in the base class.
#
# class Person < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
+ # self.site = "https://api.people.com"
# protected
# def validate
# errors.add("last", "has invalid characters") unless last =~ /[a-zA-Z]*/
@@ -114,47 +114,64 @@ module ActiveResource
#
# == Authentication
#
- # Many REST APIs will require authentication, usually in the form of basic
- # HTTP authentication. Authentication can be specified by:
+ # Many REST APIs require authentication. The HTTP spec describes two ways to
+ # make requests with a username and password (see RFC 2617).
#
- # === HTTP Basic Authentication
- # * putting the credentials in the URL for the +site+ variable.
+ # Basic authentication simply sends a username and password along with HTTP
+ # requests. These sensitive credentials are sent unencrypted, visible to
+ # any onlooker, so this scheme should only be used with SSL.
+ #
+ # Digest authentication sends a crytographic hash of the username, password,
+ # HTTP method, URI, and a single-use secret key provided by the server.
+ # Sensitive credentials aren't visible to onlookers, so digest authentication
+ # doesn't require SSL. However, this doesn't mean the connection is secure!
+ # Just the username and password.
+ #
+ # (You really, really want to use SSL. There's little reason not to.)
+ #
+ # === Picking an authentication scheme
+ #
+ # Basic authentication is the default. To switch to digest authentication,
+ # set +auth_type+ to +:digest+:
#
# class Person < ActiveResource::Base
- # self.site = "http://ryan:password@api.people.com:3000/"
+ # self.auth_type = :digest
# end
#
- # * defining +user+ and/or +password+ variables
+ # === Setting the username and password
+ #
+ # Set +user+ and +password+ on the class, or include them in the +site+ URL.
#
# class Person < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
+ # # Set user and password directly:
# self.user = "ryan"
# self.password = "password"
- # end
#
- # For obvious security reasons, it is probably best if such services are available
- # over HTTPS.
- #
- # Note: Some values cannot be provided in the URL passed to site. e.g. email addresses
- # as usernames. In those situations you should use the separate user and password option.
+ # # Or include them in the site:
+ # self.site = "https://ryan:password@api.people.com"
+ # end
#
# === Certificate Authentication
#
- # * End point uses an X509 certificate for authentication. <tt>See ssl_options=</tt> for all options.
+ # You can also authenticate using an X509 certificate. <tt>See ssl_options=</tt> for all options.
#
# class Person < ActiveResource::Base
# self.site = "https://secure.api.people.com/"
- # self.ssl_options = {:cert => OpenSSL::X509::Certificate.new(File.open(pem_file))
- # :key => OpenSSL::PKey::RSA.new(File.open(pem_file)),
- # :ca_path => "/path/to/OpenSSL/formatted/CA_Certs",
- # :verify_mode => OpenSSL::SSL::VERIFY_PEER}
+ #
+ # File.open(pem_file_path, 'rb') do |pem_file|
+ # self.ssl_options = {
+ # cert: OpenSSL::X509::Certificate.new(pem_file),
+ # key: OpenSSL::PKey::RSA.new(pem_file),
+ # ca_path: "/path/to/OpenSSL/formatted/CA_Certs",
+ # verify_mode: OpenSSL::SSL::VERIFY_PEER }
+ # end
# end
#
#
# == Errors & Validation
#
# Error handling and validation is handled in much the same manner as you're used to seeing in
- # Active Record. Both the response code in the HTTP response and the body of the response are used to
+ # Active Record. Both the response code in the HTTP response and the body of the response are used to
# indicate that an error occurred.
#
# === Resource errors
@@ -163,7 +180,7 @@ module ActiveResource
# response code will be returned from the server which will raise an ActiveResource::ResourceNotFound
# exception.
#
- # # GET http://api.people.com:3000/people/999.json
+ # # GET https://api.people.com/people/999.json
# ryan = Person.find(999) # 404, raises ActiveResource::ResourceNotFound
#
#
@@ -185,7 +202,7 @@ module ActiveResource
# * Other - ActiveResource::ConnectionError
#
# These custom exceptions allow you to deal with resource errors more naturally and with more precision
- # rather than returning a general HTTP error. For example:
+ # rather than returning a general HTTP error. For example:
#
# begin
# ryan = Person.find(my_id)
@@ -199,7 +216,7 @@ module ActiveResource
# an ActiveResource::MissingPrefixParam will be raised.
#
# class Comment < ActiveResource::Base
- # self.site = "http://someip.com/posts/:post_id/"
+ # self.site = "https://someip.com/posts/:post_id"
# end
#
# Comment.find(1)
@@ -208,8 +225,8 @@ module ActiveResource
# === Validation errors
#
# Active Resource supports validations on resources and will return errors if any of these validations fail
- # (e.g., "First name can not be blank" and so on). These types of errors are denoted in the response by
- # a response code of <tt>422</tt> and an XML or JSON representation of the validation errors. The save operation will
+ # (e.g., "First name can not be blank" and so on). These types of errors are denoted in the response by
+ # a response code of <tt>422</tt> and an XML or JSON representation of the validation errors. The save operation will
# then fail (with a <tt>false</tt> return value) and the validation errors can be accessed on the resource in question.
#
# ryan = Person.find(1)
@@ -217,20 +234,29 @@ module ActiveResource
# ryan.save # => false
#
# # When
- # # PUT http://api.people.com:3000/people/1.json
+ # # PUT https://api.people.com/people/1.xml
# # or
- # # PUT http://api.people.com:3000/people/1.json
+ # # PUT https://api.people.com/people/1.json
# # is requested with invalid values, the response is:
# #
# # Response (422):
# # <errors><error>First cannot be empty</error></errors>
# # or
- # # {"errors":["First cannot be empty"]}
+ # # {"errors":{"first":["cannot be empty"]}}
# #
#
# ryan.errors.invalid?(:first) # => true
# ryan.errors.full_messages # => ['First cannot be empty']
#
+ # For backwards-compatibility with older endpoints, the following formats are also supported in JSON responses:
+ #
+ # # {"errors":['First cannot be empty']}
+ # # This was the required format for previous versions of ActiveResource
+ # # {"first":["cannot be empty"]}
+ # # This was the default format produced by respond_with in ActionController <3.2.1
+ #
+ # Parsing either of these formats will result in a deprecation warning.
+ #
# Learn more about Active Resource's validation features in the ActiveResource::Validations documentation.
#
# === Timeouts
@@ -240,7 +266,7 @@ module ActiveResource
# amount of time before Active Resource times out with the +timeout+ variable.
#
# class Person < ActiveResource::Base
- # self.site = "http://api.people.com:3000/"
+ # self.site = "https://api.people.com"
# self.timeout = 5
# end
#
@@ -383,22 +409,22 @@ module ActiveResource
@known_attributes ||= []
end
- # Gets the URI of the REST resources to map for this class. The site variable is required for
+ # Gets the URI of the REST resources to map for this class. The site variable is required for
# Active Resource's mapping to work.
def site
# Not using superclass_delegating_reader because don't want subclasses to modify superclass instance
#
# With superclass_delegating_reader
#
- # Parent.site = 'http://anonymous@test.com'
- # Subclass.site # => 'http://anonymous@test.com'
+ # Parent.site = 'https://anonymous@test.com'
+ # Subclass.site # => 'https://anonymous@test.com'
# Subclass.site.user = 'david'
- # Parent.site # => 'http://david@test.com'
+ # Parent.site # => 'https://david@test.com'
#
# Without superclass_delegating_reader (expected behavior)
#
- # Parent.site = 'http://anonymous@test.com'
- # Subclass.site # => 'http://anonymous@test.com'
+ # Parent.site = 'https://anonymous@test.com'
+ # Subclass.site # => 'https://anonymous@test.com'
# Subclass.site.user = 'david' # => TypeError: can't modify frozen object
#
if defined?(@site)
@@ -592,7 +618,7 @@ module ActiveResource
prefix(options)
end
- # An attribute reader for the source string for the resource path \prefix. This
+ # An attribute reader for the source string for the resource path \prefix. This
# method is regenerated at runtime based on what the \prefix is set to.
def prefix_source
prefix # generate #prefix and #prefix_source methods first
@@ -625,7 +651,7 @@ module ActiveResource
alias_method :set_element_name, :element_name= #:nodoc:
alias_method :set_collection_name, :collection_name= #:nodoc:
- # Gets the element path for the given ID in +id+. If the +query_options+ parameter is omitted, Rails
+ # Gets the element path for the given ID in +id+. If the +query_options+ parameter is omitted, Rails
# will split from the \prefix options.
#
# ==== Options
@@ -638,7 +664,7 @@ module ActiveResource
# # => /posts/1.json
#
# class Comment < ActiveResource::Base
- # self.site = "http://37s.sunrise.i/posts/:post_id/"
+ # self.site = "https://37s.sunrise.com/posts/:post_id"
# end
#
# Comment.element_path(1, :post_id => 5)
@@ -668,7 +694,7 @@ module ActiveResource
# # => /posts/new.json
#
# class Comment < ActiveResource::Base
- # self.site = "http://37s.sunrise.i/posts/:post_id/"
+ # self.site = "https://37s.sunrise.com/posts/:post_id"
# end
#
# Comment.collection_path(:post_id => 5)
@@ -677,7 +703,7 @@ module ActiveResource
"#{prefix(prefix_options)}#{collection_name}/new.#{format.extension}"
end
- # Gets the collection path for the REST resources. If the +query_options+ parameter is omitted, Rails
+ # Gets the collection path for the REST resources. If the +query_options+ parameter is omitted, Rails
# will split from the +prefix_options+.
#
# ==== Options
@@ -725,8 +751,8 @@ module ActiveResource
# ryan = Person.new(:first => 'ryan')
# ryan.save
#
- # Returns the newly created resource. If a failure has occurred an
- # exception will be raised (see <tt>save</tt>). If the resource is invalid and
+ # Returns the newly created resource. If a failure has occurred an
+ # exception will be raised (see <tt>save</tt>). If the resource is invalid and
# has not been saved then <tt>valid?</tt> will return <tt>false</tt>,
# while <tt>new?</tt> will still return <tt>true</tt>.
#
@@ -747,11 +773,11 @@ module ActiveResource
self.new(attributes).tap { |resource| resource.save }
end
- # Core method for finding resources. Used similarly to Active Record's +find+ method.
+ # Core method for finding resources. Used similarly to Active Record's +find+ method.
#
# ==== Arguments
- # The first argument is considered to be the scope of the query. That is, how many
- # resources are returned from the request. It can be one of the following.
+ # The first argument is considered to be the scope of the query. That is, how many
+ # resources are returned from the request. It can be one of the following.
#
# * <tt>:one</tt> - Returns a single resource.
# * <tt>:first</tt> - Returns the first resource found.
@@ -834,7 +860,7 @@ module ActiveResource
find(:last, *args)
end
- # This is an alias for find(:all). You can pass in all the same
+ # This is an alias for find(:all). You can pass in all the same
# arguments to this method as you can to <tt>find(:all)</tt>
def all(*args)
find(:all, *args)
@@ -939,12 +965,12 @@ module ActiveResource
# Accepts a URI and creates the site URI from that.
def create_site_uri_from(site)
- site.is_a?(URI) ? site.dup : URI.parser.parse(site)
+ site.is_a?(URI) ? site.dup : URI.parse(site)
end
# Accepts a URI and creates the proxy URI from that.
def create_proxy_uri_from(proxy)
- proxy.is_a?(URI) ? proxy.dup : URI.parser.parse(proxy)
+ proxy.is_a?(URI) ? proxy.dup : URI.parse(proxy)
end
# contains a set of the current prefix parameters.
@@ -1015,7 +1041,7 @@ module ActiveResource
# not_ryan.new? # => true
#
# Any active resource member attributes will NOT be cloned, though all other
- # attributes are. This is to prevent the conflict between any +prefix_options+
+ # attributes are. This is to prevent the conflict between any +prefix_options+
# that refer to the original parent resource and the newly cloned parent
# resource that does not exist.
#
@@ -1031,7 +1057,7 @@ module ActiveResource
# Clone all attributes except the pk and any nested ARes
cloned = Hash[attributes.reject {|k,v| k == self.class.primary_key || v.is_a?(ActiveResource::Base)}.map { |k, v| [k, v.clone] }]
# Form the new resource - bypass initialize of resource with 'new' as that will call 'load' which
- # attempts to convert hashes into member objects and arrays into collections of objects. We want
+ # attempts to convert hashes into member objects and arrays into collections of objects. We want
# the raw objects to be cloned so we bypass load by directly setting the attributes hash.
resource = self.class.new({})
resource.prefix_options = self.prefix_options
@@ -1083,7 +1109,7 @@ module ActiveResource
attributes[self.class.primary_key] = id
end
- # Test for equality. Resource are equal if and only if +other+ is the same object or
+ # Test for equality. Resource are equal if and only if +other+ is the same object or
# is an instance of the same class, is not <tt>new?</tt>, and has the same +id+.
#
# ==== Examples
@@ -1139,7 +1165,7 @@ module ActiveResource
end
end
- # Saves (+POST+) or \updates (+PUT+) a resource. Delegates to +create+ if the object is \new,
+ # Saves (+POST+) or \updates (+PUT+) a resource. Delegates to +create+ if the object is \new,
# +update+ if it exists. If the response to the \save includes a body, it will be assumed that this body
# is Json for the final object as it looked after the \save (which would include attributes like +created_at+
# that weren't part of the original submit).
@@ -1190,7 +1216,7 @@ module ActiveResource
end
# Evaluates to <tt>true</tt> if this resource is not <tt>new?</tt> and is
- # found on the remote service. Using this method, you can check for
+ # found on the remote service. Using this method, you can check for
# resources that may have been deleted between the object's instantiation
# and actions on it.
#
@@ -1232,7 +1258,7 @@ module ActiveResource
end
# A method to manually load attributes from a \hash. Recursively loads collections of
- # resources. This method is called in +initialize+ and +create+ when a \hash of attributes
+ # resources. This method is called in +initialize+ and +create+ when a \hash of attributes
# is provided.
#
# ==== Examples
@@ -1289,12 +1315,12 @@ module ActiveResource
#
# Note: Unlike ActiveRecord::Base.update_attribute, this method <b>is</b>
# subject to normal validation routines as an update sends the whole body
- # of the resource in the request. (See Validations).
+ # of the resource in the request. (See Validations).
#
# As such, this method is equivalent to calling update_attributes with a single attribute/value pair.
#
# If the saving fails because of a connection or remote service error, an
- # exception will be raised. If saving fails because the resource is
+ # exception will be raised. If saving fails because the resource is
# invalid then <tt>false</tt> will be returned.
def update_attribute(name, value)
self.send("#{name}=".to_sym, value)
@@ -1305,7 +1331,7 @@ module ActiveResource
# and requests that the record be saved.
#
# If the saving fails because of a connection or remote service error, an
- # exception will be raised. If saving fails because the resource is
+ # exception will be raised. If saving fails because the resource is
# invalid then <tt>false</tt> will be returned.
#
# Note: Though this request can be made with a partial set of the
@@ -1413,7 +1439,7 @@ module ActiveResource
namespaces = module_names[0, module_names.size-1].map do |module_name|
receiver = receiver.const_get(module_name)
end
- const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false]
+ const_args = [resource_name, false]
if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(*const_args) }
namespace.const_get(*const_args)
else
@@ -1425,7 +1451,7 @@ module ActiveResource
def find_or_create_resource_for(name)
resource_name = name.to_s.camelize
- const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false]
+ const_args = [resource_name, false]
if self.class.const_defined?(*const_args)
self.class.const_get(*const_args)
else
diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb
index 94839c8c25..46060b6f74 100644
--- a/activeresource/lib/active_resource/connection.rb
+++ b/activeresource/lib/active_resource/connection.rb
@@ -39,14 +39,14 @@ module ActiveResource
# Set URI for remote service.
def site=(site)
- @site = site.is_a?(URI) ? site : URI.parser.parse(site)
+ @site = site.is_a?(URI) ? site : URI.parse(site)
@user = URI.parser.unescape(@site.user) if @site.user
@password = URI.parser.unescape(@site.password) if @site.password
end
# Set the proxy for remote service.
def proxy=(proxy)
- @proxy = proxy.is_a?(URI) ? proxy : URI.parser.parse(proxy)
+ @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
end
# Sets the user for remote service.
@@ -166,38 +166,28 @@ module ActiveResource
end
def configure_http(http)
- http = apply_ssl_options(http)
-
- # Net::HTTP timeouts default to 60 seconds.
- if @timeout
- http.open_timeout = @timeout
- http.read_timeout = @timeout
+ apply_ssl_options(http).tap do |https|
+ # Net::HTTP timeouts default to 60 seconds.
+ if defined? @timeout
+ https.open_timeout = @timeout
+ https.read_timeout = @timeout
+ end
end
-
- http
end
def apply_ssl_options(http)
- return http unless @site.is_a?(URI::HTTPS)
-
- http.use_ssl = true
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- return http unless defined?(@ssl_options)
-
- http.ca_path = @ssl_options[:ca_path] if @ssl_options[:ca_path]
- http.ca_file = @ssl_options[:ca_file] if @ssl_options[:ca_file]
+ http.tap do |https|
+ # Skip config if site is already a https:// URI.
+ if defined? @ssl_options
+ http.use_ssl = true
- http.cert = @ssl_options[:cert] if @ssl_options[:cert]
- http.key = @ssl_options[:key] if @ssl_options[:key]
+ # Default to no cert verification (WTF? FIXME)
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- http.cert_store = @ssl_options[:cert_store] if @ssl_options[:cert_store]
- http.ssl_timeout = @ssl_options[:ssl_timeout] if @ssl_options[:ssl_timeout]
-
- http.verify_mode = @ssl_options[:verify_mode] if @ssl_options[:verify_mode]
- http.verify_callback = @ssl_options[:verify_callback] if @ssl_options[:verify_callback]
- http.verify_depth = @ssl_options[:verify_depth] if @ssl_options[:verify_depth]
-
- http
+ # All the SSL options have corresponding http settings.
+ @ssl_options.each { |key, value| http.send "#{key}=", value }
+ end
+ end
end
def default_header
diff --git a/activeresource/lib/active_resource/custom_methods.rb b/activeresource/lib/active_resource/custom_methods.rb
index c1931b2758..a0eb28ed13 100644
--- a/activeresource/lib/active_resource/custom_methods.rb
+++ b/activeresource/lib/active_resource/custom_methods.rb
@@ -2,7 +2,7 @@ require 'active_support/core_ext/object/blank'
module ActiveResource
# A module to support custom REST methods and sub-resources, allowing you to break out
- # of the "default" REST methods with your own custom resource requests. For example,
+ # of the "default" REST methods with your own custom resource requests. For example,
# say you use Rails to expose a REST service and configure your routes with:
#
# map.resources :people, :new => { :register => :post },
@@ -20,10 +20,10 @@ module ActiveResource
# standard methods.
#
# class Person < ActiveResource::Base
- # self.site = "http://37s.sunrise.i:3000"
+ # self.site = "https://37s.sunrise.com"
# end
#
- # Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.json
+ # Person.new(:name => 'Ryan').post(:register) # POST /people/new/register.json
# # => { :id => 1, :name => 'Ryan' }
#
# Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.json
@@ -85,37 +85,35 @@ module ActiveResource
end
end
- module InstanceMethods
- def get(method_name, options = {})
- self.class.format.decode(connection.get(custom_method_element_url(method_name, options), self.class.headers).body)
- end
+ def get(method_name, options = {})
+ self.class.format.decode(connection.get(custom_method_element_url(method_name, options), self.class.headers).body)
+ end
- def post(method_name, options = {}, body = nil)
- request_body = body.blank? ? encode : body
- if new?
- connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
- else
- connection.post(custom_method_element_url(method_name, options), request_body, self.class.headers)
- end
+ def post(method_name, options = {}, body = nil)
+ request_body = body.blank? ? encode : body
+ if new?
+ connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
+ else
+ connection.post(custom_method_element_url(method_name, options), request_body, self.class.headers)
end
+ end
- def put(method_name, options = {}, body = '')
- connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
- end
+ def put(method_name, options = {}, body = '')
+ connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
+ end
- def delete(method_name, options = {})
- connection.delete(custom_method_element_url(method_name, options), self.class.headers)
- end
+ def delete(method_name, options = {})
+ connection.delete(custom_method_element_url(method_name, options), self.class.headers)
+ end
- private
- def custom_method_element_url(method_name, options = {})
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
- end
+ private
+ def custom_method_element_url(method_name, options = {})
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
+ end
- def custom_method_new_element_url(method_name, options = {})
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
- end
- end
+ def custom_method_new_element_url(method_name, options = {})
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
+ end
end
end
diff --git a/activeresource/lib/active_resource/exceptions.rb b/activeresource/lib/active_resource/exceptions.rb
index 6b953b28ad..51bede3bd0 100644
--- a/activeresource/lib/active_resource/exceptions.rb
+++ b/activeresource/lib/active_resource/exceptions.rb
@@ -33,35 +33,45 @@ module ActiveResource
# 3xx Redirection
class Redirection < ConnectionError # :nodoc:
- def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
+ def to_s
+ response['Location'] ? "#{super} => #{response['Location']}" : super
+ end
end
- # Raised when ...
- class MissingPrefixParam < ArgumentError; end # :nodoc:
+ class MissingPrefixParam < ArgumentError # :nodoc:
+ end
# 4xx Client Error
- class ClientError < ConnectionError; end # :nodoc:
+ class ClientError < ConnectionError # :nodoc:
+ end
# 400 Bad Request
- class BadRequest < ClientError; end # :nodoc
+ class BadRequest < ClientError # :nodoc:
+ end
# 401 Unauthorized
- class UnauthorizedAccess < ClientError; end # :nodoc
+ class UnauthorizedAccess < ClientError # :nodoc:
+ end
# 403 Forbidden
- class ForbiddenAccess < ClientError; end # :nodoc
+ class ForbiddenAccess < ClientError # :nodoc:
+ end
# 404 Not Found
- class ResourceNotFound < ClientError; end # :nodoc:
+ class ResourceNotFound < ClientError # :nodoc:
+ end
# 409 Conflict
- class ResourceConflict < ClientError; end # :nodoc:
+ class ResourceConflict < ClientError # :nodoc:
+ end
# 410 Gone
- class ResourceGone < ClientError; end # :nodoc:
+ class ResourceGone < ClientError # :nodoc:
+ end
# 5xx Server Error
- class ServerError < ConnectionError; end # :nodoc:
+ class ServerError < ConnectionError # :nodoc:
+ end
# 405 Method Not Allowed
class MethodNotAllowed < ClientError # :nodoc:
diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb
index 36f52d61d3..666b961f87 100644
--- a/activeresource/lib/active_resource/http_mock.rb
+++ b/activeresource/lib/active_resource/http_mock.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/object/inclusion'
module ActiveResource
class InvalidRequestError < StandardError; end #:nodoc:
- # One thing that has always been a pain with remote web services is testing. The HttpMock
+ # One thing that has always been a pain with remote web services is testing. The HttpMock
# class makes it easy to test your Active Resource models by creating a set of mock responses to specific
# requests.
#
@@ -15,17 +15,17 @@ module ActiveResource
#
# mock.http_method(path, request_headers = {}, body = nil, status = 200, response_headers = {})
#
- # * <tt>http_method</tt> - The HTTP method to listen for. This can be +get+, +post+, +put+, +delete+ or
+ # * <tt>http_method</tt> - The HTTP method to listen for. This can be +get+, +post+, +put+, +delete+ or
# +head+.
# * <tt>path</tt> - A string, starting with a "/", defining the URI that is expected to be
# called.
- # * <tt>request_headers</tt> - Headers that are expected along with the request. This argument uses a
- # hash format, such as <tt>{ "Content-Type" => "application/json" }</tt>. This mock will only trigger
+ # * <tt>request_headers</tt> - Headers that are expected along with the request. This argument uses a
+ # hash format, such as <tt>{ "Content-Type" => "application/json" }</tt>. This mock will only trigger
# if your tests sends a request with identical headers.
- # * <tt>body</tt> - The data to be returned. This should be a string of Active Resource parseable content,
+ # * <tt>body</tt> - The data to be returned. This should be a string of Active Resource parseable content,
# such as Json.
# * <tt>status</tt> - The HTTP response code, as an integer, to return with the response.
- # * <tt>response_headers</tt> - Headers to be returned with the response. Uses the same hash format as
+ # * <tt>response_headers</tt> - Headers to be returned with the response. Uses the same hash format as
# <tt>request_headers</tt> listed above.
#
# In order for a mock to deliver its content, the incoming request must match by the <tt>http_method</tt>,
@@ -291,12 +291,9 @@ module ActiveResource
if resp_cls && !resp_cls.body_permitted?
@body = nil
end
-
- if @body.nil?
- self['Content-Length'] = "0"
- else
- self['Content-Length'] = body.size.to_s
- end
+
+ self['Content-Length'] = @body.nil? ? "0" : body.size.to_s
+
end
# Returns true if code is 2xx,
diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb
index ca265d053e..028acb8bce 100644
--- a/activeresource/lib/active_resource/validations.rb
+++ b/activeresource/lib/active_resource/validations.rb
@@ -25,10 +25,48 @@ module ActiveResource
end
end
+ # Grabs errors from a hash of attribute => array of errors elements
+ # The second parameter directs the errors cache to be cleared (default)
+ # or not (by passing true)
+ #
+ # Unrecognized attribute names will be humanized and added to the record's
+ # base errors.
+ def from_hash(messages, save_cache = false)
+ clear unless save_cache
+
+ messages.each do |(key,errors)|
+ errors.each do |error|
+ if @base.attributes.keys.include?(key)
+ add key, error
+ elsif key == 'base'
+ self[:base] << error
+ else
+ # reporting an error on an attribute not in attributes
+ # format and add them to base
+ self[:base] << "#{key.humanize} #{error}"
+ end
+ end
+ end
+ end
+
# Grabs errors from a json response.
def from_json(json, save_cache = false)
- array = Array.wrap(ActiveSupport::JSON.decode(json)['errors']) rescue []
- from_array array, save_cache
+ decoded = ActiveSupport::JSON.decode(json) || {} rescue {}
+ if decoded.kind_of?(Hash) && (decoded.has_key?('errors') || decoded.empty?)
+ errors = decoded['errors'] || {}
+ if errors.kind_of?(Array)
+ # 3.2.1-style with array of strings
+ ActiveSupport::Deprecation.warn('Returning errors as an array of strings is deprecated.')
+ from_array errors, save_cache
+ else
+ # 3.2.2+ style
+ from_hash errors, save_cache
+ end
+ else
+ # <3.2-style respond_with - lacks 'errors' key
+ ActiveSupport::Deprecation.warn('Returning errors as a hash without a root "errors" key is deprecated.')
+ from_hash decoded, save_cache
+ end
end
# Grabs errors from an XML response.
@@ -41,7 +79,7 @@ module ActiveResource
# Module to support validation and errors with Active Resource objects. The module overrides
# Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned
# in the web service response. The module also adds an +errors+ collection that mimics the interface
- # of the errors provided by ActiveRecord::Errors.
+ # of the errors provided by ActiveModel::Errors.
#
# ==== Example
#
diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb
index d53374b261..d02784bd5d 100644
--- a/activeresource/lib/active_resource/version.rb
+++ b/activeresource/lib/active_resource/version.rb
@@ -1,7 +1,7 @@
module ActiveResource
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb
index 9c1e9a526d..c68625df4f 100644
--- a/activeresource/test/abstract_unit.rb
+++ b/activeresource/test/abstract_unit.rb
@@ -3,15 +3,14 @@ require File.expand_path('../../../load_paths', __FILE__)
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
-require 'test/unit'
+require 'minitest/autorun'
require 'active_resource'
require 'active_support'
require 'active_support/test_case'
-
require 'setter_trap'
+require 'active_support/logger'
-require 'logger'
-ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log")
+ActiveResource::Base.logger = ActiveSupport::Logger.new("#{File.dirname(__FILE__)}/debug.log")
def setup_response
matz_hash = { 'person' => { :id => 1, :name => 'Matz' } }
diff --git a/activeresource/test/cases/authorization_test.rb b/activeresource/test/cases/authorization_test.rb
index 17cd9b30fc..fbfe086599 100644
--- a/activeresource/test/cases/authorization_test.rb
+++ b/activeresource/test/cases/authorization_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class AuthorizationTest < Test::Unit::TestCase
+class AuthorizationTest < ActiveSupport::TestCase
Response = Struct.new(:code)
def setup
@@ -9,8 +9,18 @@ class AuthorizationTest < Test::Unit::TestCase
@david = { :person => { :id => 2, :name => 'David' } }.to_json
@authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost")
@basic_authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
+ end
- @nonce = "MTI0OTUxMzc4NzpjYWI3NDM3NDNmY2JmODU4ZjQ2ZjcwNGZkMTJiMjE0NA=="
+ private
+ def decode(response)
+ @authenticated_conn.format.decode(response.body)
+ end
+end
+
+class BasicAuthorizationTest < AuthorizationTest
+ def setup
+ super
+ @authenticated_conn.auth_type = :basic
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/2.json", @basic_authorization_request_header, @david
@@ -19,41 +29,55 @@ class AuthorizationTest < Test::Unit::TestCase
mock.delete "/people/2.json", @basic_authorization_request_header, nil, 200
mock.post "/people/2/addresses.json", @basic_authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
mock.head "/people/2.json", @basic_authorization_request_header, nil, 200
+ end
+ end
- mock.get "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
- mock.get "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "c064d5ba8891a25290c76c8c7d31fb7b") }, @david, 200
- mock.get "/people/1.json", { 'Authorization' => request_digest_auth_header("/people/1.json", "f9c0b594257bb8422af4abd429c5bb70") }, @matz, 200
+ def test_get
+ david = decode(@authenticated_conn.get("/people/2.json"))
+ assert_equal "David", david["name"]
+ end
- mock.put "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "50a685d814f94665b9d160fbbaa3958a") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
- mock.put "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "5a75cde841122d8e0f20f8fd1f98a743") }, nil, 204
+ def test_post
+ response = @authenticated_conn.post("/people/2/addresses.json")
+ assert_equal "/people/1/addresses/5", response["Location"]
+ end
- mock.delete "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "846f799107eab5ca4285b909ee299a33") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
- mock.delete "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "9f5b155224edbbb69fd99d8ce094681e") }, nil, 200
+ def test_put
+ response = @authenticated_conn.put("/people/2.json")
+ assert_equal 204, response.code
+ end
- mock.post "/people/2/addresses.json", { 'Authorization' => blank_digest_auth_header("/people/2/addresses.json", "6984d405ff3d9ed07bbf747dcf16afb0") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
- mock.post "/people/2/addresses.json", { 'Authorization' => request_digest_auth_header("/people/2/addresses.json", "4bda6a28dbf930b5af9244073623bd04") }, nil, 201, 'Location' => '/people/1/addresses/5'
+ def test_delete
+ response = @authenticated_conn.delete("/people/2.json")
+ assert_equal 200, response.code
+ end
- mock.head "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "15e5ed84ba5c4cfcd5c98a36c2e4f421") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
- mock.head "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "d4c6d2bcc8717abb2e2ccb8c49ee6a91") }, nil, 200
- end
+ def test_head
+ response = @authenticated_conn.head("/people/2.json")
+ assert_equal 200, response.code
+ end
- # Make client nonce deterministic
- class << @authenticated_conn
- private
+ def test_retry_on_401_doesnt_happen_with_basic_auth
+ assert_raise(ActiveResource::UnauthorizedAccess) { @authenticated_conn.get("/people/1.json") }
+ assert_equal "", @authenticated_conn.send(:response_auth_header)
+ end
- def client_nonce
- 'i-am-a-client-nonce'
- end
- end
+ def test_raises_invalid_request_on_unauthorized_requests
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.json") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.json") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.json") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.json") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.json") }
end
+
def test_authorization_header
authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json'))
assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["david", "test123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_with_username_but_no_password
@@ -62,7 +86,7 @@ class AuthorizationTest < Test::Unit::TestCase
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["david"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_with_password_but_no_username
@@ -71,7 +95,7 @@ class AuthorizationTest < Test::Unit::TestCase
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["", "test123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_with_decoded_credentials_from_url
@@ -80,7 +104,7 @@ class AuthorizationTest < Test::Unit::TestCase
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["my@email.com", "123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["my@email.com", "123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_explicitly_setting_username_and_password
@@ -92,7 +116,7 @@ class AuthorizationTest < Test::Unit::TestCase
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["david", "test123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_explicitly_setting_username_but_no_password
@@ -102,7 +126,7 @@ class AuthorizationTest < Test::Unit::TestCase
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["david"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_explicitly_setting_password_but_no_username
@@ -112,89 +136,89 @@ class AuthorizationTest < Test::Unit::TestCase
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["", "test123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_if_credentials_supplied_and_auth_type_is_basic
- @authenticated_conn.auth_type = :basic
authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json'))
assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
- assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ assert_equal ["david", "test123"], ::Base64.decode64(authorization[1]).split(":")[0..1]
end
- def test_authorization_header_if_credentials_supplied_and_auth_type_is_digest
- @authenticated_conn.auth_type = :digest
- authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json'))
- assert_equal blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671"), authorization_header['Authorization']
+ def test_client_nonce_is_not_nil
+ assert_not_nil ActiveResource::Connection.new("http://david:test123@localhost").send(:client_nonce)
end
+end
- def test_authorization_header_with_query_string_if_auth_type_is_digest
+class DigestAuthorizationTest < AuthorizationTest
+ def setup
+ super
@authenticated_conn.auth_type = :digest
- authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json?only=name'))
- assert_equal blank_digest_auth_header("/people/2.json?only=name", "f8457b0b5d21b6b80737a386217afb24"), authorization_header['Authorization']
- end
- def test_get
- david = decode(@authenticated_conn.get("/people/2.json"))
- assert_equal "David", david["name"]
- end
+ # Make client nonce deterministic
+ def @authenticated_conn.client_nonce; 'i-am-a-client-nonce' end
- def test_post
- response = @authenticated_conn.post("/people/2/addresses.json")
- assert_equal "/people/1/addresses/5", response["Location"]
- end
+ @nonce = "MTI0OTUxMzc4NzpjYWI3NDM3NDNmY2JmODU4ZjQ2ZjcwNGZkMTJiMjE0NA=="
- def test_put
- response = @authenticated_conn.put("/people/2.json")
- assert_equal 204, response.code
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.get "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "c064d5ba8891a25290c76c8c7d31fb7b") }, @david, 200
+ mock.get "/people/1.json", { 'Authorization' => request_digest_auth_header("/people/1.json", "f9c0b594257bb8422af4abd429c5bb70") }, @matz, 200
+
+ mock.put "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "50a685d814f94665b9d160fbbaa3958a") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.put "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "5a75cde841122d8e0f20f8fd1f98a743") }, nil, 204
+
+ mock.delete "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "846f799107eab5ca4285b909ee299a33") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.delete "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "9f5b155224edbbb69fd99d8ce094681e") }, nil, 200
+
+ mock.post "/people/2/addresses.json", { 'Authorization' => blank_digest_auth_header("/people/2/addresses.json", "6984d405ff3d9ed07bbf747dcf16afb0") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.post "/people/2/addresses.json", { 'Authorization' => request_digest_auth_header("/people/2/addresses.json", "4bda6a28dbf930b5af9244073623bd04") }, nil, 201, 'Location' => '/people/1/addresses/5'
+
+ mock.head "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "15e5ed84ba5c4cfcd5c98a36c2e4f421") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.head "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "d4c6d2bcc8717abb2e2ccb8c49ee6a91") }, nil, 200
+ end
end
- def test_delete
- response = @authenticated_conn.delete("/people/2.json")
- assert_equal 200, response.code
+ def test_authorization_header_if_credentials_supplied_and_auth_type_is_digest
+ authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json'))
+ assert_equal blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671"), authorization_header['Authorization']
end
- def test_head
- response = @authenticated_conn.head("/people/2.json")
- assert_equal 200, response.code
+ def test_authorization_header_with_query_string_if_auth_type_is_digest
+ authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json?only=name'))
+ assert_equal blank_digest_auth_header("/people/2.json?only=name", "f8457b0b5d21b6b80737a386217afb24"), authorization_header['Authorization']
end
def test_get_with_digest_auth_handles_initial_401_response_and_retries
- @authenticated_conn.auth_type = :digest
response = @authenticated_conn.get("/people/2.json")
assert_equal "David", decode(response)["name"]
end
def test_post_with_digest_auth_handles_initial_401_response_and_retries
- @authenticated_conn.auth_type = :digest
response = @authenticated_conn.post("/people/2/addresses.json")
assert_equal "/people/1/addresses/5", response["Location"]
assert_equal 201, response.code
end
def test_put_with_digest_auth_handles_initial_401_response_and_retries
- @authenticated_conn.auth_type = :digest
- response = @authenticated_conn.put("/people/2.json")
- assert_equal 204, response.code
+ response = @authenticated_conn.put("/people/2.json")
+ assert_equal 204, response.code
end
def test_delete_with_digest_auth_handles_initial_401_response_and_retries
- @authenticated_conn.auth_type = :digest
response = @authenticated_conn.delete("/people/2.json")
assert_equal 200, response.code
end
def test_head_with_digest_auth_handles_initial_401_response_and_retries
- @authenticated_conn.auth_type = :digest
response = @authenticated_conn.head("/people/2.json")
assert_equal 200, response.code
end
def test_get_with_digest_auth_caches_nonce
- @authenticated_conn.auth_type = :digest
response = @authenticated_conn.get("/people/2.json")
assert_equal "David", decode(response)["name"]
@@ -203,19 +227,6 @@ class AuthorizationTest < Test::Unit::TestCase
assert_equal "Matz", decode(response)["name"]
end
- def test_retry_on_401_only_happens_with_digest_auth
- assert_raise(ActiveResource::UnauthorizedAccess) { @authenticated_conn.get("/people/1.json") }
- assert_equal "", @authenticated_conn.send(:response_auth_header)
- end
-
- def test_raises_invalid_request_on_unauthorized_requests
- assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.json") }
- assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.json") }
- assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.json") }
- assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.json") }
- assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.json") }
- end
-
def test_raises_invalid_request_on_unauthorized_requests_with_digest_auth
@conn.auth_type = :digest
assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.json") }
@@ -225,17 +236,7 @@ class AuthorizationTest < Test::Unit::TestCase
assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.json") }
end
- def test_client_nonce_is_not_nil
- assert_not_nil ActiveResource::Connection.new("http://david:test123@localhost").send(:client_nonce)
- end
-
- protected
- def assert_response_raises(klass, code)
- assert_raise(klass, "Expected response code #{code} to raise #{klass}") do
- @conn.__send__(:handle_response, Response.new(code))
- end
- end
-
+ private
def blank_digest_auth_header(uri, response)
%Q(Digest username="david", realm="", qop="", uri="#{uri}", nonce="", nc="0", cnonce="i-am-a-client-nonce", opaque="", response="#{response}")
end
@@ -247,8 +248,4 @@ class AuthorizationTest < Test::Unit::TestCase
def response_digest_auth_header
%Q(Digest realm="RailsTestApp", qop="auth", algorithm=MD5, nonce="#{@nonce}", opaque="ef6dfb078ba22298d366f99567814ffb")
end
-
- def decode(response)
- @authenticated_conn.format.decode(response.body)
- end
end
diff --git a/activeresource/test/cases/base/custom_methods_test.rb b/activeresource/test/cases/base/custom_methods_test.rb
index 3eaa9b1c5b..f7aa7a4a09 100644
--- a/activeresource/test/cases/base/custom_methods_test.rb
+++ b/activeresource/test/cases/base/custom_methods_test.rb
@@ -3,7 +3,7 @@ require 'fixtures/person'
require 'fixtures/street_address'
require 'active_support/core_ext/hash/conversions'
-class CustomMethodsTest < Test::Unit::TestCase
+class CustomMethodsTest < ActiveSupport::TestCase
def setup
@matz = { :person => { :id => 1, :name => 'Matz' } }.to_json
@matz_deep = { :person => { :id => 1, :name => 'Matz', :other => 'other' } }.to_json
diff --git a/activeresource/test/cases/base/equality_test.rb b/activeresource/test/cases/base/equality_test.rb
index 84f1a7b998..fffd8b75c3 100644
--- a/activeresource/test/cases/base/equality_test.rb
+++ b/activeresource/test/cases/base/equality_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require "fixtures/person"
require "fixtures/street_address"
-class BaseEqualityTest < Test::Unit::TestCase
+class BaseEqualityTest < ActiveSupport::TestCase
def setup
@new = Person.new
@one = Person.new(:id => 1)
diff --git a/activeresource/test/cases/base/load_test.rb b/activeresource/test/cases/base/load_test.rb
index 784e7dd036..f07e1ea16b 100644
--- a/activeresource/test/cases/base/load_test.rb
+++ b/activeresource/test/cases/base/load_test.rb
@@ -32,7 +32,7 @@ module Highrise
end
-class BaseLoadTest < Test::Unit::TestCase
+class BaseLoadTest < ActiveSupport::TestCase
def setup
@matz = { :id => 1, :name => 'Matz' }
diff --git a/activeresource/test/cases/base_errors_test.rb b/activeresource/test/cases/base_errors_test.rb
index 5063916d10..88ac2de96e 100644
--- a/activeresource/test/cases/base_errors_test.rb
+++ b/activeresource/test/cases/base_errors_test.rb
@@ -1,11 +1,11 @@
require 'abstract_unit'
require "fixtures/person"
-class BaseErrorsTest < Test::Unit::TestCase
+class BaseErrorsTest < ActiveSupport::TestCase
def setup
ActiveResource::HttpMock.respond_to do |mock|
mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error><error>Name can't be blank</error><error>Name must start with a letter</error><error>Person quota full for today.</error></errors>), 422, {'Content-Type' => 'application/xml; charset=utf-8'}
- mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json; charset=utf-8'}
+ mock.post "/people.json", {}, %q({"errors":{"age":["can't be blank"],"name":["can't be blank", "must start with a letter"],"person":["quota full for today."]}}), 422, {'Content-Type' => 'application/json; charset=utf-8'}
end
end
@@ -83,7 +83,7 @@ class BaseErrorsTest < Test::Unit::TestCase
def test_should_mark_as_invalid_when_content_type_is_unavailable_in_response_header
ActiveResource::HttpMock.respond_to do |mock|
mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error><error>Name can't be blank</error><error>Name must start with a letter</error><error>Person quota full for today.</error></errors>), 422, {}
- mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {}
+ mock.post "/people.json", {}, %q({"errors":{"age":["can't be blank"],"name":["can't be blank", "must start with a letter"],"person":["quota full for today."]}}), 422, {}
end
[ :json, :xml ].each do |format|
@@ -93,6 +93,36 @@ class BaseErrorsTest < Test::Unit::TestCase
end
end
+ def test_should_parse_json_string_errors_with_an_errors_key
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/people.json", {}, %q({"errors":["Age can't be blank", "Name can't be blank", "Name must start with a letter", "Person quota full for today."]}), 422, {'Content-Type' => 'application/json; charset=utf-8'}
+ end
+
+ assert_deprecated(/as an array/) do
+ invalid_user_using_format(:json) do
+ assert @person.errors[:name].any?
+ assert_equal ["can't be blank"], @person.errors[:age]
+ assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name]
+ assert_equal ["Person quota full for today."], @person.errors[:base]
+ end
+ end
+ end
+
+ def test_should_parse_3_1_style_json_errors
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/people.json", {}, %q({"age":["can't be blank"],"name":["can't be blank", "must start with a letter"],"person":["quota full for today."]}), 422, {'Content-Type' => 'application/json; charset=utf-8'}
+ end
+
+ assert_deprecated(/without a root/) do
+ invalid_user_using_format(:json) do
+ assert @person.errors[:name].any?
+ assert_equal ["can't be blank"], @person.errors[:age]
+ assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name]
+ assert_equal ["Person quota full for today."], @person.errors[:base]
+ end
+ end
+ end
+
private
def invalid_user_using_format(mime_type_reference)
previous_format = Person.format
diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb
index 7b42f64a35..f5a58793d1 100644
--- a/activeresource/test/cases/base_test.rb
+++ b/activeresource/test/cases/base_test.rb
@@ -8,11 +8,10 @@ require "fixtures/proxy"
require "fixtures/address"
require "fixtures/subscription_plan"
require 'active_support/json'
-require 'active_support/ordered_hash'
require 'active_support/core_ext/hash/conversions'
require 'mocha'
-class BaseTest < Test::Unit::TestCase
+class BaseTest < ActiveSupport::TestCase
def setup
setup_response # find me in abstract_unit
@original_person_site = Person.site
@@ -464,8 +463,7 @@ class BaseTest < Test::Unit::TestCase
assert Person.collection_path(:gender => 'male', :student => true).include?('student=true')
assert_equal '/people.json?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
-
- assert_equal '/people.json?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => ActiveSupport::OrderedHash[:a, [2,1], 'b', 'fred'])
+ assert_equal '/people.json?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'})
end
def test_custom_element_path
diff --git a/activeresource/test/cases/connection_test.rb b/activeresource/test/cases/connection_test.rb
index 535107aeef..0a07ead15e 100644
--- a/activeresource/test/cases/connection_test.rb
+++ b/activeresource/test/cases/connection_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class ConnectionTest < Test::Unit::TestCase
+class ConnectionTest < ActiveSupport::TestCase
ResponseCodeStub = Struct.new(:code)
RedirectResponseStub = Struct.new(:code, :Location)
@@ -224,7 +224,6 @@ class ConnectionTest < Test::Unit::TestCase
http = Net::HTTP.new('')
@conn.site="https://secure"
@conn.ssl_options={:verify_mode => OpenSSL::SSL::VERIFY_PEER}
- @conn.timeout = 10 # prevent warning about uninitialized.
@conn.send(:configure_http, http)
assert http.use_ssl?
diff --git a/activeresource/test/cases/finder_test.rb b/activeresource/test/cases/finder_test.rb
index 5fbbfeef6e..3e8550d356 100644
--- a/activeresource/test/cases/finder_test.rb
+++ b/activeresource/test/cases/finder_test.rb
@@ -6,7 +6,7 @@ require "fixtures/beast"
require "fixtures/proxy"
require 'active_support/core_ext/hash/conversions'
-class FinderTest < Test::Unit::TestCase
+class FinderTest < ActiveSupport::TestCase
def setup
setup_response # find me in abstract_unit
end
diff --git a/activeresource/test/cases/format_test.rb b/activeresource/test/cases/format_test.rb
index 174142ec52..30342ecc74 100644
--- a/activeresource/test/cases/format_test.rb
+++ b/activeresource/test/cases/format_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require "fixtures/person"
require "fixtures/street_address"
-class FormatTest < Test::Unit::TestCase
+class FormatTest < ActiveSupport::TestCase
def setup
@matz = { :id => 1, :name => 'Matz' }
@david = { :id => 2, :name => 'David' }
@@ -19,7 +19,7 @@ class FormatTest < Test::Unit::TestCase
end
def test_formats_on_single_element
- for format in [ :json, :xml ]
+ [ :json, :xml ].each do |format|
using_format(Person, format) do
ActiveResource::HttpMock.respond_to.get "/people/1.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
assert_equal @david[:name], Person.find(1).name
@@ -28,7 +28,7 @@ class FormatTest < Test::Unit::TestCase
end
def test_formats_on_collection
- for format in [ :json, :xml ]
+ [ :json, :xml ].each do |format|
using_format(Person, format) do
ActiveResource::HttpMock.respond_to.get "/people.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@programmers)
remote_programmers = Person.find(:all)
@@ -39,7 +39,7 @@ class FormatTest < Test::Unit::TestCase
end
def test_formats_on_custom_collection_method
- for format in [ :json, :xml ]
+ [ :json, :xml ].each do |format|
using_format(Person, format) do
ActiveResource::HttpMock.respond_to.get "/people/retrieve.#{format}?name=David", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode([@david])
remote_programmers = Person.get(:retrieve, :name => 'David')
diff --git a/activeresource/test/cases/observing_test.rb b/activeresource/test/cases/observing_test.rb
index 58d3d389ff..b2371a1bdf 100644
--- a/activeresource/test/cases/observing_test.rb
+++ b/activeresource/test/cases/observing_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'fixtures/person'
require 'active_support/core_ext/hash/conversions'
-class ObservingTest < Test::Unit::TestCase
+class ObservingTest < ActiveSupport::TestCase
cattr_accessor :history
class PersonObserver < ActiveModel::Observer
diff --git a/activeresource/test/fixtures/street_address.rb b/activeresource/test/fixtures/street_address.rb
index 94a86702b0..6a8adb98b5 100644
--- a/activeresource/test/fixtures/street_address.rb
+++ b/activeresource/test/fixtures/street_address.rb
@@ -1,4 +1,4 @@
class StreetAddress < ActiveResource::Base
- self.site = "http://37s.sunrise.i:3000/people/:person_id/"
+ self.site = "http://37s.sunrise.i:3000/people/:person_id"
self.element_name = 'address'
end
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
deleted file mode 100644
index 8609198641..0000000000
--- a/activesupport/CHANGELOG
+++ /dev/null
@@ -1,1571 +0,0 @@
-*Rails 3.2.0 (unreleased)*
-
-* Module#qualified_const_(defined?|get|set) are analogous to the corresponding methods
- in the standard API, but accept qualified constant names. [fxn]
-
-* Added inflection #deconstantize which complements #demodulize. This inflection
- removes the righmost segment in a qualified constant name. [fxn]
-
-* Added ActiveSupport:TaggedLogging that can wrap any standard Logger class to provide tagging capabilities [DHH]
-
- Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
- Logger.tagged("BCX") { Logger.info "Stuff" } # Logs "[BCX] Stuff"
- Logger.tagged("BCX", "Jason") { Logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff"
- Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff"
-
-* Added safe_constantize that constantizes a string but returns nil instead of an exception if the constant (or part of it) does not exist [Ryan Oblak]
-
-* ActiveSupport::OrderedHash is now marked as extractable when using Array#extract_options! [Prem Sichanugrist]
-
-* Added Array#prepend as an alias for Array#unshift and Array#append as an alias for Array#<< [DHH]
-
-* The definition of blank string for Ruby 1.9 has been extended to Unicode whitespace.
-Also, in 1.8 the ideographic space U+3000 is considered to be whitespace. [Akira Matsuda, Damien Mathieu]
-
-* The inflector understands acronyms. [dlee]
-
-* Deprecated ActiveSupport::Memoizable in favor of Ruby memoization pattern [José Valim]
-
-* Added Time#all_day/week/quarter/year as a way of generating ranges (example: Event.where(created_at: Time.now.all_week)) [DHH]
-
-* Added instance_accessor: false as an option to Class#cattr_accessor and friends [DHH]
-
-* Removed ActiveSupport::SecureRandom in favor of SecureRandom from the standard library [Jon Leighton]
-
-* ActiveSupport::OrderedHash now has different behavior for #each and
-#each_pair when given a block accepting its parameters with a splat. [Andrew Radev]
-
-*Rails 3.1.0 (August 30, 2011)*
-
-* ActiveSupport::Dependencies#load and ActiveSupport::Dependencies#require now
-return the value from `super` [Aaron Patterson]
-
-* Fixed ActiveSupport::Gzip to work properly in Ruby 1.8 [Guillermo Iguaran]
-
-* Kernel.require_library_or_gem was deprecated and will be removed in Rails 3.2.0 [Josh Kalderimis]
-
-* ActiveSupport::Duration#duplicable? was fixed for Ruby 1.8 [thedarkone]
-
-* ActiveSupport::BufferedLogger set log encoding to BINARY, but still use text
-mode to output portable newlines. [fxn]
-
-* ActiveSupport::Dependencies now raises NameError if it finds an existing constant in load_missing_constant. This better reflects the nature of the error which is usually caused by calling constantize on a nested constant. [Andrew White]
-
-* Deprecated ActiveSupport::SecureRandom in favour of SecureRandom from the standard library [Jon Leighton]
-
-* New reporting method Kernel#quietly. [fxn]
-
-* Add String#inquiry as a convenience method for turning a string into a StringInquirer object [DHH]
-
-* Add Object#in? to test if an object is included in another object [Prem Sichanugrist, Brian Morearty, John Reitano]
-
-* LocalCache strategy is now a real middleware class, not an anonymous class
-posing for pictures.
-
-* ActiveSupport::Dependencies::ClassCache class has been introduced for
-holding references to reloadable classes.
-
-* ActiveSupport::Dependencies::Reference has been refactored to take direct
-advantage of the new ClassCache.
-
-* Backports Range#cover? as an alias for Range#include? in Ruby 1.8 [Diego Carrion, fxn]
-
-* Added weeks_ago and prev_week to Date/DateTime/Time. [Rob Zolkos, fxn]
-
-* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
-
-* JSON decoding now uses the multi_json gem which also vendors a json engine called OkJson. The yaml backend has been removed in favor of OkJson as a default engine for 1.8.x, while the built in 1.9.x json implementation will be used by default. [Josh Kalderimis]
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-* Hash.from_xml no longer loses attributes on tags containing only whitespace [André Arko]
-
-
-*Rails 3.0.6 (April 5, 2011)
-
-* No changes.
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* No changes.
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* No Changes, just a version bump.
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* Implemented String#strip_heredoc. [fxn]
-
-* Pluggable cache stores: setting config.cache_store = "custom_store" will require 'active_support/cache/custom_store' and look for the CustomStore constant. #5486 [Mike Perham]
-
-* Removed Object#returning, Object#tap should be used instead. [Santiago Pastorino]
-
-* Deprecation behavior is no longer hardcoded to the name of the environment.
- Instead, it is set via config.active_support.deprecation and can be one
- of :log, :stderr or :notify. :notify is a new style that sends the warning
- via ActiveSupport::Notifications, and is the new default for production
- [Yehuda Katz]
-
-* Renamed ActiveSupport::Dependecies.load_(once_)paths to autoload_(once_)paths. [fxn]
-
-* Added ActiveSupport::FileUpdateChecker to execute a block only if a set of files changed, used by Router and I18n locale files. [José Valim]
-
-* Added ActiveSupport::DescendantsTracker to track descendants with support to constants reloading. [José Valim]
-
-* ActiveSupport::OrderedHash#merge and #merge! accept a block. #4838 [Paul Mucur, fxn]
-
-* Date#since, #ago, #beginning_of_day, #end_of_day, and #xmlschema honor now the user time zone if set. [Geoff Buesing]
-
-* Extracted String#truncate from TextHelper#truncate [DHH]
-
-* Ruby 1.9: support UTF-8 case folding. #4595 [Norman Clarke]
-
-* Removes Array#rand and backports Array#sample from Ruby 1.9, thanks to Marc-Andre Lafortune. [fxn]
-
-* Ruby 1.9: Renames last_(month|year) to prev_(month|year) in Date and Time. [fxn]
-
-* Aliases Date#sunday to Date#end_of_week. [fxn]
-
-* Backports Date#>> from 1.9 so that calculations do the right thing around the calendar reform. [fxn]
-
-* Date#to_time handles properly years in the range 0..138. [fxn]
-
-* Deprecate {{}} as interpolation syntax for I18n in favor of %{} [José Valim]
-
-* Array#to_xml is more powerful and able to handle the same types as Hash#to_xml #4490 [Neeraj Singh]
-
-* Harmonize the caching API and refactor the backends. #4452 [Brian Durand]
- All caches:
- * Add default options to initializer that will be sent to all read, write, fetch, exist?, increment, and decrement
- * Add support for the :expires_in option to fetch and write for all caches. Cache entries are stored with the create timestamp and a ttl so that expiration can be handled independently of the implementation.
- * Add support for a :namespace option. This can be used to set a global prefix for cache entries.
- * Deprecate expand_cache_key on ActiveSupport::Cache and move it to ActionController::Caching and ActionDispatch::Http::Cache since the logic in the method used some Rails specific environment variables and was only used by ActionPack classes. Not very DRY but there didn't seem to be a good shared spot and ActiveSupport really shouldn't be Rails specific.
- * Add support for :race_condition_ttl to fetch. This setting can prevent race conditions on fetch calls where several processes try to regenerate a recently expired entry at once.
- * Add support for :compress option to fetch and write which will compress any data over a configurable threshold.
- * Nil values can now be stored in the cache and are distinct from cache misses for fetch.
- * Easier API to create new implementations. Just need to implement the methods read_entry, write_entry, and delete_entry instead of overwriting existing methods.
- * Since all cache implementations support storing objects, update the docs to state that ActiveCache::Cache::Store implementations should store objects. Keys, however, must be strings since some implementations require that.
- * Increase test coverage.
- * Document methods which are provided as convenience but which may not be universally available.
-
- MemoryStore:
- * MemoryStore can now safely be used as the cache for single server sites.
- * Make thread safe so that the default cache implementation used by Rails is thread safe. The overhead is minimal and it is still the fastest store available.
- * Provide :size initialization option indicating the maximum size of the cache in memory (defaults to 32Mb).
- * Add prune logic that removes the least recently used cache entries to keep the cache size from exceeding the max.
- * Deprecated SynchronizedMemoryStore since it isn't needed anymore.
-
- FileStore:
- * Escape key values so they will work as file names on all file systems, be consistent, and case sensitive
- * Use a hash algorithm to segment the cache into sub directories so that a large cache doesn't exceed file system limits.
- * FileStore can be slow so implement the LocalCache strategy to cache reads for the duration of a request.
- * Add cleanup method to keep the disk from filling up with expired entries.
- * Fix increment and decrement to use file system locks so they are consistent between processes.
-
- MemCacheStore:
- * Support all keys. Previously keys with spaces in them would fail
- * Deprecate CompressedMemCacheStore since it isn't needed anymore (use :compress => true)
-
-* JSON: encode objects that don't have a native JSON representation using to_hash, if available, instead of instance_values (the old fallback) or to_s (other encoders' default). Encode BigDecimal and Regexp encode as strings to conform with other encoders. Try to transcode non-UTF-8 strings. [Jeremy Kemper]
-
-* HashWithIndifferentAccess: remove inherited symbolize_keys! since its keys are always strings. [Santiago Pastorino]
-
-* Improve transliteration quality. #4374 [Norman Clarke]
-
-* Speed up and add Ruby 1.9 support for ActiveSupport::Multibyte::Chars#tidy_bytes. #4350 [Norman Clarke]
-
-* Reduced load time by deferring configuration of classes using
- ActiveSupport::on_load(:component_name) [YK]
-
-* Rename #metaclass to #singleton_class now that ruby-core has decided [JK]
-
-* New assertions assert_blank and assert_present. #4299 [Juanjo Bazan]
-
-* Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. [Jeremy Kemper]
-
-* JSON backend for YAJL. Preferred if available. #2666 [Brian Lopez]
-
-* Introduce class_attribute to declare inheritable class attributes. Writing an attribute on a subclass behaves just like overriding the superclass reader method. Unifies and replaces most usage of cattr_accessor, class_inheritable_attribute, superclass_delegating_attribute, and extlib_inheritable_attribute. [Jeremy Kemper, Yehuda Katz]
-
-* Time#- with a DateTime argument behaves the same as with a Time argument, i.e. returns the difference between self and arg as a Float #3476 [Geoff Buesing]
-
-* YAML serialization for OrderedHash. #3608 [Gregor Schmidt]
-
-* Update bundled TZInfo to v0.3.16 [Geoff Buesing]
-
-* Georgetown TimeZone is now mapped to "America/Guyana" instead of "America/Argentina/San_Juan" #1821 [Geoff Buesing, Reuben Sivan]
-
-* Changed the default ActiveSupport.use_standard_json_time_format from false to true and
-ActiveSupport.escape_html_entities_in_json from true to false to match previously announced Rails 3 defaults [DHH]
-
-* Added Object#presence that returns the object if it's #present? otherwise returns nil [DHH/Colin Kelley]
-
-* Add Enumerable#exclude? to bring parity to Enumerable#include? and avoid if !x.include?/else calls [DHH]
-
-* Update Edinburgh TimeZone to use "Europe/London" instead of "Europe/Dublin" #3310 [Phil Ross]
-
-* Update bundled TZInfo to v0.3.15 [Geoff Buesing]
-
-* JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON library agnostic. [Jeremy Kemper]
-
-* String #to_time and #to_datetime: handle fractional seconds #864 [Jason Frey]
-
-* Update bundled TZInfo to v0.3.13 [Geoff Buesing]
-
-* 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]
-
-* Time.local instances: Adding 24.hours across the DST boundary adds 24 hours instead of one day #2066 [Michael Curtis]
-
-
-*2.3.2 [Final] (March 15, 2009)*
-
-* XmlMini supports LibXML and Nokogiri backends. #2084, #2190 [Bart ten Brinke, Aaron Patterson]
- Example: XmlMini.backend = 'Nokogiri'
-
-* Vendorize i18n 0.1.3 gem (fixes issues with incompatible character encodings in Ruby 1.9) #2038 [Akira Matsuda]
-
-* Update bundled memcache-client from 1.5.0.5 to 1.6.4.99. See http://www.mikeperham.com/2009/02/15/memcache-client-performance/ [Mike Perham]
-
-* Ruby 1.9.1p0 fix: URI.unescape can decode multibyte chars. #2033 [MOROHASHI Kyosuke]
-
-* Time#to_s(:rfc822) uses #formatted_offset instead of unreliable and non-standard %z directive #1899 [Zachary Zolton]
-
-* Make TimeWithZone#to_formatted_s an alias to TimeWithZone#to_s #1796 [Levin Alexander]
-
-* Introduce Array.wrap(foo) to wrap the argument in an array unless it's already an array. Wraps nil as an empty array. Use instead of Array(foo) and foo.to_a since they treat String as Enumerable. [Jeremy Kemper]
-
-* TimeWithZone#xmlschema accepts optional fraction_digits argument [#1725 state:resolved] [Nicholas Dainty]
-
-* Object#tap shim for Ruby < 1.8.7. Similar to Object#returning, tap yields self then returns self. [Jeremy Kemper]
- array.select { ... }.tap(&:inspect).map { ... }
-
-* TimeWithZone#- gives correct result with wrapped DateTime, and with DateTime argument [Geoff Buesing]
-
-* Updated i18n gem to version 0.1.1 #1635 [Yaroslav Markin]
-
-* Add :allow_nil option to delegate. #1127 [Sergio Gil]
-
-* Add Benchmark.ms convenience method to benchmark realtime in milliseconds. [Jeremy Kemper]
-
-* Updated included memcache-client to the 1.5.0.5 version which includes fixes from fiveruns and 37signals to deal with failover and timeouts #1535 [Joshua Sierles]
-
-* Multibyte: add multibyte-safe Chars#ord rather than falling back to String#ord. #1483 [Jason Cheow]
-
-* I18n support for Array#to_sentence. Introduces support.array.words_connector, .two_words_connector, and .last_word_connector translation keys. #1397 [Akira Matsuda]
-
-* Added ActiveSupport::OrderedHash#each_key and ActiveSupport::OrderedHash#each_value #1410 [Christoffer Sawicki]
-
-* Added ActiveSupport::MessageVerifier and MessageEncryptor to aid users who need to store signed and/or encrypted messages. [Michael Koziarski]
-
-* Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers [David Heinemeier Hansson]
-
-* Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chris Wanstrath]
-
-* Added Enumerable#none? to check that none of the elements match the block #1408 [Damian Janowski]
-
-* TimeZone offset tests: use current_period, to ensure TimeZone#utc_offset is up-to-date [Geoff Buesing]
-
-* Update bundled TZInfo to 0.3.12 [Geoff Buesing]
-
-* Added lambda merging to OptionMerger (especially useful with named_scope and with_options) #726 [Paweł Kondzior]
-
-
-*2.2.1 [RC2] (November 14th, 2008)*
-
-* Increment the version of our altered memcache-client to prevent confusion caused when the 1.5.0 gem is installed.
-
-* Fixed the option merging in Array#to_xml #1126 [Rudolf Gavlas]
-
-* Make I18n::Backend::Simple reload its translations in development mode [David Heinemeier Hansson/Sven Fuchs]
-
-
-*2.2.0 [RC1] (October 24th, 2008)*
-
-* TimeWithZone#freeze: preload instance variables so that we can actually freeze [Geoff Buesing]
-
-* Fix Brasilia timezone #1180 [Marcus Derencius, Kane]
-
-* Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years #970 [Tom Lea]
-
-* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik Naik]
-
-* Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]
-
- This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.
-
-* TimeWithZone #wday, #yday and #to_date avoid trip through #method_missing [Geoff Buesing]
-
-* Added Time, Date, DateTime and TimeWithZone #past?, #future? and #today? #720 [Clemens Kofler, Geoff Buesing]
-
-* Fixed Sri Jayawardenepura time zone to map to Asia/Colombo [Jamis Buck]
-
-* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
-
-* Changed cache benchmarking to be reported in milliseconds [David Heinemeier Hansson]
-
-* Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing]
-
-* Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example:
-
- a = (1..10).to_a
- a.in_groups(3) # => [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]]
- a.in_groups(3, false) # => [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
-
-* Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone [Geoff Buesing]
-
-* Added Memoizable mixin for caching simple lazy loaded attributes [Josh Peek]
-
-* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. [Michael Koziarski]
-
-* Add Inflection rules for String#humanize. #535 [Dan Manges]
-
- ActiveSupport::Inflector.inflections do |inflect|
- inflect.human(/_cnt$/i, '\1_count')
- end
-
- 'jargon_cnt'.humanize # => 'Jargon count'
-
-* TimeWithZone: when crossing DST boundary, treat Durations of days, months or years as variable-length, and all other values as absolute length. A time + 24.hours will advance exactly 24 hours, but a time + 1.day will advance 23-25 hours, depending on the day. Ensure consistent behavior across all advancing methods [Geoff Buesing]
-
-* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller]
-
-* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] [David Heinemeier Hansson]
-
-* Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields]
-
-* Added Object#present? which is equivalent to !Object#blank? [David Heinemeier Hansson]
-
-* Added Enumberable#many? to encapsulate collection.size > 1 [David Heinemeier Hansson/Damian Janowski]
-
-* Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell]
-
-* Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek]
-
-* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false [David Heinemeier Hansson]
-
-* Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger)
-
-
-*2.1.0 (May 31st, 2008)*
-
-* TimeZone#to_s shows offset as GMT instead of UTC, because GMT will be more familiar to end users (see time zone selects used by Windows OS, google.com and yahoo.com.) Reverts [8370] [Geoff Buesing]
-
-* Hash.from_xml: datetime xml types overflow to Ruby DateTime class when out of range of Time. Adding tests for utc offsets [Geoff Buesing]
-
-* TimeWithZone #+ and #- : ensure overflow to DateTime with Numeric arg [Geoff Buesing]
-
-* Time#to_json: don't convert to utc before encoding. References #175 [Geoff Buesing]
-
-* Remove unused JSON::RESERVED_WORDS, JSON.valid_identifier? and JSON.reserved_word? methods. Resolves #164. [Cheah Chu Yeow]
-
-* Adding Date.current, which returns Time.zone.today if config.time_zone is set; otherwise returns Date.today [Geoff Buesing]
-
-* TimeWithZone: date part getter methods (#year #mon #day etc) are defined on class; no longer relying on method_missing [Geoff Buesing]
-
-* Time.zone.parse return nil for strings with no date information [Geoff Buesing]
-
-* Time.zone.parse respects offset information in string. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
-
-* Added Ruby 1.8 implementation of Process.daemon
-
-* Duration #since and #ago with no argument (e.g., 5.days.ago) return TimeWithZone when config.time_zone is set. Introducing Time.current, which returns Time.zone.now if config.time_zone is set, otherwise just returns Time.now [Geoff Buesing]
-
-* Time#since behaves correctly when passed a Duration. Closes #11527 [kemiller]
-
-* Add #getutc alias for DateTime#utc [Geoff Buesing]
-
-* Refactor TimeWithZone: don't send #since, #ago, #+, #-, #advance through method_missing [Geoff Buesing]
-
-* TimeWithZone respects config.active_support.use_standard_json_time_format [Geoff Buesing]
-
-* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping. [Rick Olson]
-
-* Improve documentation. [Xavier Noria]
-
-* Modified ActiveSupport::Callbacks::Callback#call to accept multiple arguments.
-
-* Time #yesterday and #tomorrow behave correctly crossing DST boundary. Closes #7399 [sblackstone]
-
-* TimeWithZone: Adding tests for dst and leap day edge cases when advancing time [Geoff Buesing]
-
-* TimeWithZone#method_missing: send to utc to advance with dst correctness, otherwise send to time. Adding tests for time calculations methods [Geoff Buesing]
-
-* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates. [Rick Olson]
-
-* TZInfo: Removing unneeded TimezoneProxy class [Geoff Buesing]
-
-* TZInfo: Removing unneeded TimezoneIndexDefinition, since we're not including Indexes::Timezones [Geoff Buesing]
-
-* Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled [Geoff Buesing]
-
-* Bundling abbreviated version of TZInfo gem 0.3.8: only the classes and zone definitions required to support Rails time zone features are included. If a recent version of the full TZInfo gem is installed, this will take precedence over the bundled version [Geoff Buesing]
-
-* TimeWithZone#marshal_load does zone lookup via Time.get_zone, so that tzinfo/Olson identifiers are handled [Geoff Buesing]
-
-* Time.zone= accepts TZInfo::Timezone instances and Olson identifiers; wraps result in TimeZone instance [Geoff Buesing]
-
-* TimeWithZone time conversions don't need to be wrapped in TimeOrDateTime, because TZInfo does this internally [Geoff Buesing]
-
-* TimeWithZone#usec returns 0 instead of error when DateTime is wrapped [Geoff Buesing]
-
-* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-
-* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter. [Rick Olson]
-
-* Ensure correct TimeWithZone#to_date [Geoff Buesing]
-
-* Make TimeWithZone work with tzinfo 0.2.x: use TZInfo::Timezone#zone_identifier alias for #abbreviation, silence warnings on tests. Raise LoadError when TZInfo version is < 0.2 by sniffing for TZInfo::TimeOrDateTime constant. Move all tzinfo-dependent TimeZone tests into uses_tzinfo block [Geoff Buesing]
-
-* Time, DateTime and TimeWithZone #in_time_zone defaults to Time.zone. Removing now unneeded #in_current_time_zone [Geoff Buesing]
-
-* TZInfo caches Timezone instances in its own internal hash cache, so TimeZone::MAPPING doesn't need to cache them as well [Geoff Buesing]
-
-* Adding TimeZone#parse [Geoff Buesing]
-
-* Adding TimeZone#at and DateTime#to_f [Geoff Buesing]
-
-* TimeWithZone responds to Ruby 1.9 weekday-named query methods [Geoff Buesing]
-
-* TimeWithZone caches TZInfo::TimezonePeriod used for time conversion so that it can be reused, and enforces DST rules correctly when instance is created from a local time [Geoff Buesing]
-
-* Fixed that BufferedLogger should create its own directory if one doesn't already exist #11285 [lotswholetime]
-
-* Fix Numeric time tests broken by DST change by anchoring them to fixed times instead of Time.now. Anchor TimeZone#now DST test to time specified with Time.at instead of Time.local to work around platform differences with Time.local and DST representation [Geoff Buesing]
-
-* Removing unneeded #change_time_zone method from Time, DateTime and TimeWithZone [Geoff Buesing]
-
-* TimeZone #local and #now correctly enforce DST rules [Geoff Buesing]
-
-* TimeWithZone instances correctly enforce DST rules. Adding TimeZone#period_for_utc [Geoff Buesing]
-
-* test_time_with_datetime_fallback expects DateTime.local_offset instead of DateTime.now.offset [Geoff Buesing]
-
-* Adding TimeWithZone #marshal_dump and #marshal_load [Geoff Buesing]
-
-* Add OrderedHash#to_hash [Josh Peek]
-
-* Adding Time#end_of_day, _quarter, _week, and _year. #9312 [Juanjo Bazan, Tarmo Tänav, BigTitus]
-
-* Adding TimeWithZone#between? [Geoff Buesing]
-
-* Time.=== returns true for TimeWithZone instances [Geoff Buesing]
-
-* TimeWithZone #+ and #- behave consistently with numeric arguments regardless of whether wrapped time is a Time or DateTime; consistenty answers false to #acts_like?(:date) [Geoff Buesing]
-
-* Add String#squish and String#squish! to remove consecutive chunks of whitespace. #11123 [Jordi Bunster, Henrik N]
-
-* Serialize BigDecimals as Floats when using to_yaml. #8746 [Ernesto Jimenez]
-
-* Adding TimeWithZone #to_yaml, #to_datetime, #eql? and method aliases for duck-typing compatibility with Time [Geoff Buesing]
-
-* TimeWithZone #in_time_zone returns +self+ if zone argument is the same as #time_zone [Geoff Buesing]
-
-* Adding TimeWithZone #to_a, #to_f, #to_i, #httpdate, #rfc2822 [Geoff Buesing]
-
-* Pruning unneeded TimeWithZone#change_time_zone_to_current [Geoff Buesing]
-
-* Time#zone=, #in_time_zone and #change_time_zone accept a Duration [Geoff Buesing]
-
-* Time#in_time_zone handles Time.local instances correctly [Geoff Buesing]
-
-* Pruning unneeded Time#change_time_zone_to_current. Enhanced docs to #change_time_zone to explain the difference between this method and #in_time_zone [Geoff Buesing]
-
-* TimeZone#new method renamed #local; when used with Time.zone, constructor now reads: Time.zone.local() [Geoff Buesing]
-
-* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing [David Heinemeier Hansson]
-
-* Remove :nodoc: entries around the ActiveSupport test/unit assertions. #10946 [dancroak, jamesh]
-
-* Add Time.zone_default accessor for setting the default time zone. Rails::Configuration.time_zone sets this. #10982 [Geoff Buesing]
-
-* cache.fetch(key, :force => true) to force a cache miss. [Jeremy Kemper]
-
-* Support retrieving TimeZones with a Duration. TimeZone[-28800] == TimeZone[-480.minutes]. [Rick Olson]
-
-* TimeWithZone#- added, so that #- can handle a Time or TimeWithZone argument correctly [Geoff Buesing]
-
-* with_timezone test helper renamed with_env_tz, to distinguish between setting ENV['TZ'] and setting Time.zone in tests [Geoff Buesing]
-
-* Time#- coerces TimeWithZone argument to a Time instance so that difference in seconds can be calculated. Closes #10914 [Geoff Buesing, yyyc514]
-
-* Adding UTC zone to TimeZone; TimeWithZone no longer has to fake UTC zone with nil [Geoff Buesing]
-
-* Time.get_zone refactored to private method, given that the encapsulated logic is only useful internally [Geoff Buesing]
-
-* Time.zone uses thread-local variable for thread safety. Adding Time.use_zone, for overriding Time.zone locally inside a block. Removing unneeded Time.zone_reset! [Geoff Buesing]
-
-* TimeZone#to_s uses UTC rather than GMT; reapplying change that was undone in [8679]. #1689 [Cheah Chu Yeow]
-
-* Time.days_in_month defaults to current year if no year is supplied as argument #10799 [Radar], uses Date.gregorian_leap? to determine leap year, and uses constant lookup to determine days in month [Geoff Buesing]
-
-* Adding Time and DateTime #compare_with_coercion, which layers behavior on #<=> so that any combination of Time, DateTime and ActiveSupport::TimeWithZone instances can be chronologically compared [Geoff Buesing]
-
-* TimeZone#now returns an ActiveSupport::TimeWithZone [Geoff Buesing]
-
-* Time #in_current_time_zone and #change_time_zone_to_current return self when Time.zone is nil [Geoff Buesing]
-
-* Remove unneeded #to_datetime_default_s alias for DateTime#to_s, given that we inherit a #to_default_s from Date that does exactly the same thing [Geoff Buesing]
-
-* Refactor Time and DateTime #to_formatted_s: use ternary instead of nested if/else [Geoff Buesing]
-
-* Adding Time and DateTime #formatted_offset, for outputting +HH:MM utc offset strings with cross-platform consistency [Geoff Buesing]
-
-* Adding alternate_utc_string option to TimeZone#formatted_offset. Removing unneeded TimeZone#offset. [Geoff Buesing]
-
-* Introduce ActiveSupport::TimeWithZone, for wrapping Time instances with a TimeZone. Introduce instance methods to Time for creating TimeWithZone instances, and class methods for managing a global time zone. [Geoff Buesing]
-
-* Replace non-dst-aware TimeZone class with dst-aware class from tzinfo_timezone plugin. TimeZone#adjust and #unadjust are no longer available; tzinfo gem must now be present in order to perform time zone calculations, via #local_to_utc and #utc_to_local methods. [Geoff Buesing]
-
-* Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher. #10727 [Josh Peek]
-
-* Introducing DateTime #utc, #utc? and #utc_offset, for duck-typing compatibility with Time. Closes #10002 [Geoff Buesing]
-
-* Time#to_json uses Numeric#to_utc_offset_s to output a cross-platform-consistent representation without having to convert to DateTime. References #9750 [Geoff Buesing]
-
-* Refactor number-to-HH:MM-string conversion logic from TimeZone#formatted_offset to a reusable Numeric#to_utc_offset_s method. [Geoff Buesing]
-
-* Continue evolution toward ActiveSupport::TestCase. #10679 [Josh Peek]
-
-* TestCase: introduce declared setup and teardown callbacks. Pass a list of methods and an optional block to call before setup or after teardown. Setup callbacks are run in the order declared; teardown callbacks are run in reverse. [Jeremy Kemper]
-
-* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Lütke]
-
-* Included MemCache-Client to make the improved ActiveSupport::Cache::MemCacheStore work out of the box [Bob Cottrell, Eric Hodel]
-
-* Added ActiveSupport::Cache::* framework as an extraction from ActionController::Caching::Fragments::* [David Heinemeier Hansson]
-
-* Fixed String#titleize to work for strings with 's too #10571 [trek]
-
-* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [David Heinemeier Hansson/Marcel Molina Jr.]
-
-* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. [Rick Olson]
-
-* Hash#symbolize_keys skips keys that can't be symbolized. #10500 [Brad Greenlee]
-
-* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554, #10594, #10632 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, Xavier Noria]
-
-* TimeZone#to_s uses UTC rather than GMT. #1689 [Cheah Chu Yeow]
-
-* Refactor of Hash#symbolize_keys! to use Hash#replace. Closes #10420 [ReinH]
-
-* Fix HashWithIndifferentAccess#to_options! so it doesn't clear the options hash. Closes #10419 [ReinH]
-
-
-*2.0.1* (December 7th, 2007)
-
-* Added Array#from and Array#to that behaves just from String#from and String#to [David Heinemeier Hansson]
-
-* Fix that empty collections should be treated as empty arrays regardless of whitespace for Hash#from_xml #10255 [adamj]
-
-* Time#time_with_datetime_fallback, Time#to_datetime, Date#to_datetime and String#to_datetime honor Ruby's default calendar reform setting. #10201 [Geoff Buesing]
-
-* Change Time and DateTime #end_of_month to return last second of month instead of beginning of last day of month. Closes #10200 [Geoff Buesing]
-
-* Speedup String#blank? [Jeremy Kemper, Michael Koziarski]
-
-* Add documentation for Hash#diff. Closes #9306 [Tarmo Tänav]
-
-* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Michael Koziarski, Tarmo Tänav]
-
-* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 [Josh Peek, Cheah Chu Yeow, Tim Pope]
-
-* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick Olson, theamazingrando]
-
-* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Michael Koziarski]
-
- The intention is to use this to reduce the amount of monkeypatching / overriding that
- is done to test/unit's classes.
-
-* Document Enumerable and Hash #to_json. #9970 [Cheah Chu Yeow]
-
-* Hash#to_xml handles symbol values. #9954 [Assaf]
-
-* Hash#symbolize_keys behaves well with integer keys. #9890 [PotatoSalad]
-
-* Multibyte: String#slice supports regexp argument. #9646 [yob]
-
-* object.duplicable? returns true if object.dup is safe. False for nil, true, false, symbols, and numbers; true otherwise. #9333 [sur]
-
-* Time, Date and DateTime #advance accept :weeks option. #9866 [Geoff Buesing]
-
-* Fix Time#years_ago and #years_since from leap days. #9865 [Geoff Buesing]
-
-* Time and DateTime#advance accept :hours, :minutes, and :seconds options. #9825 [Geoff Buesing]
-
-* Fix Date#years_ago and #years_since from leap days. #9864 [Geoff Buesing]
-
-* Refactor Time and Date#months_since and #months_ago to use #advance. #9863 [Geoff Buesing]
-
-* Rebundle Builder 2.1.2 but prefer a newer RubyGem if available. [Jeremy Kemper]
-
-* Add Range#overlaps?(range), Range#include?(range), and Range#step without a block. [brandon]
-
-* Correct BufferedLogger#level? checks. #9806 [wildchild, Johan Sorensen]
-
-* String#to_xs uses Eric Wong's fast_xs extension, if available, for Builder speedup. http://bogomips.org/fast_xs/ [Jeremy Kemper]
-
-* Introduce BasicObject as Builder::BlankSlate for Ruby 1.9 forward compatibility. [Jeremy Kemper]
-
-* Unbundle Builder in favor of a gem dependency. [Jeremy Kemper]
-
-* Disambiguate Time, Date, and DateTime#to_json formatting. #9750 [Geoff Buesing, Cheah Chu Yeow]
-
-* Hash#to_json takes :only or :except options to specific or omit certain hash keys. Enumerable#to_json passes through its options to each element. #9751 [Cheah Chu Yeow]
-
-* BufferedLogger#auto_flushing = N flushes the log every N messages. Buffers with an array instead of string. Disabling auto_flushing still flushes when the buffer hits a maximum size, as a failsafe against memory-gobbling. [Jeremy Kemper]
-
-* Fixed Date#xmlschema for dates outside the range of what can be created with Time #9744 [Geoff Buesing]
-
-* Fixed that La Paz was included in -25200 and -14400 offsets when it should only be in -14400 #9735 [bermi]
-
-* Fixed JSON encoding to use quoted keys according to the JSON standard. #8762 [choonkat, Cheah Chu Yeow]
-
-* Alias Object#send to send! for Ruby 1.9 forward compatibility. [Jeremy Kemper]
-
-* Backport Object#instance_variable_defined? for Ruby < 1.8.6. [Jeremy Kemper]
-
-* BufferedLogger#add converts the message to a string. #9702, #9724 [eigentone, DrMark, Tom Ward]
-
-* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [David Heinemeier Hansson]
-
-* Object#instance_exec produces fewer garbage methods. [Mauricio Fernandez]
-
-* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 [Rick Olson]
-
-* Fixed cache_page to use the request url instead of the routing options when picking a save path. #8614 [Josh Peek]
-
-* Object.subclasses_of includes anonymous subclasses. [Jeremy Kemper]
-
-* Fixed that pluralizing an empty string should return the same empty string, not "s". #7720 [Josh Peek]
-
-* Added call to inspect on non-string classes for the logger #8533 [Coda Hale]
-
-* Deprecation: remove deprecated :mday option from Time, Date, and DateTime#change. [Jeremy Kemper]
-
-* Fix JSON decoder with nested quotes and commas. #9579 [Zach Dennis]
-
-* Hash#to_xml doesn't double-unescape. #8806 [Ezran]
-
-* Added Array#rand #9170 [Norbert Crombach]. Examples:
-
- [].rand # => nil
- ['a'].rand # => 'a'
- [1,2,3].rand # => 1 or 2 or 3
-
-* Deprecation: removed Reloadable. [Jeremy Kemper]
-
-* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [Manfred Stienstra]
-
-* Add ljust, rjust and center to utf8-handler. Closes #9165 [Manfred Stienstra]
-
-* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 [Geoff Buesing]
-
-* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, Manfred Stienstra]
-
-* Added Array#extract_options! to encapsulate the pattern of getting an options hash out of a variable number of parameters. #8759 [Norbert Crombach]
-
-* Let alias_attribute work with attributes with initial capital letters (legacy columns etc). Closes #8596 [mpalmer]
-
-* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [David Heinemeier Hansson]
-
-* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/Josh Susser]
-
-* Demote Hash#to_xml to use XmlSimple#xml_in_string so it can't read files or stdin. #8453 [candlerb, Jeremy Kemper]
-
-* Backport clean_logger changes to support ruby 1.8.2 [Mislav Marohnić]
-
-* Added proper handling of arrays #8537 [Josh Susser]
-
- Before:
- Hash.from_xml '<images></images>'
- # => {:images => nil}
-
- Hash.from_xml '<images><image>foo.jpg</image></images>'
- # => {:images => {:image => "foo.jpg"}}
-
- Hash.from_xml '<images><image>foo.jpg</image><image>bar.jpg</image></images>'
- # => {:images => {:image => ["foo.jpg", "bar.jpg"]}}
-
- After:
- Hash.from_xml '<images type="array"></images>'
- # => {:images => []}
-
- Hash.from_xml '<images type="array"><image>foo.jpg</image></images>'
- # => {:images => ["foo.jpg"]}
-
- Hash.from_xml '<images type="array"><image>foo.jpg</image><image>bar.jpg</image></images>'
- # => {:images => ["foo.jpg", "bar.jpg"]}
-
-* Improve Time and Date test coverage. #8646 [Josh Peek]
-
-* Add Date#since, ago, beginning_of_day, and end_of_day. Date + seconds works now. #8575 [Geoff Buesing]
-
-* String#to_time overflows to DateTime. Add String#to_datetime. #8572 [Geoff Buesing]
-
-* Date.yesterday and .tomorrow. #8571 [Geoff Buesing]
-
-* Readable Date and DateTime#inspect. #8570 [Geoff Buesing]
-
-* Move common DateTime calculations to Date. #8536 [Geoff Buesing]
-
-* Added Date#change (like Time#change) [David Heinemeier Hansson]
-
-* DateTime#to_time converts to Time unless out of range. #8512 [Geoff Buesing]
-
-* Date#to_datetime, #to_s(:rfc822). #8512 [Geoff Buesing]
-
-* Time durations use since instead of + for accuracy. #8513 [Geoff Buesing]
-
-* escape <'s and >'s in JSON strings. #8371 [Rick Olson]
-
-* Inflections: MatrixTest -> MatrixTests instead of MatricesTest. #8496 [jbwiv]
-
-* Multibyte strings respond_to the String methods they proxy so they can be duck-typed. #6549 [Tuxie]
-
-* Array#to_xml yields the builder just like Hash and ActiveRecord::Base. #8472 [seth]
-
-* Date, Time, and DateTime support formatting blocks in addition to strftime strings. Introduce :long_ordinal format, e.g. "February 21st, 2005". #8191 [Coda Hale]
-
-* Document Object#blank?. #6491 [Chris Mear]
-
-* Date, Time, and DateTime#to_json. #8399 [wycats]
-
-* Simplify API of assert_difference by passing in an expression that is evaluated before and after the passed in block. See documenation for examples of new API. [Marcel Molina Jr.]
-
-* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Lütke]
-
-* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [David Heinemeier Hansson]
-
-* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [David Heinemeier Hansson]
-
- <person>
- <name>David</name>
- <avatar type="file" name="me.jpg" content_type="image/jpg">R0lGODlhkACZAPUAAM5lcfjrtMQCG=\n</avatar>
- </person>
-
- ...becomes:
-
- attributes = { :person => { :name => "David", :avatar => #<StringIO> } }
- attributes[:person][:avatar].content_type # => "image/jpg"
- attributes[:person][:avatar].original_filename # => "me.jpg"
- attributes[:person][:avatar].read # => binary data of the file
-
- Which is duck-type compatible with the files that you get when doing multipart uploads through HTML.
-
-* Improved multibyte performance by relying less on exception raising #8159 [Blaine]
-
-* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]
-
-* Added yielding of builder in Hash#to_xml [David Heinemeier Hansson]
-
-* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [David Heinemeier Hansson]
-
-* Hash#to_xml supports YAML attributes. #7502 [jonathan]
-
-* Refactor ActiveSupport::JSON to be less obtuse. Add support for JSON decoding by way of Syck with ActiveSupport::JSON.decode(json_string). Prevent hash keys that are JavaScript reserved words from being unquoted during encoding. [Sam Stephenson]
-
-* alias_method_chain preserves the original method's visibility. #7854 [Jonathan Viney]
-
-* Update Dependencies to ignore constants inherited from ancestors. Closes #6951. [Nicholas Seckar]
-
-* Array#to_query preserves its ordering. #7756 [Greg Spurrier]
-
-* Out-of-range Time calculations transparently overflow to DateTime. Introduce Time#to_datetime. #7706, #7715 [Geoff Buesing]
-
-* DateTime calculations analogous to the Date and Time extensions. #7693 [Geoff Buesing]
-
-* Give DateTime correct .to_s implementations, lets it play nice with ActiveRecord quoting. #7649 [Geoff Buesing]
-
-* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Michael Koziarski]
-
-* Allow users to provide custom formatters to Logger. [Anthony Eden]
-
-* Hash#to_query CGI-escapes its keys. [Jeremy Kemper]
-
-* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 [Bruce Perens]
-
-* :db format for Date#to_s [Jeremy Kemper]
- Date.new(2007, 1, 27).to_s(:db) # => '2007-01-27'
-
-* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick Olson]
-
-* Added Hash#to_query to turn a hash of values into a form-encoded query string [Nicholas Seckar]
-
-* Increase test coverage for subclasses_of. Closes #7335. [Roman2K, Nicholas Seckar]
-
-* Remove unused code from Duration#inspect. Closes #7180. [Rich Collins]
-
-* Added test coverage for Inflector.inflections.clear. Closes #7179. [Rich Collins]
-
-* ActiveSupport::Multibyte::Handlers::UTF8Handler should raise when a range and an integer are passed in (just like the native implementation). Closes #7176 [Rich Collins]
-
-* A couple extra tests for #classify. Closes #7273. [Josh Susser]
-
-* Better docs for Object extensions [zackchandler, Jamis Buck]
-
-* Fix that Dates couldn't be subtracted from Dates after [5940]. [Sam Stephenson]
-
-* Add Object#acts_like? and Time#acts_like_time? and Date#acts_like_date? to facilitate duck-typing. [Jamis Buck]
-
-* Make 1.months and friends accurate by introducing a Duration class. #6835 [eventualbuddha]
-
-
-*1.4.2* (March 12th, 2007)
-
-* Ruby 1.8.6 and 1.9 define private Time#to_date and #to_datetime; make them
-public for compatibility. [Jeremy Kemper]
-
-* Deprecation: warn on stderr if RAILS_DEFAULT_LOGGER isn't set yet. [Jeremy Kemper]
-
-
-*1.4.1* (February 5th, 2007)
-
-* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 [Bruce Perens]
-
-* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick Olson]
-
-* Full test coverage for Inflector. #7228 [Dan Kubb]
-
-
-*1.4.0* (January 16th, 2007)
-
-* Document Inflector.ordinalize and merge docs from String inflections. #7023 [smeade]
-
-* Unbundle flexmock. [Jeremy Kemper]
-
-* Fix Dependencies.autoloaded? to ignore anonymous modules. Closes #6561. [Nicholas Seckar]
-
-* Update load once paths to prevent nested once constants from being detected and claimed by an external non-once load. [Nicholas Seckar]
-
-* Deprecation: silence warnings when reporting test errors. [Jeremy Kemper]
-
-* Hash#slice(*keys) returns a new hash with only the given keys. #slice! replaces the hash with only the given keys. Works with HashWithIndifferentAccess also. [Jeremy Kemper]
-
-* HashWithIndifferentAccess#to_hash converts to a Hash with String keys and the same default value. [Jeremy Kemper]
-
-* Fix remove_constant to correctly handle constant names of the form "::A::...". References #6720. [Nicholas Seckar]
-
-* Fixed Array#to_xml when it contains a series of hashes (each piece would get its own XML declaration) #6610 [thkarcher/cyu]
-
-* Added Time#to_s(:time) which will just return H:M, like 17:44 [David Heinemeier Hansson]
-
-* Add Module#attr_accessor_with_default to initialize value of attribute before setting it. Closes #6538. [Stuart Halloway, Marcel Molina Jr.]
-
-* Hash#to_xml handles keys with the same name as Kernel methods. #6613 [Jonathan del Strother]
-
-* Added Time#end_of_day to get 23:59:59 of that day [David Heinemeier Hansson]
-
-* Don't quote hash keys in Hash#to_json if they're valid JavaScript identifiers. Disable this with ActiveSupport::JSON.unquote_hash_key_identifiers = false if you need strict JSON compliance. [Sam Stephenson]
-
-* Lazily load the Unicode Database in the UTF-8 Handler [Rick Olson]
-
-* Update dependencies to delete partially loaded constants. [Nicholas Seckar]
-
-* Fix unicode JSON regexp for Onigurama compatibility. #6494 [whitley]
-
-* update XmlSimple to 1.0.10. Closes #6532. [Nick Sieger]
-
-* Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. [Nicholas Seckar]
-
-* next_week respects DST changes. #6483, #5617, #2353, #2509, #4551 [marclove, Rob Biedenharn, rails@roetzel.de, jsolson@damogran.org, drbrain@segment7.net]
-
-* Expose methods added to Enumerable in the documentation, such as group_by. Closes #6170. [sergeykojin@gmail.com, Marcel Molina Jr.]
-
-* Ensure Chars#tidy_bytes only tidies broken bytes. Closes #6397 [Manfred Stienstra]
-
-* Add 'unloadable', a method used to mark any constant as requiring an unload after each request. [Nicholas Seckar]
-
-* Make core_ext/string/access.rb multibyte safe. Closes #6388 [Manfred Stienstra]
-
-* Make String#chars slicing behaviour consistent with String. Closes #6387 [Manfred Stienstra]
-
-* Pull in latest multibyte patch. Closes #6346 [Manfred Stienstra]
-
-* Add ActiveSupport::Multibyte. Provides String#chars which lets you deal with strings as a sequence of chars, not of bytes. Closes #6242 [Julian Tarkhanov, Manfred Stienstra, Thijs van der Vossen & Jan Behrens]
-
-* Fix issue with #class_inheritable_accessor saving updates to the parent class when initialized with an Array or Hash [mojombo]
-
-* Hash#to_xml supports Bignum and BigDecimal. #6313 [edibiase]
-
-* Don't undefine #class in OptionMerger [Rick Olson]
-
-* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 [David Heinemeier Hansson]
-
-* alias_method_chain works with accessor= methods also. #6153 [Caio Chassot]
-
-* Fix loadable_constants_for_path to handle load paths that do not end with a slash. [Nicholas Seckar]
-
-* Fix logic error in determining what was loaded by a given file. Closes #6039. [Nicholas Seckar]
-
-* Equate Kernel.const_missing with Object.const_missing. Fixes #5988. [Nicholas Seckar]
-
-* Add ApplicationController special case to Dependencies. [Nicholas Seckar]
-
-* Don't pad remaining places with in_groups_of if specified padding value is false. [Marcel Molina Jr.]
-
-* Fix cases where empty xml nodes weren't being translated to nil in Hash.create_from_xml [Rick Olso n]
-
- <written-on type="date"></written-on> # => { :type => 'date' } # WRONG
- <written-on type="date"></written-on> # => nil # RIGHT
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Inflections: don't singularize -ies plurals. [foamdino@gmail.com, Mark Van Holstyn]
-
-* Update Initializer to use load_once_paths to avoid plugin reloading. References #5852. [Nicholas Seckar]
-
-* Use Array#assoc in ActiveSupport::OrderedHash. [Mauricio Fernandez]
-
-* Greatly increased performance of String.to_json, which speeds up RJS considerably on large pages, fixes #3473 [Shugo Maeda]
-
-* Detect missing_constants calls from removed modules and fail accordingly. [Nicholas Seckar]
-
-* Stop using defined? in Dependencies.qualified_const_defined? since defined? may invoke const_missing. [Nicholas Seckar]
-
-* Dependencies can autoload directories of nested classes. [Jeremy Kemper]
- Example:
- invoice.rb class Invoice
- invoice/lineitem.rb class Invoice::Lineitem
-
-* Add Deprecation.silence so that Reloadable does not scold itself. [Nicholas Seckar]
-
-* Add debugging logging to Dependencies. Currently can be enabled with Dependencies.log_activity = true; adding to Initializer and documenting is forthcoming. [Nicholas Seckar]
-
-* Replace Reloadable with improvements to the Dependencies mechanism. [Nicholas Seckar]
-
-* DateTime#to_time gives hour/minute/second resolution. #5747 [jon.evans@pobox.com]
-
-* attr_internal to support namespacing and deprecation. Like attr_* except backed by internally-named instance variable. Set attr_internal_naming_format to change the format from the default '@_%s'. [Jeremy Kemper]
- # def foo() @foo__rofl end
- # def foo=(v) @foo__rofl = v end
- self.attr_internal_naming_format = '@%s__rofl'
- attr_internal :foo
-
-* Raise fully qualified names upon name errors. #5533 [Lars Pind, Nicholas Seckar]
-
-* Add extention to obtain the missing constant from NameError instances. [Nicholas Seckar]
-
-* Thoroughly document inflections. #5700 [petermichaux@gmail.com]
-
-* Added Module#alias_attribute [Jamis/David Heinemeier Hansson]. Example:
-
- class Content < ActiveRecord::Base
- # has a title attribute
- end
-
- class Email < ActiveRecord::Base
- alias_attribute :subject, :title
- end
-
- e = Email.find(1)
- e.title # => "Superstars"
- e.subject # => "Superstars"
- e.subject? # => true
- e.subject = "Megastars"
- e.title # => "Megastars"
-
-* Deprecation: easier to work with warning behavior as procs; default behaviors for each environment so users needn't update env.rb; and testing pleasure with assert_deprecated, assert_not_deprecated. [Jeremy Kemper]
- By default, test prints to $stderr, dev logs, production ignores.
- Provide your own per-environment in e.g. config/environments/development.rb:
- ActiveSupport::Deprecation.behavior = Proc.new { |message| raise message }
-
-* First cut of the Rails Deprecation system. [Michael Koziarski]
-
-* Strip boolean XML content before checking for 'true' [Rick Olson]
-
-* Customize default BigDecimal formatting. References #5672 [Dave Thomas]
-
-* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml. [Rick Olson]
-
-* Optional identity for Enumerable#sum defaults to zero. #5657 [gensym@mac.com]
-
-* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 [Shugo Maeda]
-
-* Fixed HashWithIndifferentAccess#default #5586 [chris@seagul.co.uk]
-
-* More compatible Hash.create_from_xml. #5523 [nunemaker@gmail.com]
-
-* Added Enumerable#sum for calculating a sum from the elements [David Heinemeier Hansson, jonathan@daikini.com]. Examples:
-
- [1, 2, 3].sum
- payments.sum { |p| p.price * p.tax_rate }
- payments.sum(&:price)
-
- This is instead of payments.inject(0) { |sum, p| sum + p.price }
-
-* Correct and clarify Array#to_sentence docs. #5458 [brad@madriska.com]
-
-* alias_method_chain preserves method punctuation so foo, foo?, and foo! may be chained with the same feature. [Jeremy Kemper]
- Example:
- alias_method_chain :save!, :validation
- is equivalent to
- alias_method :save_without_validation!, :save!
- alias_method :save!, :save_with_validation!
-
-* Enhance Symbol#to_proc so it works with list objects, such as multi-dimensional arrays. Closes #5295 [nov@yo.rim.or.jp]. Example:
-
- {1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last)
- # => ["one", "two", "three"]
-
-* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [David Heinemeier Hansson]. Example:
-
- Hash.create_from_xml <<-EOT
- <note>
- <title>This is a note</title>
- <created-at type="date">2004-10-10</created-at>
- </note>
- EOT
-
- ...would return:
-
- { :note => { :title => "This is a note", :created_at => Date.new(2004, 10, 10) } }
-
-* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim@weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary [David Heinemeier Hansson]
-
-* Fixed that Module#alias_method_chain should work with both foo? foo! and foo at the same time #4954 [anna@wota.jp]
-
-* to_xml fixes, features, and speedup: introduce :dasherize option that converts updated_at to updated-at if true (the existing default); binary columns get encoding="base64" attribute; nil values get nil="true" attribute to distinguish empty values; add type information for float columns; allow arbitrarily deep :include; include SQL type information as the type attribute. #4989 [Blair Zajac <blair@orcaware.com>]
-
-* Add OrderedHash#values. [Sam Stephenson]
-
-* Added Array#to_s(:db) that'll produce a comma-separated list of ids [David Heinemeier Hansson]. Example:
-
- Purchase.find(:all, :conditions => "product_id IN (#{shops.products.to_s(:db)})"
-
-* Normalize classify's argument to a String so that it plays nice with Symbols. [Marcel Molina Jr.]
-
-* Strip out leading schema name in classify. References #5139. [Michael Schoen]
-
-* Remove Enumerable#first_match since break(value) handles the use case well enough. [Nicholas Seckar]
-
- Enumerable#first_match was like detect, but instead of returning the matching element, the yielded value returned. For example:
-
- user_xml = adapters(:from => User, :to => Xml).first_match do |adapter|
- adapter.adapt @user
- end
-
- But this is just as easily done with:
-
- user_xml = adapters(:from => User, :to => Xml).each do
- break adapter.adapt(@user)
- end
-
-* Make Array#in_groups_of just return the grouped collection if a block isn't given. [Marcel Molina Jr.]
-
-* Don't destroy a HashWithIndifferentAccess if symbolize_keys! or stringify_keys! is called on it. Closes #5076. [Marcel Molina Jr., guy.naor@famundo.com]
-
-* Document Module::delegate. #5002 [pergesu@gmail.com]
-
-* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
-
-* Strip out punctuation on predicates or bang methods being aliased with alias_method_chain since target?_without_feature is not a valid method name. Add tests for Module#alias_method_chain. [Marcel Molina Jr.]
-
-* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-
-* Allow default options in with_options to be overridden. Closes #4480. [murphy@cYcnus.de]
-
-* Added Module#alias_method_chain [Jamis Buck]
-
-* Updated to Builder 2.0 [David Heinemeier Hansson]
-
-* Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson]
-
-*1.3.1* (April 6th, 2006)
-
-* Clean paths inside of exception messages and traces. [Nicholas Seckar]
-
-* Add Pathname.clean_within for cleaning all the paths inside of a string. [Nicholas Seckar]
-
-* provide an empty Dependencies::LoadingModule.load which prints deprecation warnings. Lets 1.0 applications function with .13-style environment.rb.
-
-
-*1.3.0* (March 27th, 2006)
-
-* When possible, avoid incorrectly obtaining constants from parent modules. Fixes #4221. [Nicholas Seckar]
-
-* Add more tests for dependencies; refactor existing cases. [Nicholas Seckar]
-
-* Move Module#parent and Module#as_load_path into core_ext. Add Module#parent. [Nicholas Seckar]
-
-* Add CachingTools::HashCaching to simplify the creation of nested, autofilling hashes. [Nicholas Seckar]
-
-* Remove a hack intended to avoid unloading the same class twice, but which would not work anyways. [Nicholas Seckar]
-
-* Update Object.subclasses_of to locate nested classes. This affects Object.remove_subclasses_of in that nested classes will now be unloaded. [Nicholas Seckar]
-
-* Update Object.remove_subclasses_of to use Class.remove_class, reducing duplication. [Nicholas Seckar]
-
-* Added Fixnum#seconds for consistency, so you can say 5.minutes + 30.seconds instead of 5.minutes + 30 #4389 [François Beausoleil]
-
-* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" [David Heinemeier Hansson]
-
-* Added Hash#diff to show the difference between two hashes [Chris McGrath]
-
-* Added Time#advance to do precise time time calculations for cases where a month being approximated to 30 days won't do #1860 [Rick Olson]
-
-* Enhance Inflector.underscore to convert '-' into '_' (as the inverse of Inflector.dasherize) [Jamis Buck]
-
-* Switched to_xml to use the xml schema format for datetimes. This allows the encoding of time zones and should improve operability. [Michael Koziarski]
-
-* Added a note to the documentation for the Date related Numeric extensions to indicate that they're
-approximations and shouldn't be used for critical calculations. [Michael Koziarski]
-
-* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [David Heinemeier Hansson]. Examples:
-
- { :name => "David", :street_name => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml
-
- ...returns:
-
- <person>
- <street-name>Paulina</street-name>
- <name>David</name>
- <age type="integer">26</age>
- <moved-on type="date">2005-11-15</moved-on>
- </person>
-
-* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) [David Heinemeier Hansson]
-
-* Fixed that Array#to_sentence will return "" on an empty array instead of ", and" #3842, #4031 [rubyonrails@beautifulpixel.com]
-
-* Add Enumerable#group_by for grouping collections based on the result of some
- block. Useful, for example, for grouping records by date.
-
- ex.
-
- latest_transcripts.group_by(&:day).each do |day, transcripts|
- p "#{day} -> #{transcripts.map(&:class) * ', '}"
- end
- "2006-03-01 -> Transcript"
- "2006-02-28 -> Transcript"
- "2006-02-27 -> Transcript, Transcript"
- "2006-02-26 -> Transcript, Transcript"
-
- Add Array#in_groups_of, for iterating over an array in groups of a certain
- size.
-
- ex.
-
- %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
- ["1", "2", "3"]
- ["4", "5", "6"]
- ["7", nil, nil]
-
- [Marcel Molina Jr., Sam Stephenson]
-
-* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [David Heinemeier Hansson]
-
-* Add 'around' methods to Logger, to make it easy to log before and after messages for a given block as requested in #3809. [Michael Koziarski] Example:
-
- logger.around_info("Start rendering component (#{options.inspect}): ",
- "\n\nEnd of component rendering") { yield }
-
-* Added Time#beginning_of_quarter #3607 [cohen.jeff@gmail.com]
-
-* Fix Object.subclasses_of to only return currently defined objects [Jonathan Viney <jonathan@bluewire.net.nz>]
-
-* Fix constantize to properly handle names beginning with '::'. [Nicholas Seckar]
-
-* Make String#last return the string instead of nil when it is shorter than the limit [Scott Barron].
-
-* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [David Heinemeier Hansson]. Example:
-
- class Account < ActiveRecord::Base
- has_one :subscription
- delegate :free?, :paying?, :to => :subscription
- delegate :overdue?, :to => "subscription.last_payment"
- end
-
- account.free? # => account.subscription.free?
- account.overdue? # => account.subscription.last_payment.overdue?
-
-* Fix Reloadable to handle the case where a class that has been 'removed' has not yet been garbage collected. [Nicholas Seckar]
-
-* Don't allow Reloadable to be included into Modules.
-
-* Remove LoadingModule. [Nicholas Seckar]
-
-* Add documentation for Reloadable::Subclasses. [Nicholas Seckar]
-
-* Add Reloadable::Subclasses which handles the common case where a base class should not be reloaded, but its subclasses should be. [Nicholas Seckar]
-
-* Further improvements to reloading code [Nicholas Seckar, Trevor Squires]
-
- - All classes/modules which include Reloadable can define reloadable? for fine grained control of reloading
- - Class.remove_class uses Module#parent to access the parent module
- - Class.remove_class expanded to handle multiple classes in a single call
- - LoadingModule.clear! has been removed as it is no longer required
- - Module#remove_classes_including has been removed in favor of Reloadable.reloadable_classes
-
-* Added reusable reloading support through the inclusion of the Relodable module that all subclasses of ActiveRecord::Base, ActiveRecord::Observer, ActiveController::Base, and ActionMailer::Base automatically gets. This means that these classes will be reloaded by the dispatcher when Dependencies.mechanism = :load. You can make your own models reloadable easily:
-
- class Setting
- include Reloadable
- end
-
- Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [David Heinemeier Hansson]
-
-* Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb [Nicholas Seckar]
-
-* Add Object#instance_exec, like instance_eval but passes its arguments to the block. (Active Support will not override the Ruby 1.9 implementation of this method.) [Sam Stephenson]
-
-* Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. [Sam Stephenson]
-
-* Fix merge and dup for hashes with indifferent access #3404 [Ken Miller]
-
-* Fix the requires in option_merger_test to unbreak AS tests. [Sam Stephenson]
-
-* Make HashWithIndifferentAccess#update behave like Hash#update by returning the hash. #3419, #3425 [asnem@student.ethz.ch, JanPrill@blauton.de, Marcel Molina Jr.]
-
-* Add ActiveSupport::JSON and Object#to_json for converting Ruby objects to JSON strings. [Sam Stephenson]
-
-* Add Object#with_options for DRYing up multiple calls to methods having shared options. [Sam Stephenson] Example:
-
- ActionController::Routing::Routes.draw do |map|
- # Account routes
- map.with_options(:controller => 'account') do |account|
- account.home '', :action => 'dashboard'
- account.signup 'signup', :action => 'new'
- account.logout 'logout', :action => 'logout'
- end
- end
-
-* Introduce Dependencies.warnings_on_first_load setting. If true, enables warnings on first load of a require_dependency. Otherwise, loads without warnings. Disabled (set to false) by default. [Jeremy Kemper]
-
-* Active Support is warnings-safe. #1792 [Eric Hodel]
-
-* Introduce enable_warnings counterpart to silence_warnings. Turn warnings on when loading a file for the first time if Dependencies.mechanism == :load. Common mistakes such as redefined methods will print warnings to stderr. [Jeremy Kemper]
-
-* Add Symbol#to_proc, which allows for, e.g. [:foo, :bar].map(&:to_s). [Marcel Molina Jr.]
-
-* Added the following methods [Marcel Molina Jr., Sam Stephenson]:
- * Object#copy_instance_variables_from(object) to copy instance variables from one object to another
- * Object#extended_by to get an instance's included/extended modules
- * Object#extend_with_included_modules_from(object) to extend an instance with the modules from another instance
-
-*1.2.5* (December 13th, 2005)
-
-* Become part of Rails 1.0
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-*1.2.3* (November 7th, 2005)
-
-* Change Inflector#constantize to use eval instead of const_get. [Nicholas Seckar]
-
-* Fix const_missing handler to ignore the trailing '.rb' on files when comparing paths. [Nicholas Seckar]
-
-* Define kernel.rb methods in "class Object" instead of "module Kernel" to work around a Windows peculiarity [Sam Stephenson]
-
-* Fix broken tests caused by incomplete loading of active support. [Nicholas Seckar]
-
-* Fix status pluralization bug so status_codes doesn't get pluralized as statuses_code. #2758 [keithm@infused.org]
-
-* Added Kernel#silence_stderr to silence stderr for the duration of the given block [Sam Stephenson]
-
-* Changed Kernel#` to print a message to stderr (like Unix) instead of raising Errno::ENOENT on Win32 [Sam Stephenson]
-
-* Changed 0.blank? to false rather than true since it violates everyone's expectation of blankness. #2518, #2705 [rails@jeffcole.net]
-
-* When loading classes using const_missing, raise a NameError if and only if the file we tried to load was not present. [Nicholas Seckar]
-
-* Added petabytes and exebytes to numeric extensions #2397 [timct@mac.com]
-
-* Added Time#end_of_month to accompany Time#beginning_of_month #2514 [Jens-Christian Fischer]
-
-
-*1.2.2* (October 26th, 2005)
-
-* Set Logger.silencer = false to disable Logger#silence. Useful for debugging fixtures.
-
-* Add title case method to String to do, e.g., 'action_web_service'.titlecase # => 'Action Web Service'. [Marcel Molina Jr.]
-
-
-*1.2.1* (October 19th, 2005)
-
-* Classify generated routing code as framework code to avoid appearing in application traces. [Nicholas Seckar]
-
-* Show all framework frames in the framework trace. [Nicholas Seckar]
-
-
-*1.2.0* (October 16th, 2005)
-
-* Update Exception extension to show the first few framework frames in an application trace. [Nicholas Seckar]
-
-* Added Exception extension to provide support for clean backtraces. [Nicholas Seckar]
-
-* Updated whiny nil to be more concise and useful. [Nicholas Seckar]
-
-* Added Enumerable#first_match [Nicholas Seckar]
-
-* Fixed that Time#change should also reset usec when also resetting minutes #2459 [ikeda@dream.big.or.jp]
-
-* Fix Logger compatibility for distributions that don't keep Ruby and its standard library in sync.
-
-* Replace '%e' from long and short time formats as Windows does not support it. #2344. [Tom Ward <tom@popdog.net>]
-
-* Added to_s(:db) to Range, so you can get "BETWEEN '2005-12-10' AND '2005-12-12'" from Date.new(2005, 12, 10)..Date.new(2005, 12, 12) (and likewise with Times)
-
-* Moved require_library_or_gem into Kernel. #1992 [Michael Schuerig <michael@schuerig.de>]
-
-* Add :rfc822 as an option for Time#to_s (to get rfc822-formatted times)
-
-* Chain the const_missing hook to any previously existing hook so rails can play nicely with rake
-
-* Clean logger is compatible with both 1.8.2 and 1.8.3 Logger. #2263 [Michael Schuerig <michael@schuerig.de>]
-
-* Added native, faster implementations of .blank? for the core types #2286 [skae]
-
-* Fixed clean logger to work with Ruby 1.8.3 Logger class #2245
-
-* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 [Chris McGrath]
-
-* Fixed Inflector.underscore for use with acronyms, so HTML becomes html instead of htm_l #2173 [k@v2studio.com]
-
-* Fixed dependencies related infinite recursion bug when a controller file does not contain a controller class. Closes #1760. [rcolli2@tampabay.rr.com]
-
-* Fixed inflections for status, quiz, move #2056 [deirdre@deirdre.net]
-
-* Added Hash#reverse_merge, Hash#reverse_merge!, and Hash#reverse_update to ease the use of default options
-
-* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 [Manfred Stienstra]
-
-* Added Kernel#silence_warnings to turn off warnings temporarily for the passed block
-
-* Added String#starts_with? and String#ends_with? #2118 [Thijs van der Vossen]
-
-* Added easy extendability to the inflector through Inflector.inflections (using the Inflector::Inflections singleton class). Examples:
-
- Inflector.inflections do |inflect|
- inflect.plural /^(ox)$/i, '\1\2en'
- inflect.singular /^(ox)en/i, '\1'
-
- inflect.irregular 'octopus', 'octopi'
-
- inflect.uncountable "equipment"
- end
-
-* Added String#at, String#from, String#to, String#first, String#last in ActiveSupport::CoreExtensions::String::Access to ease access to individual characters and substrings in a string serving basically as human names for range access.
-
-* Make Time#last_month work when invoked on the 31st of a month.
-
-* Add Time.days_in_month, and make Time#next_month work when invoked on the 31st of a month
-
-* Fixed that Time#midnight would have a non-zero usec on some platforms #1836
-
-* Fixed inflections of "index/indices" #1766 [damn_pepe@gmail.com]
-
-* Added stripping of _id to String#humanize, so "employee_id" becomes "Employee" #1574 [Justin French]
-
-* Factor Fixnum and Bignum extensions into Integer extensions [Nicholas Seckar]
-
-* Hooked #ordinalize into Fixnum and Bignum classes. [Nicholas Seckar, danp]
-
-* Added Fixnum#ordinalize to turn 1.ordinalize to "1st", 3.ordinalize to "3rd", and 10.ordinalize to "10th" and so on #1724 [paul@cnt.org]
-
-
-*1.1.1* (11 July, 2005)
-
-* Added more efficient implementation of the development mode reset of classes #1638 [Chris McGrath]
-
-
-*1.1.0* (6 July, 2005)
-
-* Fixed conflict with Glue gem #1606 [Rick Olson]
-
-* Added new rules to the Inflector to deal with more unusual plurals mouse/louse => mice/lice, information => information, ox => oxen, virus => viri, archive => archives #1571, #1583, #1490, #1599, #1608 [foamdino@gmail.com/others]
-
-* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 [Chris McGrath]
-
-* Made 1.year == 365.25.days to account for leap years. This allows you to do User.find(:all, :conditions => ['birthday > ?', 50.years.ago]) without losing a lot of days. #1488 [tuxie@dekadance.se]
-
-* Added an exception if calling id on nil to WhinyNil #584 [kevin-temp@writesoon.com]
-
-* Added Fix/Bignum#multiple_of? which returns true on 14.multiple_of?(7) and false on 16.multiple_of?(7) #1464 [Thomas Fuchs]
-
-* Added even? and odd? to work with Bignums in addition to Fixnums #1464 [Thomas Fuchs]
-
-* Fixed Time#at_beginning_of_week returned the next Monday instead of the previous one when called on a Sunday #1403 [jean.helou@gmail.com]
-
-* Increased the speed of indifferent hash access by using Hash#default. #1436 [Nicholas Seckar]
-
-* Added that " " is now also blank? (using strip if available)
-
-* Fixed Dependencies so all modules are able to load missing constants #1173 [Nicholas Seckar]
-
-* Fixed the Inflector to underscore strings containing numbers, so Area51Controller becomes area51_controller #1176 [Nicholas Seckar]
-
-* Fixed that HashWithIndifferentAccess stringified all keys including symbols, ints, objects, and arrays #1162 [Nicholas Seckar]
-
-* Fixed Time#last_year to go back in time, not forward #1278 [fabien@odilat.com]
-
-* Fixed the pluralization of analysis to analyses #1295 [seattle@rootimage.msu.edu]
-
-* Fixed that Time.local(2005,12).months_since(1) would raise "ArgumentError: argument out of range" #1311 [jhahn@niveon.com]
-
-* Added silencing to the default Logger class
-
-
-*1.0.4* (19th April, 2005)
-
-* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 [Nicholas Seckar]
-
-* Fixed inflection of perspectives and similar words #1045 [Thijs van der Vossen]
-
-* Added Fixnum#even? and Fixnum#odd?
-
-* Fixed problem with classes being required twice. Object#const_missing now uses require_dependency to load files. It used to use require_or_load which would cause models to be loaded twice, which was not good for validations and other class methods #971 [Nicholas Seckar]
-
-
-*1.0.3* (27th March, 2005)
-
-* Fixed Inflector.pluralize to handle capitalized words #932 [Jeremy Kemper]
-
-* Added Object#suppress which allows you to make a saner choice around with exceptions to swallow #980. Example:
-
- suppress(ZeroDivisionError) { 1/0 }
-
- ...instead of:
-
- 1/0 rescue nil # BAD, EVIL, DIRTY.
-
-
-*1.0.2* (22th March, 2005)
-
-* Added Kernel#returning -- a Ruby-ized realization of the K combinator, courtesy of Mikael Brockman.
-
- def foo
- returning values = [] do
- values << 'bar'
- values << 'baz'
- end
- end
-
- foo # => ['bar', 'baz']
-
-
-*1.0.1* (7th March, 2005)
-
-* Fixed Hash#indifferent_access to also deal with include? and fetch and nested hashes #726 [Nicholas Seckar]
-
-* Added Object#blank? -- see http://redhanded.hobix.com/inspect/objectBlank.html #783 [_why the lucky stiff]
-
-* Added inflection rules for "sh" words, like "wish" and "fish" #755 [phillip@pjbsoftware.com]
-
-* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 [Tobias Lütke]
-
-* Added String#to_time and String#to_date for wrapping ParseDate
-
-
-*1.0.0* (24th February, 2005)
-
-* Added TimeZone as the first of a number of value objects that among others Active Record can use rich value objects using composed_of #688 [Jamis Buck]
-
-* Added Date::Conversions for getting dates in different convenient string representations and other objects
-
-* Added Time::Conversions for getting times in different convenient string representations and other objects
-
-* Added Time::Calculations to ask for things like Time.now.tomorrow, Time.now.yesterday, Time.now.months_ago(4) #580 [DP|Flurin]. Examples:
-
- "Later today" => now.in(3.hours),
- "Tomorrow morning" => now.tomorrow.change(:hour => 9),
- "Tomorrow afternoon" => now.tomorrow.change(:hour => 14),
- "In a couple of days" => now.tomorrow.tomorrow.change(:hour => 9),
- "Next monday" => now.next_week.change(:hour => 9),
- "In a month" => now.next_month.change(:hour => 9),
- "In 6 months" => now.months_since(6).change(:hour => 9),
- "In a year" => now.in(1.year).change(:hour => 9)
-
-* Upgraded to breakpoint 92 which fixes:
-
- * overload IRB.parse_opts(), fixes #443
- => breakpoints in tests work even when running them via rake
- * untaint handlers, might fix an issue discussed on the Rails ML
- * added verbose mode to breakpoint_client
- * less noise caused by breakpoint_client by default
- * ignored TerminateLineInput exception in signal handler
- => quiet exit on Ctrl-C
-
-* Fixed Inflector for words like "news" and "series" that are the same in plural and singular #603 [echion], #615 [marcenuc]
-
-* Added Hash#stringify_keys and Hash#stringify_keys!
-
-* Added IndifferentAccess as a way to wrap a hash by a symbol-based store that also can be accessed by string keys
-
-* Added Inflector.constantize to turn "Admin::User" into a reference for the constant Admin::User
-
-* Added that Inflector.camelize and Inflector.underscore can deal with modules like turning "Admin::User" into "admin/user" and back
-
-* Added Inflector.humanize to turn attribute names like employee_salary into "Employee salary". Used by automated error reporting in AR.
-
-* Added availability of class inheritable attributes to the masses #477 [Jeremy Kemper]
-
- class Foo
- class_inheritable_reader :read_me
- class_inheritable_writer :write_me
- class_inheritable_accessor :read_and_write_me
- class_inheritable_array :read_and_concat_me
- class_inheritable_hash :read_and_update_me
- end
-
- # Bar gets a clone of (not a reference to) Foo's attributes.
- class Bar < Foo
- end
-
- Bar.read_and_write_me == Foo.read_and_write_me
- Bar.read_and_write_me = 'bar'
- Bar.read_and_write_me != Foo.read_and_write_me
-
-* Added Inflections as an extension on String, so Inflector.pluralize(Inflector.classify(name)) becomes name.classify.pluralize #476 [Jeremy Kemper]
-
-* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina Jr.]
-
-* Fixed that Dependencies.reload can't load the same file twice #420 [Kent Sibilev]
-
-* Added Fixnum#ago/until, Fixnum#since/from_now #450 [Jeremy Kemper]
-
-* Added that Inflector now accepts Symbols and Classes by calling .to_s on the word supplied
-
-* Added time unit extensions to Fixnum that'll return the period in seconds, like 2.days + 4.hours.
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
new file mode 100644
index 0000000000..29109ea64d
--- /dev/null
+++ b/activesupport/CHANGELOG.md
@@ -0,0 +1,1631 @@
+## Rails 4.0.0 (unreleased) ##
+
+* Adds Integer#ordinal to get the ordinal suffix string of an integer. *Tim Gildea*
+
+* AS::Callbacks: `:per_key` option is no longer supported
+
+* `AS::Callbacks#define_callbacks`: add `:skip_after_callbacks_if_terminated` option.
+
+* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it. *Carlos Antonio da Silva*
+
+* Remove ActiveSupport::TestCase#pending method, use `skip` instead. *Carlos Antonio da Silva*
+
+* Deprecates the compatibility method Module#local_constant_names,
+ use Module#local_constants instead (which returns symbols). *fxn*
+
+* Deletes the compatibility method Module#method_names,
+ use Module#methods from now on (which returns symbols). *fxn*
+
+* Deletes the compatibility method Module#instance_method_names,
+ use Module#instance_methods from now on (which returns symbols). *fxn*
+
+* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger
+ from Ruby stdlib.
+
+* Unicode database updated to 6.1.0.
+
+## Rails 3.2.0 (January 20, 2012) ##
+
+* Add ActiveSupport::Cache::NullStore for use in development and testing. *Brian Durand*
+
+* Module#synchronize is deprecated with no replacement. Please use `monitor`
+ from ruby's standard library.
+
+* (Date|DateTime|Time)#beginning_of_week accept an optional argument to
+ be able to set the day at which weeks are assumed to start.
+
+* Deprecated ActiveSupport::MessageEncryptor#encrypt and decrypt. *José Valim*
+
+* ActiveSupport::Notifications.subscribed provides subscriptions to events while a block runs. *fxn*
+
+* Module#qualified_const_(defined?|get|set) are analogous to the corresponding methods
+ in the standard API, but accept qualified constant names. *fxn*
+
+* Added inflection #deconstantize which complements #demodulize. This inflection
+ removes the righmost segment in a qualified constant name. *fxn*
+
+* Added ActiveSupport:TaggedLogging that can wrap any standard Logger class to provide tagging capabilities *DHH*
+
+ Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
+ Logger.tagged("BCX") { Logger.info "Stuff" } # Logs "[BCX] Stuff"
+ Logger.tagged("BCX", "Jason") { Logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff"
+ Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff"
+
+* Added safe_constantize that constantizes a string but returns nil instead of an exception if the constant (or part of it) does not exist *Ryan Oblak*
+
+* ActiveSupport::OrderedHash is now marked as extractable when using Array#extract_options! *Prem Sichanugrist*
+
+* Added Array#prepend as an alias for Array#unshift and Array#append as an alias for Array#<< *DHH*
+
+* The definition of blank string for Ruby 1.9 has been extended to Unicode whitespace.
+ Also, in 1.8 the ideographic space U+3000 is considered to be whitespace. *Akira Matsuda, Damien Mathieu*
+
+* The inflector understands acronyms. *dlee*
+
+* Deprecated ActiveSupport::Memoizable in favor of Ruby memoization pattern *José Valim*
+
+* Added Time#all_day/week/quarter/year as a way of generating ranges (example: Event.where(created_at: Time.now.all_week)) *DHH*
+
+* Added instance_accessor: false as an option to Class#cattr_accessor and friends *DHH*
+
+* Removed ActiveSupport::SecureRandom in favor of SecureRandom from the standard library *Jon Leighton*
+
+* ActiveSupport::OrderedHash now has different behavior for #each and
+ \#each_pair when given a block accepting its parameters with a splat. *Andrew Radev*
+
+* ActiveSupport::BufferedLogger#silence is deprecated. If you want to squelch
+ logs for a certain block, change the log level for that block.
+
+* ActiveSupport::BufferedLogger#open_log is deprecated. This method should
+ not have been public in the first place.
+
+* ActiveSupport::BufferedLogger's behavior of automatically creating the
+ directory for your log file is deprecated. Please make sure to create the
+ directory for your log file before instantiating.
+
+* ActiveSupport::BufferedLogger#auto_flushing is deprecated. Either set the
+ sync level on the underlying file handle like this:
+
+ f = File.open('foo.log', 'w')
+ f.sync = true
+ ActiveSupport::BufferedLogger.new f
+
+ Or tune your filesystem. The FS cache is now what controls flushing.
+
+* ActiveSupport::BufferedLogger#flush is deprecated. Set sync on your
+ filehandle, or tune your filesystem.
+
+## Rails 3.1.0 (August 30, 2011) ##
+
+* ActiveSupport::Dependencies#load and ActiveSupport::Dependencies#require now
+ return the value from `super` *Aaron Patterson*
+
+* Fixed ActiveSupport::Gzip to work properly in Ruby 1.8 *Guillermo Iguaran*
+
+* Kernel.require_library_or_gem was deprecated and will be removed in Rails 3.2.0 *Josh Kalderimis*
+
+* ActiveSupport::Duration#duplicable? was fixed for Ruby 1.8 *thedarkone*
+
+* ActiveSupport::BufferedLogger set log encoding to BINARY, but still use text
+ mode to output portable newlines. *fxn*
+
+* ActiveSupport::Dependencies now raises NameError if it finds an existing constant in load_missing_constant. This better reflects the nature of the error which is usually caused by calling constantize on a nested constant. *Andrew White*
+
+* Deprecated ActiveSupport::SecureRandom in favour of SecureRandom from the standard library *Jon Leighton*
+
+* New reporting method Kernel#quietly. *fxn*
+
+* Add String#inquiry as a convenience method for turning a string into a StringInquirer object *DHH*
+
+* Add Object#in? to test if an object is included in another object *Prem Sichanugrist, Brian Morearty, John Reitano*
+
+* LocalCache strategy is now a real middleware class, not an anonymous class
+ posing for pictures.
+
+* ActiveSupport::Dependencies::ClassCache class has been introduced for
+ holding references to reloadable classes.
+
+* ActiveSupport::Dependencies::Reference has been refactored to take direct
+ advantage of the new ClassCache.
+
+* Backports Range#cover? as an alias for Range#include? in Ruby 1.8 *Diego Carrion, fxn*
+
+* Added weeks_ago and prev_week to Date/DateTime/Time. *Rob Zolkos, fxn*
+
+* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! *Andrew White*
+
+* JSON decoding now uses the multi_json gem which also vendors a json engine called OkJson. The yaml backend has been removed in favor of OkJson as a default engine for 1.8.x, while the built in 1.9.x json implementation will be used by default. *Josh Kalderimis*
+
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* Hash.from_xml no longer loses attributes on tags containing only whitespace *André Arko*
+
+
+* Rails 3.0.6 (April 5, 2011)
+
+* No changes.
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* No changes.
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! *Andrew White*
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* No Changes, just a version bump.
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* Implemented String#strip_heredoc. *fxn*
+
+* Pluggable cache stores: setting config.cache_store = "custom_store" will require 'active_support/cache/custom_store' and look for the CustomStore constant. #5486 *Mike Perham*
+
+* Removed Object#returning, Object#tap should be used instead. *Santiago Pastorino*
+
+* Deprecation behavior is no longer hardcoded to the name of the environment.
+ Instead, it is set via config.active_support.deprecation and can be one
+ of :log, :stderr or :notify. :notify is a new style that sends the warning
+ via ActiveSupport::Notifications, and is the new default for production
+ *Yehuda Katz*
+
+* Renamed ActiveSupport::Dependecies.load_(once_)paths to autoload_(once_)paths. *fxn*
+
+* Added ActiveSupport::FileUpdateChecker to execute a block only if a set of files changed, used by Router and I18n locale files. *José Valim*
+
+* Added ActiveSupport::DescendantsTracker to track descendants with support to constants reloading. *José Valim*
+
+* ActiveSupport::OrderedHash#merge and #merge! accept a block. #4838 *Paul Mucur, fxn*
+
+* Date#since, #ago, #beginning_of_day, #end_of_day, and #xmlschema honor now the user time zone if set. *Geoff Buesing*
+
+* Extracted String#truncate from TextHelper#truncate *DHH*
+
+* Ruby 1.9: support UTF-8 case folding. #4595 *Norman Clarke*
+
+* Removes Array#rand and backports Array#sample from Ruby 1.9, thanks to Marc-Andre Lafortune. *fxn*
+
+* Ruby 1.9: Renames last_(month|year) to prev_(month|year) in Date and Time. *fxn*
+
+* Aliases Date#sunday to Date#end_of_week. *fxn*
+
+* Backports Date#>> from 1.9 so that calculations do the right thing around the calendar reform. *fxn*
+
+* Date#to_time handles properly years in the range 0..138. *fxn*
+
+* Deprecate {{}} as interpolation syntax for I18n in favor of %{} *José Valim*
+
+* Array#to_xml is more powerful and able to handle the same types as Hash#to_xml #4490 *Neeraj Singh*
+
+* Harmonize the caching API and refactor the backends. #4452 *Brian Durand*
+ All caches:
+ * Add default options to initializer that will be sent to all read, write, fetch, exist?, increment, and decrement
+ * Add support for the :expires_in option to fetch and write for all caches. Cache entries are stored with the create timestamp and a ttl so that expiration can be handled independently of the implementation.
+ * Add support for a :namespace option. This can be used to set a global prefix for cache entries.
+ * Deprecate expand_cache_key on ActiveSupport::Cache and move it to ActionController::Caching and ActionDispatch::Http::Cache since the logic in the method used some Rails specific environment variables and was only used by ActionPack classes. Not very DRY but there didn't seem to be a good shared spot and ActiveSupport really shouldn't be Rails specific.
+ * Add support for :race_condition_ttl to fetch. This setting can prevent race conditions on fetch calls where several processes try to regenerate a recently expired entry at once.
+ * Add support for :compress option to fetch and write which will compress any data over a configurable threshold.
+ * Nil values can now be stored in the cache and are distinct from cache misses for fetch.
+ * Easier API to create new implementations. Just need to implement the methods read_entry, write_entry, and delete_entry instead of overwriting existing methods.
+ * Since all cache implementations support storing objects, update the docs to state that ActiveCache::Cache::Store implementations should store objects. Keys, however, must be strings since some implementations require that.
+ * Increase test coverage.
+ * Document methods which are provided as convenience but which may not be universally available.
+
+ MemoryStore:
+ * MemoryStore can now safely be used as the cache for single server sites.
+ * Make thread safe so that the default cache implementation used by Rails is thread safe. The overhead is minimal and it is still the fastest store available.
+ * Provide :size initialization option indicating the maximum size of the cache in memory (defaults to 32Mb).
+ * Add prune logic that removes the least recently used cache entries to keep the cache size from exceeding the max.
+ * Deprecated SynchronizedMemoryStore since it isn't needed anymore.
+
+ FileStore:
+ * Escape key values so they will work as file names on all file systems, be consistent, and case sensitive
+ * Use a hash algorithm to segment the cache into sub directories so that a large cache doesn't exceed file system limits.
+ * FileStore can be slow so implement the LocalCache strategy to cache reads for the duration of a request.
+ * Add cleanup method to keep the disk from filling up with expired entries.
+ * Fix increment and decrement to use file system locks so they are consistent between processes.
+
+ MemCacheStore:
+ * Support all keys. Previously keys with spaces in them would fail
+ * Deprecate CompressedMemCacheStore since it isn't needed anymore (use :compress => true)
+
+* JSON: encode objects that don't have a native JSON representation using to_hash, if available, instead of instance_values (the old fallback) or to_s (other encoders' default). Encode BigDecimal and Regexp encode as strings to conform with other encoders. Try to transcode non-UTF-8 strings. *Jeremy Kemper*
+
+* HashWithIndifferentAccess: remove inherited symbolize_keys! since its keys are always strings. *Santiago Pastorino*
+
+* Improve transliteration quality. #4374 *Norman Clarke*
+
+* Speed up and add Ruby 1.9 support for ActiveSupport::Multibyte::Chars#tidy_bytes. #4350 *Norman Clarke*
+
+* Reduced load time by deferring configuration of classes using
+ ActiveSupport::on_load(:component_name) *YK*
+
+* Rename #metaclass to #singleton_class now that ruby-core has decided *JK*
+
+* New assertions assert_blank and assert_present. #4299 *Juanjo Bazan*
+
+* Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. *Jeremy Kemper*
+
+* JSON backend for YAJL. Preferred if available. #2666 *Brian Lopez*
+
+* Introduce class_attribute to declare inheritable class attributes. Writing an attribute on a subclass behaves just like overriding the superclass reader method. Unifies and replaces most usage of cattr_accessor, class_inheritable_attribute, superclass_delegating_attribute, and extlib_inheritable_attribute. *Jeremy Kemper, Yehuda Katz*
+
+* Time#- with a DateTime argument behaves the same as with a Time argument, i.e. returns the difference between self and arg as a Float #3476 *Geoff Buesing*
+
+* YAML serialization for OrderedHash. #3608 *Gregor Schmidt*
+
+* Update bundled TZInfo to v0.3.16 *Geoff Buesing*
+
+* Georgetown TimeZone is now mapped to "America/Guyana" instead of "America/Argentina/San_Juan" #1821 *Geoff Buesing, Reuben Sivan*
+
+* Changed the default ActiveSupport.use_standard_json_time_format from false to true and
+ ActiveSupport.escape_html_entities_in_json from true to false to match previously announced Rails 3 defaults *DHH*
+
+* Added Object#presence that returns the object if it's #present? otherwise returns nil *DHH/Colin Kelley*
+
+* Add Enumerable#exclude? to bring parity to Enumerable#include? and avoid if !x.include?/else calls *DHH*
+
+* Update Edinburgh TimeZone to use "Europe/London" instead of "Europe/Dublin" #3310 *Phil Ross*
+
+* Update bundled TZInfo to v0.3.15 *Geoff Buesing*
+
+* JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON library agnostic. *Jeremy Kemper*
+
+* String #to_time and #to_datetime: handle fractional seconds #864 *Jason Frey*
+
+* Update bundled TZInfo to v0.3.13 *Geoff Buesing*
+
+* 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*
+
+* Time.local instances: Adding 24.hours across the DST boundary adds 24 hours instead of one day #2066 *Michael Curtis*
+
+
+## 2.3.2 Final (March 15, 2009) ##
+
+* XmlMini supports LibXML and Nokogiri backends. #2084, #2190 *Bart ten Brinke, Aaron Patterson*
+ Example: XmlMini.backend = 'Nokogiri'
+
+* Vendorize i18n 0.1.3 gem (fixes issues with incompatible character encodings in Ruby 1.9) #2038 *Akira Matsuda*
+
+* Update bundled memcache-client from 1.5.0.5 to 1.6.4.99. See http://www.mikeperham.com/2009/02/15/memcache-client-performance/ *Mike Perham*
+
+* Ruby 1.9.1p0 fix: URI.unescape can decode multibyte chars. #2033 *MOROHASHI Kyosuke*
+
+* Time#to_s(:rfc822) uses #formatted_offset instead of unreliable and non-standard %z directive #1899 *Zachary Zolton*
+
+* Make TimeWithZone#to_formatted_s an alias to TimeWithZone#to_s #1796 *Levin Alexander*
+
+* Introduce Array.wrap(foo) to wrap the argument in an array unless it's already an array. Wraps nil as an empty array. Use instead of Array(foo) and foo.to_a since they treat String as Enumerable. *Jeremy Kemper*
+
+* TimeWithZone#xmlschema accepts optional fraction_digits argument [#1725 state:resolved] *Nicholas Dainty*
+
+* Object#tap shim for Ruby < 1.8.7. Similar to Object#returning, tap yields self then returns self. *Jeremy Kemper*
+ array.select { ... }.tap(&:inspect).map { ... }
+
+* TimeWithZone#- gives correct result with wrapped DateTime, and with DateTime argument *Geoff Buesing*
+
+* Updated i18n gem to version 0.1.1 #1635 *Yaroslav Markin*
+
+* Add :allow_nil option to delegate. #1127 *Sergio Gil*
+
+* Add Benchmark.ms convenience method to benchmark realtime in milliseconds. *Jeremy Kemper*
+
+* Updated included memcache-client to the 1.5.0.5 version which includes fixes from fiveruns and 37signals to deal with failover and timeouts #1535 *Joshua Sierles*
+
+* Multibyte: add multibyte-safe Chars#ord rather than falling back to String#ord. #1483 *Jason Cheow*
+
+* I18n support for Array#to_sentence. Introduces support.array.words_connector, .two_words_connector, and .last_word_connector translation keys. #1397 *Akira Matsuda*
+
+* Added ActiveSupport::OrderedHash#each_key and ActiveSupport::OrderedHash#each_value #1410 *Christoffer Sawicki*
+
+* Added ActiveSupport::MessageVerifier and MessageEncryptor to aid users who need to store signed and/or encrypted messages. *Michael Koziarski*
+
+* Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers *David Heinemeier Hansson*
+
+* Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) *Chris Wanstrath*
+
+* Added Enumerable#none? to check that none of the elements match the block #1408 *Damian Janowski*
+
+* TimeZone offset tests: use current_period, to ensure TimeZone#utc_offset is up-to-date *Geoff Buesing*
+
+* Update bundled TZInfo to 0.3.12 *Geoff Buesing*
+
+* Added lambda merging to OptionMerger (especially useful with named_scope and with_options) #726 *Paweł Kondzior*
+
+
+## 2.2.1 RC2 (November 14th, 2008) ##
+
+* Increment the version of our altered memcache-client to prevent confusion caused when the 1.5.0 gem is installed.
+
+* Fixed the option merging in Array#to_xml #1126 *Rudolf Gavlas*
+
+* Make I18n::Backend::Simple reload its translations in development mode *David Heinemeier Hansson/Sven Fuchs*
+
+
+## 2.2.0 RC1 (October 24th, 2008) ##
+
+* TimeWithZone#freeze: preload instance variables so that we can actually freeze *Geoff Buesing*
+
+* Fix Brasilia timezone #1180 *Marcus Derencius, Kane*
+
+* Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years #970 *Tom Lea*
+
+* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. *Norbert Crombach, Pratik Naik*
+
+* Switch from String#chars to String#mb_chars for the unicode proxy. *Manfred Stienstra*
+
+ This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.
+
+* TimeWithZone #wday, #yday and #to_date avoid trip through #method_missing *Geoff Buesing*
+
+* Added Time, Date, DateTime and TimeWithZone #past?, #future? and #today? #720 *Clemens Kofler, Geoff Buesing*
+
+* Fixed Sri Jayawardenepura time zone to map to Asia/Colombo *Jamis Buck*
+
+* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 *Matt Darby*
+
+* Changed cache benchmarking to be reported in milliseconds *David Heinemeier Hansson*
+
+* Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] *Luca Guidi, Geoff Buesing*
+
+* Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example:
+
+ a = (1..10).to_a
+ a.in_groups(3) # => [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]]
+ a.in_groups(3, false) # => [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
+
+* Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone *Geoff Buesing*
+
+* Added Memoizable mixin for caching simple lazy loaded attributes *Josh Peek*
+
+* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. *Michael Koziarski*
+
+* Add Inflection rules for String#humanize. #535 *Dan Manges*
+
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.human(/_cnt$/i, '\1_count')
+ end
+
+ 'jargon_cnt'.humanize # => 'Jargon count'
+
+* TimeWithZone: when crossing DST boundary, treat Durations of days, months or years as variable-length, and all other values as absolute length. A time + 24.hours will advance exactly 24 hours, but a time + 1.day will advance 23-25 hours, depending on the day. Ensure consistent behavior across all advancing methods *Geoff Buesing*
+
+* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 *Ernie Miller*
+
+* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] *David Heinemeier Hansson*
+
+* Added test/do declaration style testing to ActiveSupport::TestCase *DHH via Jay Fields*
+
+* Added Object#present? which is equivalent to !Object#blank? *David Heinemeier Hansson*
+
+* Added Enumberable#many? to encapsulate collection.size > 1 *David Heinemeier Hansson/Damian Janowski*
+
+* Add more standard Hash methods to ActiveSupport::OrderedHash *Steve Purcell*
+
+* Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport *Josh Peek*
+
+* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false *David Heinemeier Hansson*
+
+* Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger)
+
+
+## 2.1.0 (May 31st, 2008) ##
+
+* TimeZone#to_s shows offset as GMT instead of UTC, because GMT will be more familiar to end users (see time zone selects used by Windows OS, google.com and yahoo.com.) Reverts [8370] *Geoff Buesing*
+
+* Hash.from_xml: datetime xml types overflow to Ruby DateTime class when out of range of Time. Adding tests for utc offsets *Geoff Buesing*
+
+* TimeWithZone #+ and #- : ensure overflow to DateTime with Numeric arg *Geoff Buesing*
+
+* Time#to_json: don't convert to utc before encoding. References #175 *Geoff Buesing*
+
+* Remove unused JSON::RESERVED_WORDS, JSON.valid_identifier? and JSON.reserved_word? methods. Resolves #164. *Cheah Chu Yeow*
+
+* Adding Date.current, which returns Time.zone.today if config.time_zone is set; otherwise returns Date.today *Geoff Buesing*
+
+* TimeWithZone: date part getter methods (#year #mon #day etc) are defined on class; no longer relying on method_missing *Geoff Buesing*
+
+* Time.zone.parse return nil for strings with no date information *Geoff Buesing*
+
+* Time.zone.parse respects offset information in string. Resolves #105. *Scott Fleckenstein, Geoff Buesing*
+
+* Added Ruby 1.8 implementation of Process.daemon
+
+* Duration #since and #ago with no argument (e.g., 5.days.ago) return TimeWithZone when config.time_zone is set. Introducing Time.current, which returns Time.zone.now if config.time_zone is set, otherwise just returns Time.now *Geoff Buesing*
+
+* Time#since behaves correctly when passed a Duration. Closes #11527 *kemiller*
+
+* Add #getutc alias for DateTime#utc *Geoff Buesing*
+
+* Refactor TimeWithZone: don't send #since, #ago, #+, #-, #advance through method_missing *Geoff Buesing*
+
+* TimeWithZone respects config.active_support.use_standard_json_time_format *Geoff Buesing*
+
+* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping. *Rick Olson*
+
+* Improve documentation. *Xavier Noria*
+
+* Modified ActiveSupport::Callbacks::Callback#call to accept multiple arguments.
+
+* Time #yesterday and #tomorrow behave correctly crossing DST boundary. Closes #7399 *sblackstone*
+
+* TimeWithZone: Adding tests for dst and leap day edge cases when advancing time *Geoff Buesing*
+
+* TimeWithZone#method_missing: send to utc to advance with dst correctness, otherwise send to time. Adding tests for time calculations methods *Geoff Buesing*
+
+* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates. *Rick Olson*
+
+* TZInfo: Removing unneeded TimezoneProxy class *Geoff Buesing*
+
+* TZInfo: Removing unneeded TimezoneIndexDefinition, since we're not including Indexes::Timezones *Geoff Buesing*
+
+* Removing unnecessary uses_tzinfo helper from tests, given that TZInfo is now bundled *Geoff Buesing*
+
+* Bundling abbreviated version of TZInfo gem 0.3.8: only the classes and zone definitions required to support Rails time zone features are included. If a recent version of the full TZInfo gem is installed, this will take precedence over the bundled version *Geoff Buesing*
+
+* TimeWithZone#marshal_load does zone lookup via Time.get_zone, so that tzinfo/Olson identifiers are handled *Geoff Buesing*
+
+* Time.zone= accepts TZInfo::Timezone instances and Olson identifiers; wraps result in TimeZone instance *Geoff Buesing*
+
+* TimeWithZone time conversions don't need to be wrapped in TimeOrDateTime, because TZInfo does this internally *Geoff Buesing*
+
+* TimeWithZone#usec returns 0 instead of error when DateTime is wrapped *Geoff Buesing*
+
+* Improve documentation. *Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert*
+
+* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter. *Rick Olson*
+
+* Ensure correct TimeWithZone#to_date *Geoff Buesing*
+
+* Make TimeWithZone work with tzinfo 0.2.x: use TZInfo::Timezone#zone_identifier alias for #abbreviation, silence warnings on tests. Raise LoadError when TZInfo version is < 0.2 by sniffing for TZInfo::TimeOrDateTime constant. Move all tzinfo-dependent TimeZone tests into uses_tzinfo block *Geoff Buesing*
+
+* Time, DateTime and TimeWithZone #in_time_zone defaults to Time.zone. Removing now unneeded #in_current_time_zone *Geoff Buesing*
+
+* TZInfo caches Timezone instances in its own internal hash cache, so TimeZone::MAPPING doesn't need to cache them as well *Geoff Buesing*
+
+* Adding TimeZone#parse *Geoff Buesing*
+
+* Adding TimeZone#at and DateTime#to_f *Geoff Buesing*
+
+* TimeWithZone responds to Ruby 1.9 weekday-named query methods *Geoff Buesing*
+
+* TimeWithZone caches TZInfo::TimezonePeriod used for time conversion so that it can be reused, and enforces DST rules correctly when instance is created from a local time *Geoff Buesing*
+
+* Fixed that BufferedLogger should create its own directory if one doesn't already exist #11285 *lotswholetime*
+
+* Fix Numeric time tests broken by DST change by anchoring them to fixed times instead of Time.now. Anchor TimeZone#now DST test to time specified with Time.at instead of Time.local to work around platform differences with Time.local and DST representation *Geoff Buesing*
+
+* Removing unneeded #change_time_zone method from Time, DateTime and TimeWithZone *Geoff Buesing*
+
+* TimeZone #local and #now correctly enforce DST rules *Geoff Buesing*
+
+* TimeWithZone instances correctly enforce DST rules. Adding TimeZone#period_for_utc *Geoff Buesing*
+
+* test_time_with_datetime_fallback expects DateTime.local_offset instead of DateTime.now.offset *Geoff Buesing*
+
+* Adding TimeWithZone #marshal_dump and #marshal_load *Geoff Buesing*
+
+* Add OrderedHash#to_hash *Josh Peek*
+
+* Adding Time#end_of_day, _quarter, _week, and _year. #9312 *Juanjo Bazan, Tarmo Tänav, BigTitus*
+
+* Adding TimeWithZone#between? *Geoff Buesing*
+
+* Time.=== returns true for TimeWithZone instances *Geoff Buesing*
+
+* TimeWithZone #+ and #- behave consistently with numeric arguments regardless of whether wrapped time is a Time or DateTime; consistenty answers false to #acts_like?(:date) *Geoff Buesing*
+
+* Add String#squish and String#squish! to remove consecutive chunks of whitespace. #11123 *Jordi Bunster, Henrik N*
+
+* Serialize BigDecimals as Floats when using to_yaml. #8746 *Ernesto Jimenez*
+
+* Adding TimeWithZone #to_yaml, #to_datetime, #eql? and method aliases for duck-typing compatibility with Time *Geoff Buesing*
+
+* TimeWithZone #in_time_zone returns +self+ if zone argument is the same as #time_zone *Geoff Buesing*
+
+* Adding TimeWithZone #to_a, #to_f, #to_i, #httpdate, #rfc2822 *Geoff Buesing*
+
+* Pruning unneeded TimeWithZone#change_time_zone_to_current *Geoff Buesing*
+
+* Time#zone=, #in_time_zone and #change_time_zone accept a Duration *Geoff Buesing*
+
+* Time#in_time_zone handles Time.local instances correctly *Geoff Buesing*
+
+* Pruning unneeded Time#change_time_zone_to_current. Enhanced docs to #change_time_zone to explain the difference between this method and #in_time_zone *Geoff Buesing*
+
+* TimeZone#new method renamed #local; when used with Time.zone, constructor now reads: Time.zone.local() *Geoff Buesing*
+
+* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing *David Heinemeier Hansson*
+
+* Remove :nodoc: entries around the ActiveSupport test/unit assertions. #10946 *dancroak, jamesh*
+
+* Add Time.zone_default accessor for setting the default time zone. Rails::Configuration.time_zone sets this. #10982 *Geoff Buesing*
+
+* cache.fetch(key, :force => true) to force a cache miss. *Jeremy Kemper*
+
+* Support retrieving TimeZones with a Duration. TimeZone[-28800] == TimeZone[-480.minutes]. *Rick Olson*
+
+* TimeWithZone#- added, so that #- can handle a Time or TimeWithZone argument correctly *Geoff Buesing*
+
+* with_timezone test helper renamed with_env_tz, to distinguish between setting ENV['TZ'] and setting Time.zone in tests *Geoff Buesing*
+
+* Time#- coerces TimeWithZone argument to a Time instance so that difference in seconds can be calculated. Closes #10914 *Geoff Buesing, yyyc514*
+
+* Adding UTC zone to TimeZone; TimeWithZone no longer has to fake UTC zone with nil *Geoff Buesing*
+
+* Time.get_zone refactored to private method, given that the encapsulated logic is only useful internally *Geoff Buesing*
+
+* Time.zone uses thread-local variable for thread safety. Adding Time.use_zone, for overriding Time.zone locally inside a block. Removing unneeded Time.zone_reset! *Geoff Buesing*
+
+* TimeZone#to_s uses UTC rather than GMT; reapplying change that was undone in [8679]. #1689 *Cheah Chu Yeow*
+
+* Time.days_in_month defaults to current year if no year is supplied as argument #10799 [Radar], uses Date.gregorian_leap? to determine leap year, and uses constant lookup to determine days in month *Geoff Buesing*
+
+* Adding Time and DateTime #compare_with_coercion, which layers behavior on #<=> so that any combination of Time, DateTime and ActiveSupport::TimeWithZone instances can be chronologically compared *Geoff Buesing*
+
+* TimeZone#now returns an ActiveSupport::TimeWithZone *Geoff Buesing*
+
+* Time #in_current_time_zone and #change_time_zone_to_current return self when Time.zone is nil *Geoff Buesing*
+
+* Remove unneeded #to_datetime_default_s alias for DateTime#to_s, given that we inherit a #to_default_s from Date that does exactly the same thing *Geoff Buesing*
+
+* Refactor Time and DateTime #to_formatted_s: use ternary instead of nested if/else *Geoff Buesing*
+
+* Adding Time and DateTime #formatted_offset, for outputting +HH:MM utc offset strings with cross-platform consistency *Geoff Buesing*
+
+* Adding alternate_utc_string option to TimeZone#formatted_offset. Removing unneeded TimeZone#offset. *Geoff Buesing*
+
+* Introduce ActiveSupport::TimeWithZone, for wrapping Time instances with a TimeZone. Introduce instance methods to Time for creating TimeWithZone instances, and class methods for managing a global time zone. *Geoff Buesing*
+
+* Replace non-dst-aware TimeZone class with dst-aware class from tzinfo_timezone plugin. TimeZone#adjust and #unadjust are no longer available; tzinfo gem must now be present in order to perform time zone calculations, via #local_to_utc and #utc_to_local methods. *Geoff Buesing*
+
+* Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher. #10727 *Josh Peek*
+
+* Introducing DateTime #utc, #utc? and #utc_offset, for duck-typing compatibility with Time. Closes #10002 *Geoff Buesing*
+
+* Time#to_json uses Numeric#to_utc_offset_s to output a cross-platform-consistent representation without having to convert to DateTime. References #9750 *Geoff Buesing*
+
+* Refactor number-to-HH:MM-string conversion logic from TimeZone#formatted_offset to a reusable Numeric#to_utc_offset_s method. *Geoff Buesing*
+
+* Continue evolution toward ActiveSupport::TestCase. #10679 *Josh Peek*
+
+* TestCase: introduce declared setup and teardown callbacks. Pass a list of methods and an optional block to call before setup or after teardown. Setup callbacks are run in the order declared; teardown callbacks are run in reverse. *Jeremy Kemper*
+
+* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib *Tobias Lütke*
+
+* Included MemCache-Client to make the improved ActiveSupport::Cache::MemCacheStore work out of the box *Bob Cottrell, Eric Hodel*
+
+## Added ActiveSupport::Cache:: framework as an extraction from ActionController::Caching::Fragments:: David Heinemeier Hansson ##
+
+* Fixed String#titleize to work for strings with 's too #10571 *trek*
+
+* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored *David Heinemeier Hansson/Marcel Molina Jr.*
+
+* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. *Rick Olson*
+
+* Hash#symbolize_keys skips keys that can't be symbolized. #10500 *Brad Greenlee*
+
+* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554, #10594, #10632 *Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, Xavier Noria*
+
+* TimeZone#to_s uses UTC rather than GMT. #1689 *Cheah Chu Yeow*
+
+* Refactor of Hash#symbolize_keys! to use Hash#replace. Closes #10420 *ReinH*
+
+* Fix HashWithIndifferentAccess#to_options! so it doesn't clear the options hash. Closes #10419 *ReinH*
+
+
+## 2.0.1 (December 7th, 2007) ##
+
+* Added Array#from and Array#to that behaves just from String#from and String#to *David Heinemeier Hansson*
+
+* Fix that empty collections should be treated as empty arrays regardless of whitespace for Hash#from_xml #10255 *adamj*
+
+* Time#time_with_datetime_fallback, Time#to_datetime, Date#to_datetime and String#to_datetime honor Ruby's default calendar reform setting. #10201 *Geoff Buesing*
+
+* Change Time and DateTime #end_of_month to return last second of month instead of beginning of last day of month. Closes #10200 *Geoff Buesing*
+
+* Speedup String#blank? *Jeremy Kemper, Michael Koziarski*
+
+* Add documentation for Hash#diff. Closes #9306 *Tarmo Tänav*
+
+* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. *Michael Koziarski, Tarmo Tänav*
+
+* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 *Josh Peek, Cheah Chu Yeow, Tim Pope*
+
+* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 *Rick Olson, theamazingrando*
+
+* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase *Michael Koziarski*
+
+ The intention is to use this to reduce the amount of monkeypatching / overriding that
+ is done to test/unit's classes.
+
+* Document Enumerable and Hash #to_json. #9970 *Cheah Chu Yeow*
+
+* Hash#to_xml handles symbol values. #9954 *Assaf*
+
+* Hash#symbolize_keys behaves well with integer keys. #9890 *PotatoSalad*
+
+* Multibyte: String#slice supports regexp argument. #9646 *yob*
+
+* object.duplicable? returns true if object.dup is safe. False for nil, true, false, symbols, and numbers; true otherwise. #9333 *sur*
+
+* Time, Date and DateTime #advance accept :weeks option. #9866 *Geoff Buesing*
+
+* Fix Time#years_ago and #years_since from leap days. #9865 *Geoff Buesing*
+
+* Time and DateTime#advance accept :hours, :minutes, and :seconds options. #9825 *Geoff Buesing*
+
+* Fix Date#years_ago and #years_since from leap days. #9864 *Geoff Buesing*
+
+* Refactor Time and Date#months_since and #months_ago to use #advance. #9863 *Geoff Buesing*
+
+* Rebundle Builder 2.1.2 but prefer a newer RubyGem if available. *Jeremy Kemper*
+
+* Add Range#overlaps?(range), Range#include?(range), and Range#step without a block. *brandon*
+
+* Correct BufferedLogger#level? checks. #9806 *wildchild, Johan Sorensen*
+
+* String#to_xs uses Eric Wong's fast_xs extension, if available, for Builder speedup. http://bogomips.org/fast_xs/ *Jeremy Kemper*
+
+* Introduce BasicObject as Builder::BlankSlate for Ruby 1.9 forward compatibility. *Jeremy Kemper*
+
+* Unbundle Builder in favor of a gem dependency. *Jeremy Kemper*
+
+* Disambiguate Time, Date, and DateTime#to_json formatting. #9750 *Geoff Buesing, Cheah Chu Yeow*
+
+* Hash#to_json takes :only or :except options to specific or omit certain hash keys. Enumerable#to_json passes through its options to each element. #9751 *Cheah Chu Yeow*
+
+* BufferedLogger#auto_flushing = N flushes the log every N messages. Buffers with an array instead of string. Disabling auto_flushing still flushes when the buffer hits a maximum size, as a failsafe against memory-gobbling. *Jeremy Kemper*
+
+* Fixed Date#xmlschema for dates outside the range of what can be created with Time #9744 *Geoff Buesing*
+
+* Fixed that La Paz was included in -25200 and -14400 offsets when it should only be in -14400 #9735 *bermi*
+
+* Fixed JSON encoding to use quoted keys according to the JSON standard. #8762 *choonkat, Cheah Chu Yeow*
+
+* Alias Object#send to send! for Ruby 1.9 forward compatibility. *Jeremy Kemper*
+
+* Backport Object#instance_variable_defined? for Ruby < 1.8.6. *Jeremy Kemper*
+
+* BufferedLogger#add converts the message to a string. #9702, #9724 *eigentone, DrMark, Tom Ward*
+
+* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) *David Heinemeier Hansson*
+
+* Object#instance_exec produces fewer garbage methods. *Mauricio Fernandez*
+
+* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 *Rick Olson*
+
+* Fixed cache_page to use the request url instead of the routing options when picking a save path. #8614 *Josh Peek*
+
+* Object.subclasses_of includes anonymous subclasses. *Jeremy Kemper*
+
+* Fixed that pluralizing an empty string should return the same empty string, not "s". #7720 *Josh Peek*
+
+* Added call to inspect on non-string classes for the logger #8533 *Coda Hale*
+
+* Deprecation: remove deprecated :mday option from Time, Date, and DateTime#change. *Jeremy Kemper*
+
+* Fix JSON decoder with nested quotes and commas. #9579 *Zach Dennis*
+
+* Hash#to_xml doesn't double-unescape. #8806 *Ezran*
+
+* Added Array#rand #9170 [Norbert Crombach]. Examples:
+
+ [].rand # => nil
+ ['a'].rand # => 'a'
+ [1,2,3].rand # => 1 or 2 or 3
+
+* Deprecation: removed Reloadable. *Jeremy Kemper*
+
+* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 *Manfred Stienstra*
+
+* Add ljust, rjust and center to utf8-handler. Closes #9165 *Manfred Stienstra*
+
+* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 *Geoff Buesing*
+
+* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. *ewan, Manfred Stienstra*
+
+* Added Array#extract_options! to encapsulate the pattern of getting an options hash out of a variable number of parameters. #8759 *Norbert Crombach*
+
+* Let alias_attribute work with attributes with initial capital letters (legacy columns etc). Closes #8596 *mpalmer*
+
+* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified *David Heinemeier Hansson*
+
+* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 *norri_b/Josh Susser*
+
+* Demote Hash#to_xml to use XmlSimple#xml_in_string so it can't read files or stdin. #8453 *candlerb, Jeremy Kemper*
+
+* Backport clean_logger changes to support ruby 1.8.2 *Mislav Marohnić*
+
+* Added proper handling of arrays #8537 *Josh Susser*
+
+ Before:
+ Hash.from_xml '<images></images>'
+ # => {:images => nil}
+
+ Hash.from_xml '<images><image>foo.jpg</image></images>'
+ # => {:images => {:image => "foo.jpg"}}
+
+ Hash.from_xml '<images><image>foo.jpg</image><image>bar.jpg</image></images>'
+ # => {:images => {:image => ["foo.jpg", "bar.jpg"]}}
+
+ After:
+ Hash.from_xml '<images type="array"></images>'
+ # => {:images => []}
+
+ Hash.from_xml '<images type="array"><image>foo.jpg</image></images>'
+ # => {:images => ["foo.jpg"]}
+
+ Hash.from_xml '<images type="array"><image>foo.jpg</image><image>bar.jpg</image></images>'
+ # => {:images => ["foo.jpg", "bar.jpg"]}
+
+* Improve Time and Date test coverage. #8646 *Josh Peek*
+
+* Add Date#since, ago, beginning_of_day, and end_of_day. Date + seconds works now. #8575 *Geoff Buesing*
+
+* String#to_time overflows to DateTime. Add String#to_datetime. #8572 *Geoff Buesing*
+
+* Date.yesterday and .tomorrow. #8571 *Geoff Buesing*
+
+* Readable Date and DateTime#inspect. #8570 *Geoff Buesing*
+
+* Move common DateTime calculations to Date. #8536 *Geoff Buesing*
+
+* Added Date#change (like Time#change) *David Heinemeier Hansson*
+
+* DateTime#to_time converts to Time unless out of range. #8512 *Geoff Buesing*
+
+* Date#to_datetime, #to_s(:rfc822). #8512 *Geoff Buesing*
+
+* Time durations use since instead of + for accuracy. #8513 *Geoff Buesing*
+
+* escape <'s and >'s in JSON strings. #8371 *Rick Olson*
+
+* Inflections: MatrixTest -> MatrixTests instead of MatricesTest. #8496 *jbwiv*
+
+* Multibyte strings respond_to the String methods they proxy so they can be duck-typed. #6549 *Tuxie*
+
+* Array#to_xml yields the builder just like Hash and ActiveRecord::Base. #8472 *seth*
+
+* Date, Time, and DateTime support formatting blocks in addition to strftime strings. Introduce :long_ordinal format, e.g. "February 21st, 2005". #8191 *Coda Hale*
+
+* Document Object#blank?. #6491 *Chris Mear*
+
+* Date, Time, and DateTime#to_json. #8399 *wycats*
+
+* Simplify API of assert_difference by passing in an expression that is evaluated before and after the passed in block. See documenation for examples of new API. *Marcel Molina Jr.*
+
+* Added assert_difference and assert_no_difference to test/unit assertions *Tobias Lütke*
+
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back *David Heinemeier Hansson*
+
+* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API *David Heinemeier Hansson*
+
+ <person>
+ <name>David</name>
+ <avatar type="file" name="me.jpg" content_type="image/jpg">R0lGODlhkACZAPUAAM5lcfjrtMQCG=\n</avatar>
+ </person>
+
+ ...becomes:
+
+ attributes = { :person => { :name => "David", :avatar => #<StringIO> } }
+ attributes[:person][:avatar].content_type # => "image/jpg"
+ attributes[:person][:avatar].original_filename # => "me.jpg"
+ attributes[:person][:avatar].read # => binary data of the file
+
+ Which is duck-type compatible with the files that you get when doing multipart uploads through HTML.
+
+* Improved multibyte performance by relying less on exception raising #8159 *Blaine*
+
+* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 *Tim Pope*
+
+* Added yielding of builder in Hash#to_xml *David Heinemeier Hansson*
+
+* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) *David Heinemeier Hansson*
+
+* Hash#to_xml supports YAML attributes. #7502 *jonathan*
+
+* Refactor ActiveSupport::JSON to be less obtuse. Add support for JSON decoding by way of Syck with ActiveSupport::JSON.decode(json_string). Prevent hash keys that are JavaScript reserved words from being unquoted during encoding. *Sam Stephenson*
+
+* alias_method_chain preserves the original method's visibility. #7854 *Jonathan Viney*
+
+* Update Dependencies to ignore constants inherited from ancestors. Closes #6951. *Nicholas Seckar*
+
+* Array#to_query preserves its ordering. #7756 *Greg Spurrier*
+
+* Out-of-range Time calculations transparently overflow to DateTime. Introduce Time#to_datetime. #7706, #7715 *Geoff Buesing*
+
+* DateTime calculations analogous to the Date and Time extensions. #7693 *Geoff Buesing*
+
+* Give DateTime correct .to_s implementations, lets it play nice with ActiveRecord quoting. #7649 *Geoff Buesing*
+
+* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. *Michael Koziarski*
+
+* Allow users to provide custom formatters to Logger. *Anthony Eden*
+
+* Hash#to_query CGI-escapes its keys. *Jeremy Kemper*
+
+* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 *Bruce Perens*
+
+* :db format for Date#to_s *Jeremy Kemper*
+ Date.new(2007, 1, 27).to_s(:db) # => '2007-01-27'
+
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. *Rick Olson*
+
+* Added Hash#to_query to turn a hash of values into a form-encoded query string *Nicholas Seckar*
+
+* Increase test coverage for subclasses_of. Closes #7335. *Roman2K, Nicholas Seckar*
+
+* Remove unused code from Duration#inspect. Closes #7180. *Rich Collins*
+
+* Added test coverage for Inflector.inflections.clear. Closes #7179. *Rich Collins*
+
+* ActiveSupport::Multibyte::Handlers::UTF8Handler should raise when a range and an integer are passed in (just like the native implementation). Closes #7176 *Rich Collins*
+
+* A couple extra tests for #classify. Closes #7273. *Josh Susser*
+
+* Better docs for Object extensions *zackchandler, Jamis Buck*
+
+* Fix that Dates couldn't be subtracted from Dates after [5940]. *Sam Stephenson*
+
+* Add Object#acts_like? and Time#acts_like_time? and Date#acts_like_date? to facilitate duck-typing. *Jamis Buck*
+
+* Make 1.months and friends accurate by introducing a Duration class. #6835 *eventualbuddha*
+
+
+## 1.4.2 (March 12th, 2007) ##
+
+* Ruby 1.8.6 and 1.9 define private Time#to_date and #to_datetime; make them
+ public for compatibility. *Jeremy Kemper*
+
+* Deprecation: warn on stderr if RAILS_DEFAULT_LOGGER isn't set yet. *Jeremy Kemper*
+
+
+## 1.4.1 (February 5th, 2007) ##
+
+* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 *Bruce Perens*
+
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. *Rick Olson*
+
+* Full test coverage for Inflector. #7228 *Dan Kubb*
+
+
+## 1.4.0 (January 16th, 2007) ##
+
+* Document Inflector.ordinalize and merge docs from String inflections. #7023 *smeade*
+
+* Unbundle flexmock. *Jeremy Kemper*
+
+* Fix Dependencies.autoloaded? to ignore anonymous modules. Closes #6561. *Nicholas Seckar*
+
+* Update load once paths to prevent nested once constants from being detected and claimed by an external non-once load. *Nicholas Seckar*
+
+* Deprecation: silence warnings when reporting test errors. *Jeremy Kemper*
+
+* Hash#slice(*keys) returns a new hash with only the given keys. #slice! replaces the hash with only the given keys. Works with HashWithIndifferentAccess also. *Jeremy Kemper*
+
+* HashWithIndifferentAccess#to_hash converts to a Hash with String keys and the same default value. *Jeremy Kemper*
+
+* Fix remove_constant to correctly handle constant names of the form "::A::...". References #6720. *Nicholas Seckar*
+
+* Fixed Array#to_xml when it contains a series of hashes (each piece would get its own XML declaration) #6610 *thkarcher/cyu*
+
+* Added Time#to_s(:time) which will just return H:M, like 17:44 *David Heinemeier Hansson*
+
+* Add Module#attr_accessor_with_default to initialize value of attribute before setting it. Closes #6538. *Stuart Halloway, Marcel Molina Jr.*
+
+* Hash#to_xml handles keys with the same name as Kernel methods. #6613 *Jonathan del Strother*
+
+* Added Time#end_of_day to get 23:59:59 of that day *David Heinemeier Hansson*
+
+* Don't quote hash keys in Hash#to_json if they're valid JavaScript identifiers. Disable this with ActiveSupport::JSON.unquote_hash_key_identifiers = false if you need strict JSON compliance. *Sam Stephenson*
+
+* Lazily load the Unicode Database in the UTF-8 Handler *Rick Olson*
+
+* Update dependencies to delete partially loaded constants. *Nicholas Seckar*
+
+* Fix unicode JSON regexp for Onigurama compatibility. #6494 *whitley*
+
+* update XmlSimple to 1.0.10. Closes #6532. *Nick Sieger*
+
+* Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. *Nicholas Seckar*
+
+* next_week respects DST changes. #6483, #5617, #2353, #2509, #4551 *marclove, Rob Biedenharn, rails@roetzel.de, jsolson@damogran.org, drbrain@segment7.net*
+
+* Expose methods added to Enumerable in the documentation, such as group_by. Closes #6170. *sergeykojin@gmail.com, Marcel Molina Jr.*
+
+* Ensure Chars#tidy_bytes only tidies broken bytes. Closes #6397 *Manfred Stienstra*
+
+* Add 'unloadable', a method used to mark any constant as requiring an unload after each request. *Nicholas Seckar*
+
+* Make core_ext/string/access.rb multibyte safe. Closes #6388 *Manfred Stienstra*
+
+* Make String#chars slicing behaviour consistent with String. Closes #6387 *Manfred Stienstra*
+
+* Pull in latest multibyte patch. Closes #6346 *Manfred Stienstra*
+
+* Add ActiveSupport::Multibyte. Provides String#chars which lets you deal with strings as a sequence of chars, not of bytes. Closes #6242 *Julian Tarkhanov, Manfred Stienstra, Thijs van der Vossen & Jan Behrens*
+
+* Fix issue with #class_inheritable_accessor saving updates to the parent class when initialized with an Array or Hash *mojombo*
+
+* Hash#to_xml supports Bignum and BigDecimal. #6313 *edibiase*
+
+* Don't undefine #class in OptionMerger *Rick Olson*
+
+* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 *David Heinemeier Hansson*
+
+* alias_method_chain works with accessor= methods also. #6153 *Caio Chassot*
+
+* Fix loadable_constants_for_path to handle load paths that do not end with a slash. *Nicholas Seckar*
+
+* Fix logic error in determining what was loaded by a given file. Closes #6039. *Nicholas Seckar*
+
+* Equate Kernel.const_missing with Object.const_missing. Fixes #5988. *Nicholas Seckar*
+
+* Add ApplicationController special case to Dependencies. *Nicholas Seckar*
+
+* Don't pad remaining places with in_groups_of if specified padding value is false. *Marcel Molina Jr.*
+
+* Fix cases where empty xml nodes weren't being translated to nil in Hash.create_from_xml *Rick Olso n*
+
+ <written-on type="date"></written-on> # => { :type => 'date' } # WRONG
+ <written-on type="date"></written-on> # => nil # RIGHT
+
+* Tighten rescue clauses. #5985 *james@grayproductions.net*
+
+* Inflections: don't singularize -ies plurals. *foamdino@gmail.com, Mark Van Holstyn*
+
+* Update Initializer to use load_once_paths to avoid plugin reloading. References #5852. *Nicholas Seckar*
+
+* Use Array#assoc in ActiveSupport::OrderedHash. *Mauricio Fernandez*
+
+* Greatly increased performance of String.to_json, which speeds up RJS considerably on large pages, fixes #3473 *Shugo Maeda*
+
+* Detect missing_constants calls from removed modules and fail accordingly. *Nicholas Seckar*
+
+* Stop using defined? in Dependencies.qualified_const_defined? since defined? may invoke const_missing. *Nicholas Seckar*
+
+* Dependencies can autoload directories of nested classes. *Jeremy Kemper*
+ Example:
+ invoice.rb class Invoice
+ invoice/lineitem.rb class Invoice::Lineitem
+
+* Add Deprecation.silence so that Reloadable does not scold itself. *Nicholas Seckar*
+
+* Add debugging logging to Dependencies. Currently can be enabled with Dependencies.log_activity = true; adding to Initializer and documenting is forthcoming. *Nicholas Seckar*
+
+* Replace Reloadable with improvements to the Dependencies mechanism. *Nicholas Seckar*
+
+* DateTime#to_time gives hour/minute/second resolution. #5747 *jon.evans@pobox.com*
+
+* attr_internal to support namespacing and deprecation. Like attr_* except backed by internally-named instance variable. Set attr_internal_naming_format to change the format from the default '@_%s'. *Jeremy Kemper*
+ # def foo() @foo__rofl end
+ # def foo=(v) @foo__rofl = v end
+ self.attr_internal_naming_format = '@%s__rofl'
+ attr_internal :foo
+
+* Raise fully qualified names upon name errors. #5533 *Lars Pind, Nicholas Seckar*
+
+* Add extention to obtain the missing constant from NameError instances. *Nicholas Seckar*
+
+* Thoroughly document inflections. #5700 *petermichaux@gmail.com*
+
+* Added Module#alias_attribute [Jamis/David Heinemeier Hansson]. Example:
+
+ class Content < ActiveRecord::Base
+ # has a title attribute
+ end
+
+ class Email < ActiveRecord::Base
+ alias_attribute :subject, :title
+ end
+
+ e = Email.find(1)
+ e.title # => "Superstars"
+ e.subject # => "Superstars"
+ e.subject? # => true
+ e.subject = "Megastars"
+ e.title # => "Megastars"
+
+* Deprecation: easier to work with warning behavior as procs; default behaviors for each environment so users needn't update env.rb; and testing pleasure with assert_deprecated, assert_not_deprecated. *Jeremy Kemper*
+ By default, test prints to $stderr, dev logs, production ignores.
+ Provide your own per-environment in e.g. config/environments/development.rb:
+ ActiveSupport::Deprecation.behavior = Proc.new { |message| raise message }
+
+* First cut of the Rails Deprecation system. *Michael Koziarski*
+
+* Strip boolean XML content before checking for 'true' *Rick Olson*
+
+* Customize default BigDecimal formatting. References #5672 *Dave Thomas*
+
+* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml. *Rick Olson*
+
+* Optional identity for Enumerable#sum defaults to zero. #5657 *gensym@mac.com*
+
+* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 *Shugo Maeda*
+
+* Fixed HashWithIndifferentAccess#default #5586 *chris@seagul.co.uk*
+
+* More compatible Hash.create_from_xml. #5523 *nunemaker@gmail.com*
+
+* Added Enumerable#sum for calculating a sum from the elements [David Heinemeier Hansson, jonathan@daikini.com]. Examples:
+
+ [1, 2, 3].sum
+ payments.sum { |p| p.price * p.tax_rate }
+ payments.sum(&:price)
+
+ This is instead of payments.inject(0) { |sum, p| sum + p.price }
+
+* Correct and clarify Array#to_sentence docs. #5458 *brad@madriska.com*
+
+* alias_method_chain preserves method punctuation so foo, foo?, and foo! may be chained with the same feature. *Jeremy Kemper*
+ Example:
+ alias_method_chain :save!, :validation
+ is equivalent to
+ alias_method :save_without_validation!, :save!
+ alias_method :save!, :save_with_validation!
+
+* Enhance Symbol#to_proc so it works with list objects, such as multi-dimensional arrays. Closes #5295 [nov@yo.rim.or.jp]. Example:
+
+ {1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last)
+ # => ["one", "two", "three"]
+
+* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [David Heinemeier Hansson]. Example:
+
+ Hash.create_from_xml <<-EOT
+ <note>
+ <title>This is a note</title>
+ <created-at type="date">2004-10-10</created-at>
+ </note>
+ EOT
+
+ ...would return:
+
+ { :note => { :title => "This is a note", :created_at => Date.new(2004, 10, 10) } }
+
+* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim@weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary *David Heinemeier Hansson*
+
+* Fixed that Module#alias_method_chain should work with both foo? foo! and foo at the same time #4954 *anna@wota.jp*
+
+* to_xml fixes, features, and speedup: introduce :dasherize option that converts updated_at to updated-at if true (the existing default); binary columns get encoding="base64" attribute; nil values get nil="true" attribute to distinguish empty values; add type information for float columns; allow arbitrarily deep :include; include SQL type information as the type attribute. #4989 *Blair Zajac <blair@orcaware.com>*
+
+* Add OrderedHash#values. *Sam Stephenson*
+
+* Added Array#to_s(:db) that'll produce a comma-separated list of ids [David Heinemeier Hansson]. Example:
+
+ Purchase.find(:all, :conditions => "product_id IN (#{shops.products.to_s(:db)})"
+
+* Normalize classify's argument to a String so that it plays nice with Symbols. *Marcel Molina Jr.*
+
+* Strip out leading schema name in classify. References #5139. *Michael Schoen*
+
+* Remove Enumerable#first_match since break(value) handles the use case well enough. *Nicholas Seckar*
+
+ Enumerable#first_match was like detect, but instead of returning the matching element, the yielded value returned. For example:
+
+ user_xml = adapters(:from => User, :to => Xml).first_match do |adapter|
+ adapter.adapt @user
+ end
+
+ But this is just as easily done with:
+
+ user_xml = adapters(:from => User, :to => Xml).each do
+ break adapter.adapt(@user)
+ end
+
+* Make Array#in_groups_of just return the grouped collection if a block isn't given. *Marcel Molina Jr.*
+
+* Don't destroy a HashWithIndifferentAccess if symbolize_keys! or stringify_keys! is called on it. Closes #5076. *Marcel Molina Jr., guy.naor@famundo.com*
+
+* Document Module::delegate. #5002 *pergesu@gmail.com*
+
+* Replace alias method chaining with Module#alias_method_chain. *Marcel Molina Jr.*
+
+* Strip out punctuation on predicates or bang methods being aliased with alias_method_chain since target?_without_feature is not a valid method name. Add tests for Module#alias_method_chain. *Marcel Molina Jr.*
+
+* Replace Ruby's deprecated append_features in favor of included. *Marcel Molina Jr.*
+
+* Allow default options in with_options to be overridden. Closes #4480. *murphy@cYcnus.de*
+
+* Added Module#alias_method_chain *Jamis Buck*
+
+* Updated to Builder 2.0 *David Heinemeier Hansson*
+
+* Add Array#split for dividing arrays into one or more subarrays by value or block. *Sam Stephenson*
+
+## 1.3.1 (April 6th, 2006) ##
+
+* Clean paths inside of exception messages and traces. *Nicholas Seckar*
+
+* Add Pathname.clean_within for cleaning all the paths inside of a string. *Nicholas Seckar*
+
+* provide an empty Dependencies::LoadingModule.load which prints deprecation warnings. Lets 1.0 applications function with .13-style environment.rb.
+
+
+## 1.3.0 (March 27th, 2006) ##
+
+* When possible, avoid incorrectly obtaining constants from parent modules. Fixes #4221. *Nicholas Seckar*
+
+* Add more tests for dependencies; refactor existing cases. *Nicholas Seckar*
+
+* Move Module#parent and Module#as_load_path into core_ext. Add Module#parent. *Nicholas Seckar*
+
+* Add CachingTools::HashCaching to simplify the creation of nested, autofilling hashes. *Nicholas Seckar*
+
+* Remove a hack intended to avoid unloading the same class twice, but which would not work anyways. *Nicholas Seckar*
+
+* Update Object.subclasses_of to locate nested classes. This affects Object.remove_subclasses_of in that nested classes will now be unloaded. *Nicholas Seckar*
+
+* Update Object.remove_subclasses_of to use Class.remove_class, reducing duplication. *Nicholas Seckar*
+
+* Added Fixnum#seconds for consistency, so you can say 5.minutes + 30.seconds instead of 5.minutes + 30 #4389 *François Beausoleil*
+
+* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" *David Heinemeier Hansson*
+
+* Added Hash#diff to show the difference between two hashes *Chris McGrath*
+
+* Added Time#advance to do precise time time calculations for cases where a month being approximated to 30 days won't do #1860 *Rick Olson*
+
+* Enhance Inflector.underscore to convert '-' into '_' (as the inverse of Inflector.dasherize) *Jamis Buck*
+
+* Switched to_xml to use the xml schema format for datetimes. This allows the encoding of time zones and should improve operability. *Michael Koziarski*
+
+* Added a note to the documentation for the Date related Numeric extensions to indicate that they're
+ approximations and shouldn't be used for critical calculations. *Michael Koziarski*
+
+* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [David Heinemeier Hansson]. Examples:
+
+ { :name => "David", :street_name => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml
+
+ ...returns:
+
+ <person>
+ <street-name>Paulina</street-name>
+ <name>David</name>
+ <age type="integer">26</age>
+ <moved-on type="date">2005-11-15</moved-on>
+ </person>
+
+* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) *David Heinemeier Hansson*
+
+* Fixed that Array#to_sentence will return "" on an empty array instead of ", and" #3842, #4031 *rubyonrails@beautifulpixel.com*
+
+* Add Enumerable#group_by for grouping collections based on the result of some
+ block. Useful, for example, for grouping records by date.
+
+ ex.
+
+ latest_transcripts.group_by(&:day).each do |day, transcripts|
+ p "#{day} -> #{transcripts.map(&:class) * ', '}"
+ end
+ "2006-03-01 -> Transcript"
+ "2006-02-28 -> Transcript"
+ "2006-02-27 -> Transcript, Transcript"
+ "2006-02-26 -> Transcript, Transcript"
+
+ Add Array#in_groups_of, for iterating over an array in groups of a certain
+ size.
+
+ ex.
+
+ %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
+ ["1", "2", "3"]
+ ["4", "5", "6"]
+ ["7", nil, nil]
+
+ *Marcel Molina Jr., Sam Stephenson*
+
+* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal *David Heinemeier Hansson*
+
+* Add 'around' methods to Logger, to make it easy to log before and after messages for a given block as requested in #3809. [Michael Koziarski] Example:
+
+ logger.around_info("Start rendering component (#{options.inspect}): ",
+ "\n\nEnd of component rendering") { yield }
+
+* Added Time#beginning_of_quarter #3607 *cohen.jeff@gmail.com*
+
+* Fix Object.subclasses_of to only return currently defined objects *Jonathan Viney <jonathan@bluewire.net.nz>*
+
+* Fix constantize to properly handle names beginning with '::'. *Nicholas Seckar*
+
+* Make String#last return the string instead of nil when it is shorter than the limit [Scott Barron].
+
+* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [David Heinemeier Hansson]. Example:
+
+ class Account < ActiveRecord::Base
+ has_one :subscription
+ delegate :free?, :paying?, :to => :subscription
+ delegate :overdue?, :to => "subscription.last_payment"
+ end
+
+ account.free? # => account.subscription.free?
+ account.overdue? # => account.subscription.last_payment.overdue?
+
+* Fix Reloadable to handle the case where a class that has been 'removed' has not yet been garbage collected. *Nicholas Seckar*
+
+* Don't allow Reloadable to be included into Modules.
+
+* Remove LoadingModule. *Nicholas Seckar*
+
+* Add documentation for Reloadable::Subclasses. *Nicholas Seckar*
+
+* Add Reloadable::Subclasses which handles the common case where a base class should not be reloaded, but its subclasses should be. *Nicholas Seckar*
+
+* Further improvements to reloading code *Nicholas Seckar, Trevor Squires*
+
+ - All classes/modules which include Reloadable can define reloadable? for fine grained control of reloading
+ - Class.remove_class uses Module#parent to access the parent module
+ - Class.remove_class expanded to handle multiple classes in a single call
+ - LoadingModule.clear! has been removed as it is no longer required
+ - Module#remove_classes_including has been removed in favor of Reloadable.reloadable_classes
+
+* Added reusable reloading support through the inclusion of the Relodable module that all subclasses of ActiveRecord::Base, ActiveRecord::Observer, ActiveController::Base, and ActionMailer::Base automatically gets. This means that these classes will be reloaded by the dispatcher when Dependencies.mechanism = :load. You can make your own models reloadable easily:
+
+ class Setting
+ include Reloadable
+ end
+
+ Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. *David Heinemeier Hansson*
+
+* Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb *Nicholas Seckar*
+
+* Add Object#instance_exec, like instance_eval but passes its arguments to the block. (Active Support will not override the Ruby 1.9 implementation of this method.) *Sam Stephenson*
+
+* Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. *Sam Stephenson*
+
+* Fix merge and dup for hashes with indifferent access #3404 *Ken Miller*
+
+* Fix the requires in option_merger_test to unbreak AS tests. *Sam Stephenson*
+
+* Make HashWithIndifferentAccess#update behave like Hash#update by returning the hash. #3419, #3425 *asnem@student.ethz.ch, JanPrill@blauton.de, Marcel Molina Jr.*
+
+* Add ActiveSupport::JSON and Object#to_json for converting Ruby objects to JSON strings. *Sam Stephenson*
+
+* Add Object#with_options for DRYing up multiple calls to methods having shared options. [Sam Stephenson] Example:
+
+ ActionController::Routing::Routes.draw do |map|
+ # Account routes
+ map.with_options(:controller => 'account') do |account|
+ account.home '', :action => 'dashboard'
+ account.signup 'signup', :action => 'new'
+ account.logout 'logout', :action => 'logout'
+ end
+ end
+
+* Introduce Dependencies.warnings_on_first_load setting. If true, enables warnings on first load of a require_dependency. Otherwise, loads without warnings. Disabled (set to false) by default. *Jeremy Kemper*
+
+* Active Support is warnings-safe. #1792 *Eric Hodel*
+
+* Introduce enable_warnings counterpart to silence_warnings. Turn warnings on when loading a file for the first time if Dependencies.mechanism == :load. Common mistakes such as redefined methods will print warnings to stderr. *Jeremy Kemper*
+
+* Add Symbol#to_proc, which allows for, e.g. [:foo, :bar].map(&:to_s). *Marcel Molina Jr.*
+
+* Added the following methods [Marcel Molina Jr., Sam Stephenson]:
+ * Object#copy_instance_variables_from(object) to copy instance variables from one object to another
+ * Object#extended_by to get an instance's included/extended modules
+ * Object#extend_with_included_modules_from(object) to extend an instance with the modules from another instance
+
+## 1.2.5 (December 13th, 2005) ##
+
+* Become part of Rails 1.0
+
+* Rename Version constant to VERSION. #2802 *Marcel Molina Jr.*
+
+## 1.2.3 (November 7th, 2005) ##
+
+* Change Inflector#constantize to use eval instead of const_get. *Nicholas Seckar*
+
+* Fix const_missing handler to ignore the trailing '.rb' on files when comparing paths. *Nicholas Seckar*
+
+* Define kernel.rb methods in "class Object" instead of "module Kernel" to work around a Windows peculiarity *Sam Stephenson*
+
+* Fix broken tests caused by incomplete loading of active support. *Nicholas Seckar*
+
+* Fix status pluralization bug so status_codes doesn't get pluralized as statuses_code. #2758 *keithm@infused.org*
+
+* Added Kernel#silence_stderr to silence stderr for the duration of the given block *Sam Stephenson*
+
+* Changed Kernel#` to print a message to stderr (like Unix) instead of raising Errno::ENOENT on Win32 *Sam Stephenson*
+
+* Changed 0.blank? to false rather than true since it violates everyone's expectation of blankness. #2518, #2705 *rails@jeffcole.net*
+
+* When loading classes using const_missing, raise a NameError if and only if the file we tried to load was not present. *Nicholas Seckar*
+
+* Added petabytes and exebytes to numeric extensions #2397 *timct@mac.com*
+
+* Added Time#end_of_month to accompany Time#beginning_of_month #2514 *Jens-Christian Fischer*
+
+
+## 1.2.2 (October 26th, 2005) ##
+
+* Set Logger.silencer = false to disable Logger#silence. Useful for debugging fixtures.
+
+* Add title case method to String to do, e.g., 'action_web_service'.titlecase # => 'Action Web Service'. *Marcel Molina Jr.*
+
+
+## 1.2.1 (October 19th, 2005) ##
+
+* Classify generated routing code as framework code to avoid appearing in application traces. *Nicholas Seckar*
+
+* Show all framework frames in the framework trace. *Nicholas Seckar*
+
+
+## 1.2.0 (October 16th, 2005) ##
+
+* Update Exception extension to show the first few framework frames in an application trace. *Nicholas Seckar*
+
+* Added Exception extension to provide support for clean backtraces. *Nicholas Seckar*
+
+* Updated whiny nil to be more concise and useful. *Nicholas Seckar*
+
+* Added Enumerable#first_match *Nicholas Seckar*
+
+* Fixed that Time#change should also reset usec when also resetting minutes #2459 *ikeda@dream.big.or.jp*
+
+* Fix Logger compatibility for distributions that don't keep Ruby and its standard library in sync.
+
+* Replace '%e' from long and short time formats as Windows does not support it. #2344. *Tom Ward <tom@popdog.net>*
+
+* Added to_s(:db) to Range, so you can get "BETWEEN '2005-12-10' AND '2005-12-12'" from Date.new(2005, 12, 10)..Date.new(2005, 12, 12) (and likewise with Times)
+
+* Moved require_library_or_gem into Kernel. #1992 *Michael Schuerig <michael@schuerig.de>*
+
+* Add :rfc822 as an option for Time#to_s (to get rfc822-formatted times)
+
+* Chain the const_missing hook to any previously existing hook so rails can play nicely with rake
+
+* Clean logger is compatible with both 1.8.2 and 1.8.3 Logger. #2263 *Michael Schuerig <michael@schuerig.de>*
+
+* Added native, faster implementations of .blank? for the core types #2286 *skae*
+
+* Fixed clean logger to work with Ruby 1.8.3 Logger class #2245
+
+* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 *Chris McGrath*
+
+* Fixed Inflector.underscore for use with acronyms, so HTML becomes html instead of htm_l #2173 *k@v2studio.com*
+
+* Fixed dependencies related infinite recursion bug when a controller file does not contain a controller class. Closes #1760. *rcolli2@tampabay.rr.com*
+
+* Fixed inflections for status, quiz, move #2056 *deirdre@deirdre.net*
+
+* Added Hash#reverse_merge, Hash#reverse_merge!, and Hash#reverse_update to ease the use of default options
+
+* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 *Manfred Stienstra*
+
+* Added Kernel#silence_warnings to turn off warnings temporarily for the passed block
+
+* Added String#starts_with? and String#ends_with? #2118 *Thijs van der Vossen*
+
+* Added easy extendability to the inflector through Inflector.inflections (using the Inflector::Inflections singleton class). Examples:
+
+ Inflector.inflections do |inflect|
+ inflect.plural /^(ox)$/i, '\1\2en'
+ inflect.singular /^(ox)en/i, '\1'
+
+ inflect.irregular 'octopus', 'octopi'
+
+ inflect.uncountable "equipment"
+ end
+
+* Added String#at, String#from, String#to, String#first, String#last in ActiveSupport::CoreExtensions::String::Access to ease access to individual characters and substrings in a string serving basically as human names for range access.
+
+* Make Time#last_month work when invoked on the 31st of a month.
+
+* Add Time.days_in_month, and make Time#next_month work when invoked on the 31st of a month
+
+* Fixed that Time#midnight would have a non-zero usec on some platforms #1836
+
+* Fixed inflections of "index/indices" #1766 *damn_pepe@gmail.com*
+
+* Added stripping of _id to String#humanize, so "employee_id" becomes "Employee" #1574 *Justin French*
+
+* Factor Fixnum and Bignum extensions into Integer extensions *Nicholas Seckar*
+
+* Hooked #ordinalize into Fixnum and Bignum classes. *Nicholas Seckar, danp*
+
+* Added Fixnum#ordinalize to turn 1.ordinalize to "1st", 3.ordinalize to "3rd", and 10.ordinalize to "10th" and so on #1724 *paul@cnt.org*
+
+
+## 1.1.1 (11 July, 2005) ##
+
+* Added more efficient implementation of the development mode reset of classes #1638 *Chris McGrath*
+
+
+## 1.1.0 (6 July, 2005) ##
+
+* Fixed conflict with Glue gem #1606 *Rick Olson*
+
+* Added new rules to the Inflector to deal with more unusual plurals mouse/louse => mice/lice, information => information, ox => oxen, virus => viri, archive => archives #1571, #1583, #1490, #1599, #1608 *foamdino@gmail.com/others*
+
+* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 *Chris McGrath*
+
+* Made 1.year == 365.25.days to account for leap years. This allows you to do User.find(:all, :conditions => ['birthday > ?', 50.years.ago]) without losing a lot of days. #1488 *tuxie@dekadance.se*
+
+* Added an exception if calling id on nil to WhinyNil #584 *kevin-temp@writesoon.com*
+
+* Added Fix/Bignum#multiple_of? which returns true on 14.multiple_of?(7) and false on 16.multiple_of?(7) #1464 *Thomas Fuchs*
+
+* Added even? and odd? to work with Bignums in addition to Fixnums #1464 *Thomas Fuchs*
+
+* Fixed Time#at_beginning_of_week returned the next Monday instead of the previous one when called on a Sunday #1403 *jean.helou@gmail.com*
+
+* Increased the speed of indifferent hash access by using Hash#default. #1436 *Nicholas Seckar*
+
+* Added that " " is now also blank? (using strip if available)
+
+* Fixed Dependencies so all modules are able to load missing constants #1173 *Nicholas Seckar*
+
+* Fixed the Inflector to underscore strings containing numbers, so Area51Controller becomes area51_controller #1176 *Nicholas Seckar*
+
+* Fixed that HashWithIndifferentAccess stringified all keys including symbols, ints, objects, and arrays #1162 *Nicholas Seckar*
+
+* Fixed Time#last_year to go back in time, not forward #1278 *fabien@odilat.com*
+
+* Fixed the pluralization of analysis to analyses #1295 *seattle@rootimage.msu.edu*
+
+* Fixed that Time.local(2005,12).months_since(1) would raise "ArgumentError: argument out of range" #1311 *jhahn@niveon.com*
+
+* Added silencing to the default Logger class
+
+
+## 1.0.4 (19th April, 2005) ##
+
+* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 *Nicholas Seckar*
+
+* Fixed inflection of perspectives and similar words #1045 *Thijs van der Vossen*
+
+* Added Fixnum#even? and Fixnum#odd?
+
+* Fixed problem with classes being required twice. Object#const_missing now uses require_dependency to load files. It used to use require_or_load which would cause models to be loaded twice, which was not good for validations and other class methods #971 *Nicholas Seckar*
+
+
+## 1.0.3 (27th March, 2005) ##
+
+* Fixed Inflector.pluralize to handle capitalized words #932 *Jeremy Kemper*
+
+* Added Object#suppress which allows you to make a saner choice around with exceptions to swallow #980. Example:
+
+ suppress(ZeroDivisionError) { 1/0 }
+
+ ...instead of:
+
+ 1/0 rescue nil # BAD, EVIL, DIRTY.
+
+
+## 1.0.2 (22th March, 2005) ##
+
+* Added Kernel#returning -- a Ruby-ized realization of the K combinator, courtesy of Mikael Brockman.
+
+ def foo
+ returning values = [] do
+ values << 'bar'
+ values << 'baz'
+ end
+ end
+
+ foo # => ['bar', 'baz']
+
+
+## 1.0.1 (7th March, 2005) ##
+
+* Fixed Hash#indifferent_access to also deal with include? and fetch and nested hashes #726 *Nicholas Seckar*
+
+* Added Object#blank? -- see http://redhanded.hobix.com/inspect/objectBlank.html #783 *_why the lucky stiff*
+
+* Added inflection rules for "sh" words, like "wish" and "fish" #755 *phillip@pjbsoftware.com*
+
+* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 *Tobias Lütke*
+
+* Added String#to_time and String#to_date for wrapping ParseDate
+
+
+## 1.0.0 (24th February, 2005) ##
+
+* Added TimeZone as the first of a number of value objects that among others Active Record can use rich value objects using composed_of #688 *Jamis Buck*
+
+* Added Date::Conversions for getting dates in different convenient string representations and other objects
+
+* Added Time::Conversions for getting times in different convenient string representations and other objects
+
+* Added Time::Calculations to ask for things like Time.now.tomorrow, Time.now.yesterday, Time.now.months_ago(4) #580 [DP|Flurin]. Examples:
+
+ "Later today" => now.in(3.hours),
+ "Tomorrow morning" => now.tomorrow.change(:hour => 9),
+ "Tomorrow afternoon" => now.tomorrow.change(:hour => 14),
+ "In a couple of days" => now.tomorrow.tomorrow.change(:hour => 9),
+ "Next monday" => now.next_week.change(:hour => 9),
+ "In a month" => now.next_month.change(:hour => 9),
+ "In 6 months" => now.months_since(6).change(:hour => 9),
+ "In a year" => now.in(1.year).change(:hour => 9)
+
+* Upgraded to breakpoint 92 which fixes:
+
+ * overload IRB.parse_opts(), fixes #443
+ => breakpoints in tests work even when running them via rake
+ * untaint handlers, might fix an issue discussed on the Rails ML
+ * added verbose mode to breakpoint_client
+ * less noise caused by breakpoint_client by default
+ * ignored TerminateLineInput exception in signal handler
+ => quiet exit on Ctrl-C
+
+* Fixed Inflector for words like "news" and "series" that are the same in plural and singular #603 [echion], #615 *marcenuc*
+
+* Added Hash#stringify_keys and Hash#stringify_keys!
+
+* Added IndifferentAccess as a way to wrap a hash by a symbol-based store that also can be accessed by string keys
+
+* Added Inflector.constantize to turn "Admin::User" into a reference for the constant Admin::User
+
+* Added that Inflector.camelize and Inflector.underscore can deal with modules like turning "Admin::User" into "admin/user" and back
+
+* Added Inflector.humanize to turn attribute names like employee_salary into "Employee salary". Used by automated error reporting in AR.
+
+* Added availability of class inheritable attributes to the masses #477 *Jeremy Kemper*
+
+ class Foo
+ class_inheritable_reader :read_me
+ class_inheritable_writer :write_me
+ class_inheritable_accessor :read_and_write_me
+ class_inheritable_array :read_and_concat_me
+ class_inheritable_hash :read_and_update_me
+ end
+
+ # Bar gets a clone of (not a reference to) Foo's attributes.
+ class Bar < Foo
+ end
+
+ Bar.read_and_write_me == Foo.read_and_write_me
+ Bar.read_and_write_me = 'bar'
+ Bar.read_and_write_me != Foo.read_and_write_me
+
+* Added Inflections as an extension on String, so Inflector.pluralize(Inflector.classify(name)) becomes name.classify.pluralize #476 *Jeremy Kemper*
+
+* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 *Marcel Molina Jr.*
+
+* Fixed that Dependencies.reload can't load the same file twice #420 *Kent Sibilev*
+
+* Added Fixnum#ago/until, Fixnum#since/from_now #450 *Jeremy Kemper*
+
+* Added that Inflector now accepts Symbols and Classes by calling .to_s on the word supplied
+
+* Added time unit extensions to Fixnum that'll return the period in seconds, like 2.days + 4.hours.
diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE
index 5e8b7a9450..c2bcf1d3e7 100644
--- a/activesupport/MIT-LICENSE
+++ b/activesupport/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2011 David Heinemeier Hansson
+Copyright (c) 2005-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index 1ab8e00608..ed688ecc59 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -19,7 +19,9 @@ Source code can be downloaded as part of the Rails project on GitHub
== License
-Active Support is released under the MIT license.
+Active Support is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 2ee6bb788a..d26d71b615 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -7,15 +7,18 @@ Gem::Specification.new do |s|
s.summary = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.'
s.description = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. Rich support for multibyte strings, internationalization, time zones, and testing.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*']
+ s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
+ s.rdoc_options.concat ['--encoding', 'UTF-8']
+
s.add_dependency('i18n', '~> 0.6')
s.add_dependency('multi_json', '~> 1.0')
+ s.add_dependency('tzinfo', '~> 0.3.31')
end
diff --git a/activesupport/bin/generate_tables b/activesupport/bin/generate_tables
index 5fefa429df..5fefa429df 100644..100755
--- a/activesupport/bin/generate_tables
+++ b/activesupport/bin/generate_tables
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index ff78e718f2..dbf0c25c5c 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2005-2011 David Heinemeier Hansson
+# Copyright (c) 2005-2012 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -38,10 +38,12 @@ end
require "active_support/dependencies/autoload"
require "active_support/version"
+require "active_support/logger"
module ActiveSupport
extend ActiveSupport::Autoload
+ autoload :Concern
autoload :DescendantsTracker
autoload :FileUpdateChecker
autoload :LogSubscriber
@@ -50,19 +52,15 @@ module ActiveSupport
# TODO: Narrow this list down
eager_autoload do
autoload :BacktraceCleaner
- autoload :Base64
autoload :BasicObject
autoload :Benchmarkable
- autoload :BufferedLogger
autoload :Cache
autoload :Callbacks
- autoload :Concern
autoload :Configurable
autoload :Deprecation
autoload :Gzip
autoload :Inflector
autoload :JSON
- autoload :Memoizable
autoload :MessageEncryptor
autoload :MessageVerifier
autoload :Multibyte
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 8f8deb9692..e97bb25b9f 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -1,11 +1,11 @@
module ActiveSupport
- # Backtraces often include many lines that are not relevant for the context under review. This makes it hard to find the
+ # Backtraces often include many lines that are not relevant for the context under review. This makes it hard to find the
# signal amongst the backtrace noise, and adds debugging time. With a BacktraceCleaner, filters and silencers are used to
# remove the noisy lines, so that only the most relevant lines remain.
#
# Filters are used to modify lines of data, while silencers are used to remove lines entirely. The typical filter use case
- # is to remove lengthy path information from the start of each line, and view file paths relevant to the app directory
- # instead of the file system root. The typical silencer use case is to exclude the output of a noisy library from the
+ # is to remove lengthy path information from the start of each line, and view file paths relevant to the app directory
+ # instead of the file system root. The typical silencer use case is to exclude the output of a noisy library from the
# backtrace, so that you can focus on the rest.
#
# ==== Example:
@@ -15,9 +15,9 @@ module ActiveSupport
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
# bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
#
- # To reconfigure an existing BacktraceCleaner (like the default one in Rails) and show as much data as possible, you can
- # always call <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the backtrace to a pristine state. If you
- # need to reconfigure an existing BacktraceCleaner so that it does not filter or modify the paths of any lines of the
+ # To reconfigure an existing BacktraceCleaner (like the default one in Rails) and show as much data as possible, you can
+ # always call <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the backtrace to a pristine state. If you
+ # need to reconfigure an existing BacktraceCleaner so that it does not filter or modify the paths of any lines of the
# backtrace, you can call BacktraceCleaner#remove_filters! These two methods will give you a completely untouched backtrace.
#
# Inspired by the Quiet Backtrace gem by Thoughtbot.
@@ -50,7 +50,7 @@ module ActiveSupport
@filters << block
end
- # Adds a silencer from the block provided. If the silencer returns true for a given line, it will be excluded from
+ # Adds a silencer from the block provided. If the silencer returns true for a given line, it will be excluded from
# the clean backtrace.
#
# Example:
diff --git a/activesupport/lib/active_support/base64.rb b/activesupport/lib/active_support/base64.rb
deleted file mode 100644
index 35014cb3d5..0000000000
--- a/activesupport/lib/active_support/base64.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-begin
- require 'base64'
-rescue LoadError
-end
-
-module ActiveSupport
- if defined? ::Base64
- Base64 = ::Base64
- else
- # Base64 provides utility methods for encoding and de-coding binary data
- # using a base 64 representation. A base 64 representation of binary data
- # consists entirely of printable US-ASCII characters. The Base64 module
- # is included in Ruby 1.8, but has been removed in Ruby 1.9.
- module Base64
- # Encodes a string to its base 64 representation. Each 60 characters of
- # output is separated by a newline character.
- #
- # ActiveSupport::Base64.encode64("Original unencoded string")
- # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==\n"
- def self.encode64(data)
- [data].pack("m")
- end
-
- # Decodes a base 64 encoded string to its original representation.
- #
- # ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==")
- # # => "Original unencoded string"
- def self.decode64(data)
- data.unpack("m").first
- end
- end
- end
-
- # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters
- # or memcache keys without further processing.
- #
- # ActiveSupport::Base64.encode64s("Original unencoded string")
- # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw=="
- def Base64.encode64s(value)
- encode64(value).gsub(/\n/, '')
- end
-end
diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb
index 3b5277c205..c3c7ab0112 100644
--- a/activesupport/lib/active_support/basic_object.rb
+++ b/activesupport/lib/active_support/basic_object.rb
@@ -1,21 +1,14 @@
module ActiveSupport
- if defined? ::BasicObject
- # A class with no predefined methods that behaves similarly to Builder's
- # BlankSlate. Used for proxy classes.
- class BasicObject < ::BasicObject
- undef_method :==
- undef_method :equal?
+ # A class with no predefined methods that behaves similarly to Builder's
+ # BlankSlate. Used for proxy classes.
+ class BasicObject < ::BasicObject
+ undef_method :==
+ undef_method :equal?
- # Let ActiveSupport::BasicObject at least raise exceptions.
- def raise(*args)
- ::Object.send(:raise, *args)
- end
- end
- else
- class BasicObject #:nodoc:
- instance_methods.each do |m|
- undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/
- end
+ # Let ActiveSupport::BasicObject at least raise exceptions.
+ def raise(*args)
+ ::Object.send(:raise, *args)
end
end
+
end
diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb
index 136e245859..0595446189 100644
--- a/activesupport/lib/active_support/buffered_logger.rb
+++ b/activesupport/lib/active_support/buffered_logger.rb
@@ -1,165 +1,7 @@
-require 'thread'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/deprecation'
+require 'active_support/logger'
module ActiveSupport
- # Inspired by the buffered logger idea by Ezra
- class BufferedLogger
- module Severity
- DEBUG = 0
- INFO = 1
- WARN = 2
- ERROR = 3
- FATAL = 4
- UNKNOWN = 5
- end
- include Severity
-
- MAX_BUFFER_SIZE = 1000
-
- ##
- # :singleton-method:
- # Set to false to disable the silencer
- cattr_accessor :silencer
- self.silencer = true
-
- # Silences the logger for the duration of the block.
- def silence(temporary_level = ERROR)
- if silencer
- old_logger_level = @tmp_levels[Thread.current]
- begin
- @tmp_levels[Thread.current] = temporary_level
- yield self
- ensure
- if old_logger_level
- @tmp_levels[Thread.current] = old_logger_level
- else
- @tmp_levels.delete(Thread.current)
- end
- end
- else
- yield self
- end
- end
-
- attr_writer :level
- attr_reader :auto_flushing
-
- def initialize(log, level = DEBUG)
- @level = level
- @tmp_levels = {}
- @buffer = Hash.new { |h,k| h[k] = [] }
- @auto_flushing = 1
- @guard = Mutex.new
-
- if log.respond_to?(:write)
- @log = log
- elsif File.exist?(log)
- @log = open_log(log, (File::WRONLY | File::APPEND))
- else
- FileUtils.mkdir_p(File.dirname(log))
- @log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT))
- end
- end
-
- def open_log(log, mode)
- open(log, mode).tap do |open_log|
- open_log.set_encoding(Encoding::BINARY) if open_log.respond_to?(:set_encoding)
- open_log.sync = true
- end
- end
-
- def level
- @tmp_levels[Thread.current] || @level
- end
-
- def add(severity, message = nil, progname = nil, &block)
- return if level > severity
- message = (message || (block && block.call) || progname).to_s
- # If a newline is necessary then create a new message ending with a newline.
- # Ensures that the original message is not mutated.
- message = "#{message}\n" unless message[-1] == ?\n
- buffer << message
- auto_flush
- message
- end
-
- # Dynamically add methods such as:
- # def info
- # def warn
- # def debug
- Severity.constants.each do |severity|
- class_eval <<-EOT, __FILE__, __LINE__ + 1
- def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
- add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
- end # end
-
- def #{severity.downcase}? # def debug?
- #{severity} >= level # DEBUG >= @level
- end # end
- EOT
- end
-
- # Set the auto-flush period. Set to true to flush after every log message,
- # to an integer to flush every N messages, or to false, nil, or zero to
- # never auto-flush. If you turn auto-flushing off, be sure to regularly
- # flush the log yourself -- it will eat up memory until you do.
- def auto_flushing=(period)
- @auto_flushing =
- case period
- when true; 1
- when false, nil, 0; MAX_BUFFER_SIZE
- when Integer; period
- else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
- end
- end
-
- def flush
- @guard.synchronize do
- write_buffer(buffer)
-
- # Important to do this even if buffer was empty or else @buffer will
- # accumulate empty arrays for each request where nothing was logged.
- clear_buffer
-
- # Clear buffers associated with dead threads or else spawned threads
- # that don't call flush will result in a memory leak.
- flush_dead_buffers
- end
- end
-
- def close
- flush
- @log.close if @log.respond_to?(:close)
- @log = nil
- end
-
- protected
- def auto_flush
- flush if buffer.size >= @auto_flushing
- end
-
- def buffer
- @buffer[Thread.current]
- end
-
- def clear_buffer
- @buffer.delete(Thread.current)
- end
-
- # Find buffers created by threads that are no longer alive and flush them to the log
- # in order to prevent memory leaks from spawned threads.
- def flush_dead_buffers #:nodoc:
- @buffer.keys.reject{|thread| thread.alive?}.each do |thread|
- buffer = @buffer[thread]
- write_buffer(buffer)
- @buffer.delete(thread)
- end
- end
-
- def write_buffer(buffer)
- buffer.each do |content|
- @log.write(content)
- end
- end
- end
+ BufferedLogger = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
+ 'BufferedLogger', '::ActiveSupport::Logger')
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 6535cc1eb5..26e737e917 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -16,6 +16,7 @@ module ActiveSupport
autoload :FileStore, 'active_support/cache/file_store'
autoload :MemoryStore, 'active_support/cache/memory_store'
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
+ autoload :NullStore, 'active_support/cache/null_store'
# These options mean something to all cache implementations. Individual cache
# implementations may support additional options.
@@ -25,75 +26,74 @@ module ActiveSupport
autoload :LocalCache, 'active_support/cache/strategy/local_cache'
end
- # Creates a new CacheStore object according to the given options.
- #
- # If no arguments are passed to this method, then a new
- # ActiveSupport::Cache::MemoryStore object will be returned.
- #
- # If you pass a Symbol as the first argument, then a corresponding cache
- # store class under the ActiveSupport::Cache namespace will be created.
- # For example:
- #
- # ActiveSupport::Cache.lookup_store(:memory_store)
- # # => returns a new ActiveSupport::Cache::MemoryStore object
- #
- # ActiveSupport::Cache.lookup_store(:mem_cache_store)
- # # => returns a new ActiveSupport::Cache::MemCacheStore object
- #
- # Any additional arguments will be passed to the corresponding cache store
- # class's constructor:
- #
- # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
- # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
- #
- # If the first argument is not a Symbol, then it will simply be returned:
- #
- # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
- # # => returns MyOwnCacheStore.new
- def self.lookup_store(*store_option)
- store, *parameters = *Array.wrap(store_option).flatten
-
- case store
- when Symbol
- store_class_name = store.to_s.camelize
- store_class =
- begin
- require "active_support/cache/#{store}"
- rescue LoadError => e
- raise "Could not find cache store adapter for #{store} (#{e})"
- else
- ActiveSupport::Cache.const_get(store_class_name)
- end
- store_class.new(*parameters)
- when nil
- ActiveSupport::Cache::MemoryStore.new
- else
- store
+ class << self
+ # Creates a new CacheStore object according to the given options.
+ #
+ # If no arguments are passed to this method, then a new
+ # ActiveSupport::Cache::MemoryStore object will be returned.
+ #
+ # If you pass a Symbol as the first argument, then a corresponding cache
+ # store class under the ActiveSupport::Cache namespace will be created.
+ # For example:
+ #
+ # ActiveSupport::Cache.lookup_store(:memory_store)
+ # # => returns a new ActiveSupport::Cache::MemoryStore object
+ #
+ # ActiveSupport::Cache.lookup_store(:mem_cache_store)
+ # # => returns a new ActiveSupport::Cache::MemCacheStore object
+ #
+ # Any additional arguments will be passed to the corresponding cache store
+ # class's constructor:
+ #
+ # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
+ # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
+ #
+ # If the first argument is not a Symbol, then it will simply be returned:
+ #
+ # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
+ # # => returns MyOwnCacheStore.new
+ def lookup_store(*store_option)
+ store, *parameters = *Array.wrap(store_option).flatten
+
+ case store
+ when Symbol
+ store_class_name = store.to_s.camelize
+ store_class =
+ begin
+ require "active_support/cache/#{store}"
+ rescue LoadError => e
+ raise "Could not find cache store adapter for #{store} (#{e})"
+ else
+ ActiveSupport::Cache.const_get(store_class_name)
+ end
+ store_class.new(*parameters)
+ when nil
+ ActiveSupport::Cache::MemoryStore.new
+ else
+ store
+ end
end
- end
- def self.expand_cache_key(key, namespace = nil)
- expanded_cache_key = namespace ? "#{namespace}/" : ""
+ def expand_cache_key(key, namespace = nil)
+ expanded_cache_key = namespace ? "#{namespace}/" : ""
+
+ if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
+ expanded_cache_key << "#{prefix}/"
+ end
- prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
- if prefix
- expanded_cache_key << "#{prefix}/"
+ expanded_cache_key << retrieve_cache_key(key)
+ expanded_cache_key
end
- expanded_cache_key <<
- if key.respond_to?(:cache_key)
- key.cache_key
- elsif key.is_a?(Array)
- if key.size > 1
- key.collect { |element| expand_cache_key(element) }.to_param
- else
- key.first.to_param
- end
- elsif key
- key.to_param
- end.to_s
+ private
- expanded_cache_key
+ def retrieve_cache_key(key)
+ case
+ when key.respond_to?(:cache_key) then key.cache_key
+ when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
+ else key.to_param
+ end.to_s
+ end
end
# An abstract cache store class. There are multiple cache store
@@ -149,7 +149,7 @@ module ActiveSupport
# Create a new cache. The options will be passed to any write method calls except
# for :namespace which can be used to set the global namespace for the cache.
- def initialize (options = nil)
+ def initialize(options = nil)
@options = options ? options.dup : {}
end
@@ -538,11 +538,11 @@ module ActiveSupport
# Create an entry with internal attributes set. This method is intended to be
# used by implementations that store cache entries in a native format instead
# of as serialized Ruby objects.
- def create (raw_value, created_at, options = {})
+ def create(raw_value, created_at, options = {})
entry = new(nil)
entry.instance_variable_set(:@value, raw_value)
entry.instance_variable_set(:@created_at, created_at.to_f)
- entry.instance_variable_set(:@compressed, !!options[:compressed])
+ entry.instance_variable_set(:@compressed, options[:compressed])
entry.instance_variable_set(:@expires_in, options[:expires_in])
entry
end
@@ -573,6 +573,9 @@ module ActiveSupport
# Get the value stored in the cache.
def value
+ # If the original value was exactly false @value is still true because
+ # it is marshalled and eventually compressed. Both operations yield
+ # strings.
if @value
Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 85e7e21624..9460532af0 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -8,7 +8,7 @@ module ActiveSupport
# A cache store implementation which stores everything on the filesystem.
#
# FileStore implements the Strategy::LocalCache strategy which implements
- # an in memory cache inside of a block.
+ # an in-memory cache inside of a block.
class FileStore < Store
attr_reader :cache_path
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index e07294178b..2e1ccb72d8 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -6,12 +6,11 @@ rescue LoadError => e
end
require 'digest/md5'
-require 'active_support/core_ext/string/encoding'
module ActiveSupport
module Cache
# A cache store implementation which stores data in Memcached:
- # http://www.danga.com/memcached/
+ # http://memcached.org/
#
# This is currently the most popular cache store for production websites.
#
@@ -21,7 +20,7 @@ module ActiveSupport
# server goes down, then MemCacheStore will ignore it until it comes back up.
#
# MemCacheStore implements the Strategy::LocalCache strategy which implements
- # an in memory cache inside of a block.
+ # an in-memory cache inside of a block.
class MemCacheStore < Store
module Response # :nodoc:
STORED = "STORED\r\n"
@@ -165,7 +164,7 @@ module ActiveSupport
# characters properly.
def escape_key(key)
key = key.to_s.dup
- key = key.force_encoding("BINARY") if key.encoding_aware?
+ key = key.force_encoding("BINARY")
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
key
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
new file mode 100644
index 0000000000..4427eaafcd
--- /dev/null
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -0,0 +1,44 @@
+module ActiveSupport
+ module Cache
+ # A cache store implementation which doesn't actually store anything. Useful in
+ # development and test environments where you don't want caching turned on but
+ # need to go through the caching interface.
+ #
+ # This cache does implement the local cache strategy, so values will actually
+ # be cached inside blocks that utilize this strategy. See
+ # ActiveSupport::Cache::Strategy::LocalCache for more details.
+ class NullStore < Store
+ def initialize(options = nil)
+ super(options)
+ extend Strategy::LocalCache
+ end
+
+ def clear(options = nil)
+ end
+
+ def cleanup(options = nil)
+ end
+
+ def increment(name, amount = 1, options = nil)
+ end
+
+ def decrement(name, amount = 1, options = nil)
+ end
+
+ def delete_matched(matcher, options = nil)
+ end
+
+ protected
+ def read_entry(key, options) # :nodoc:
+ end
+
+ def write_entry(key, entry, options) # :nodoc:
+ true
+ end
+
+ def delete_entry(key, options) # :nodoc:
+ false
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 0649a058aa..db5f228a70 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -4,9 +4,9 @@ require 'active_support/core_ext/string/inflections'
module ActiveSupport
module Cache
module Strategy
- # Caches that implement LocalCache will be backed by an in memory cache for the
+ # Caches that implement LocalCache will be backed by an in-memory cache for the
# duration of a block. Repeated calls to the cache for the same key will hit the
- # in memory cache for faster access.
+ # in-memory cache for faster access.
module LocalCache
# Simple memory backed cache. This cache is not thread safe and is intended only
# for serving as a temporary memory cache for a single thread.
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 656cba625c..6de49409e5 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,6 +1,5 @@
require 'active_support/concern'
require 'active_support/descendants_tracker'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
@@ -67,8 +66,6 @@ module ActiveSupport
#
# Calls the before and around callbacks in the order they were set, yields
# the block (if given one), and then runs the after callbacks in reverse order.
- # Optionally accepts a key, which will be used to compile an optimized callback
- # method for each key. See +ClassMethods.define_callbacks+ for more information.
#
# If the callback chain was halted, returns +false+. Otherwise returns the result
# of the block, or +true+ if no block is given.
@@ -77,48 +74,53 @@ module ActiveSupport
# save
# end
#
- def run_callbacks(kind, *args, &block)
- send("_run_#{kind}_callbacks", *args, &block)
+ def run_callbacks(kind, key = nil, &block)
+ #TODO: deprecate key argument
+ self.class.__run_callbacks(kind, self, &block)
+ end
+
+ private
+
+ # A hook invoked everytime a before callback is halted.
+ # This can be overriden in AS::Callback implementors in order
+ # to provide better debugging/logging.
+ def halted_callback_hook(filter)
end
class Callback #:nodoc:#
@@_callback_sequence = 0
- attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
+ attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
def initialize(chain, filter, kind, options, klass)
@chain, @kind, @klass = chain, kind, klass
+ deprecate_per_key_option(options)
normalize_options!(options)
- @per_key = options.delete(:per_key)
@raw_filter, @options = filter, options
@filter = _compile_filter(filter)
- @compiled_options = _compile_options(options)
- @callback_id = next_id
+ recompile_options!
+ end
- _compile_per_key_options
+ def deprecate_per_key_option(options)
+ if options[:per_key]
+ raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
+ end
end
def clone(chain, klass)
obj = super()
obj.chain = chain
obj.klass = klass
- obj.per_key = @per_key.dup
obj.options = @options.dup
- obj.per_key[:if] = @per_key[:if].dup
- obj.per_key[:unless] = @per_key[:unless].dup
obj.options[:if] = @options[:if].dup
obj.options[:unless] = @options[:unless].dup
obj
end
def normalize_options!(options)
- options[:if] = Array.wrap(options[:if])
- options[:unless] = Array.wrap(options[:unless])
-
- options[:per_key] ||= {}
- options[:per_key][:if] = Array.wrap(options[:per_key][:if])
- options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
+ options[:if] = Array(options[:if])
+ options[:unless] = Array(options[:unless])
end
def name
@@ -134,100 +136,46 @@ module ActiveSupport
end
def _update_filter(filter_options, new_options)
- filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
- filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
+ filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
+ filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
end
- def recompile!(_options, _per_key)
+ def recompile!(_options)
+ deprecate_per_key_option(_options)
_update_filter(self.options, _options)
- _update_filter(self.per_key, _per_key)
- @callback_id = next_id
- @filter = _compile_filter(@raw_filter)
- @compiled_options = _compile_options(@options)
- _compile_per_key_options
+ recompile_options!
end
- def _compile_per_key_options
- key_options = _compile_options(@per_key)
-
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _one_time_conditions_valid_#{@callback_id}?
- true #{key_options[0]}
- end
- RUBY_EVAL
- end
-
- # This will supply contents for before and around filters, and no
- # contents for after filters (for the forward pass).
- def start(key=nil, object=nil)
- return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
-
- # options[0] is the compiled form of supplied conditions
- # options[1] is the "end" for the conditional
- #
+ # Wraps code with filter
+ def apply(code)
case @kind
when :before
- # if condition # before_save :filter_name, :if => :condition
- # filter_name
- # end
- filter = <<-RUBY_EVAL
- unless halted
- # This double assignment is to prevent warnings in 1.9.3. I would
- # remove the `result` variable, but apparently some other
- # generated code is depending on this variable being set sometimes
- # and sometimes not.
+ <<-RUBY_EVAL
+ if !halted && #{@compiled_options}
+ # This double assignment is to prevent warnings in 1.9.3 as
+ # the `result` variable is not always used except if the
+ # terminator code refers to it.
result = result = #{@filter}
halted = (#{chain.config[:terminator]})
- end
- RUBY_EVAL
-
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
- when :around
- # Compile around filters with conditions into proxy methods
- # that contain the conditions.
- #
- # For `around_save :filter_name, :if => :condition':
- #
- # def _conditional_callback_save_17
- # if condition
- # filter_name do
- # yield self
- # end
- # else
- # yield self
- # end
- # end
- #
- name = "_conditional_callback_#{@kind}_#{next_id}"
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{name}(halted)
- #{@compiled_options[0] || "if true"} && !halted
- #{@filter} do
- yield self
- end
- else
- yield self
+ if halted
+ halted_callback_hook(#{@raw_filter.inspect.inspect})
end
end
+ #{code}
RUBY_EVAL
- "#{name}(halted) do"
- end
- end
-
- # This will supply contents for around and after filters, but not
- # before filters (for the backward pass).
- def end(key=nil, object=nil)
- return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
-
- case @kind
when :after
- # if condition # after_save :filter_name, :if => :condition
- # filter_name
- # end
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
+ <<-RUBY_EVAL
+ #{code}
+ if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
+ #{@filter}
+ end
+ RUBY_EVAL
when :around
+ name = define_conditional_callback
<<-RUBY_EVAL
+ #{name}(halted) do
+ #{code}
value
end
RUBY_EVAL
@@ -236,23 +184,52 @@ module ActiveSupport
private
+ # Compile around filters with conditions into proxy methods
+ # that contain the conditions.
+ #
+ # For `around_save :filter_name, :if => :condition':
+ #
+ # def _conditional_callback_save_17
+ # if condition
+ # filter_name do
+ # yield self
+ # end
+ # else
+ # yield self
+ # end
+ # end
+ #
+ def define_conditional_callback
+ name = "_conditional_callback_#{@kind}_#{next_id}"
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def #{name}(halted)
+ if #{@compiled_options} && !halted
+ #{@filter} do
+ yield self
+ end
+ else
+ yield self
+ end
+ end
+ RUBY_EVAL
+ name
+ end
+
# Options support the same options as filters themselves (and support
# symbols, string, procs, and objects), so compile a conditional
# expression based on the options
- def _compile_options(options)
- return [] if options[:if].empty? && options[:unless].empty?
-
- conditions = []
+ def recompile_options!
+ conditions = ["true"]
unless options[:if].empty?
- conditions << Array.wrap(_compile_filter(options[:if]))
+ conditions << Array(_compile_filter(options[:if]))
end
unless options[:unless].empty?
- conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
+ conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
end
- ["if #{conditions.flatten.join(" && ")}", "end"]
+ @compiled_options = conditions.flatten.join(" && ")
end
# Filters support:
@@ -294,7 +271,7 @@ module ActiveSupport
@klass.send(:define_method, "#{method_name}_object") { filter }
_normalize_legacy_filter(kind, filter)
- scopes = Array.wrap(chain.config[:scope])
+ scopes = Array(chain.config[:scope])
method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -335,15 +312,25 @@ module ActiveSupport
}.merge(config)
end
- def compile(key=nil, object=nil)
+ def compile
method = []
method << "value = nil"
method << "halted = false"
- each do |callback|
- method << callback.start(key, object)
+ callbacks = yielding
+ reverse_each do |callback|
+ callbacks = callback.apply(callbacks)
end
+ method << callbacks
+ method << "raise rescued_error if rescued_error" if config[:rescuable]
+ method << "halted ? false : (block_given? ? value : true)"
+ method.flatten.compact.join("\n")
+ end
+
+ # Returns part of method that evaluates the callback block
+ def yielding
+ method = []
if config[:rescuable]
method << "rescued_error = nil"
method << "begin"
@@ -356,57 +343,36 @@ module ActiveSupport
method << "rescued_error = e"
method << "end"
end
-
- reverse_each do |callback|
- method << callback.end(key, object)
- end
-
- method << "raise rescued_error if rescued_error" if config[:rescuable]
- method << "halted ? false : (block_given? ? value : true)"
- method.compact.join("\n")
+ method.join("\n")
end
+
end
module ClassMethods
- # Generate the internal runner method called by +run_callbacks+.
- def __define_runner(symbol) #:nodoc:
- body = send("_#{symbol}_callbacks").compile
-
- silence_warnings do
- undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _run_#{symbol}_callbacks(key = nil, &blk)
- if key
- name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
-
- unless respond_to?(name)
- self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
- end
-
- send(name, &blk)
- else
- #{body}
- end
- end
- private :_run_#{symbol}_callbacks
- RUBY_EVAL
- end
- end
- # This is called the first time a callback is called with a particular
- # key. It creates a new callback method for the key, calculating
- # which callbacks can be omitted because of per_key conditions.
+ # This method runs callback chain for the given kind.
+ # If this called first time it creates a new callback method for the kind.
+ # This generated method plays caching role.
#
- def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
- @_keyed_callbacks ||= {}
- @_keyed_callbacks[name] ||= begin
- str = send("_#{kind}_callbacks").compile(name, object)
+ def __run_callbacks(kind, object, &blk) #:nodoc:
+ name = __callback_runner_name(kind)
+ unless object.respond_to?(name, true)
+ str = object.send("_#{kind}_callbacks").compile
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}() #{str} end
protected :#{name}
RUBY_EVAL
- true
end
+ object.send(name, &blk)
+ end
+
+ def __reset_runner(symbol)
+ name = __callback_runner_name(symbol)
+ undef_method(name) if method_defined?(name)
+ end
+
+ def __callback_runner_name(kind)
+ "_run__#{self.name.hash.abs}__#{kind}__callbacks"
end
# This is used internally to append, prepend and skip callbacks to the
@@ -420,7 +386,7 @@ module ActiveSupport
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
chain = target.send("_#{name}_callbacks")
yield target, chain.dup, type, filters, options
- target.__define_runner(name)
+ target.__reset_runner(name)
end
end
@@ -459,29 +425,6 @@ module ActiveSupport
# will be called only when it returns a false value.
# * <tt>:prepend</tt> - If true, the callback will be prepended to the existing
# chain rather than appended.
- # * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options;
- # see "Per-key conditions" below.
- #
- # ===== Per-key conditions
- #
- # When creating or skipping callbacks, you can specify conditions that
- # are always the same for a given key. For instance, in Action Pack,
- # we convert :only and :except conditions into per-key conditions.
- #
- # before_filter :authenticate, :except => "index"
- #
- # becomes
- #
- # set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
- #
- # Per-key conditions are evaluated only once per use of a given key.
- # In the case of the above example, you would do:
- #
- # run_callbacks(:process_action, action_name) { ... dispatch stuff ... }
- #
- # In that case, each action_name would get its own compiled callback
- # method that took into consideration the per_key conditions. This
- # is a speed improvement for ActionPack.
#
def set_callback(name, *filter_list, &block)
mapped = nil
@@ -516,7 +459,7 @@ module ActiveSupport
if filter && options.any?
new_filter = filter.clone(chain, self)
chain.insert(chain.index(filter), new_filter)
- new_filter.recompile!(options, options[:per_key] || {})
+ new_filter.recompile!(options)
end
chain.delete(filter)
@@ -534,12 +477,12 @@ module ActiveSupport
chain = target.send("_#{symbol}_callbacks").dup
callbacks.each { |c| chain.delete(c) }
target.send("_#{symbol}_callbacks=", chain)
- target.__define_runner(symbol)
+ target.__reset_runner(symbol)
end
self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
- __define_runner(symbol)
+ __reset_runner(symbol)
end
# Define sets of events in the object lifecycle that support callbacks.
@@ -560,6 +503,11 @@ module ActiveSupport
# other callbacks are not executed. Defaults to "false", meaning no value
# halts the chain.
#
+ # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after callbacks should be terminated
+ # by the <tt>:terminator</tt> option. By default after callbacks executed no matter
+ # if callback chain was terminated or not.
+ # Option makes sence only when <tt>:terminator</tt> option is specified.
+ #
# * <tt>:rescuable</tt> - By default, after filters are not executed if
# the given block or a before filter raises an error. By setting this option
# to <tt>true</tt> exception raised by given block is stored and after
@@ -613,7 +561,6 @@ module ActiveSupport
callbacks.each do |callback|
class_attribute "_#{callback}_callbacks"
send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
- __define_runner(callback)
end
end
end
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index 81fb859334..c94a8d99f4 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -4,17 +4,12 @@ module ActiveSupport
# module M
# def self.included(base)
# base.extend ClassMethods
- # base.send(:include, InstanceMethods)
# scope :disabled, where(:disabled => true)
# end
#
# module ClassMethods
# ...
# end
- #
- # module InstanceMethods
- # ...
- # end
# end
#
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as:
@@ -31,10 +26,6 @@ module ActiveSupport
# module ClassMethods
# ...
# end
- #
- # module InstanceMethods
- # ...
- # end
# end
#
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+
@@ -118,7 +109,6 @@ module ActiveSupport
@_dependencies.each { |dep| base.send(:include, dep) }
super
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
- base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods")
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
end
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index a2d2719de7..a8aa53a80f 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -1,7 +1,5 @@
require 'active_support/concern'
require 'active_support/ordered_options'
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/array/extract_options'
module ActiveSupport
diff --git a/activesupport/lib/active_support/core_ext.rb b/activesupport/lib/active_support/core_ext.rb
index 46a8609dd7..b48bdf08e8 100644
--- a/activesupport/lib/active_support/core_ext.rb
+++ b/activesupport/lib/active_support/core_ext.rb
@@ -1,3 +1,4 @@
Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
+ next if File.basename(path, '.rb') == 'logger'
require "active_support/core_ext/#{File.basename(path, '.rb')}"
end
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index 268c9bed4c..79ba79192a 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -4,5 +4,4 @@ require 'active_support/core_ext/array/uniq_by'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/grouping'
-require 'active_support/core_ext/array/random_access'
require 'active_support/core_ext/array/prepend_and_append'
diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb
index 4cd9bfadac..2b3f639cb1 100644
--- a/activesupport/lib/active_support/core_ext/array/grouping.rb
+++ b/activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -1,5 +1,3 @@
-require 'enumerator'
-
class Array
# Splits or iterates over the array in groups of size +number+,
# padding any remaining slots with +fill_with+ unless it is +false+.
diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb
deleted file mode 100644
index bb1807a68a..0000000000
--- a/activesupport/lib/active_support/core_ext/array/random_access.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class Array
- # Backport of Array#sample based on Marc-Andre Lafortune's https://github.com/marcandre/backports/
- # Returns a random element or +n+ random elements from the array.
- # If the array is empty and +n+ is nil, returns <tt>nil</tt>.
- # If +n+ is passed and its value is less than 0, it raises an +ArgumentError+ exception.
- # If the value of +n+ is equal or greater than 0 it returns <tt>[]</tt>.
- #
- # [1,2,3,4,5,6].sample # => 4
- # [1,2,3,4,5,6].sample(3) # => [2, 4, 5]
- # [1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size
- # [].sample # => nil
- # [].sample(3) # => []
- def sample(n=nil)
- return self[Kernel.rand(size)] if n.nil?
- n = n.to_int
- rescue Exception => e
- raise TypeError, "Coercion error: #{n.inspect}.to_int => Integer failed:\n(#{e.message})"
- else
- raise TypeError, "Coercion error: obj.to_int did NOT return an Integer (was #{n.class})" unless n.kind_of? Integer
- raise ArgumentError, "negative array size" if n < 0
- n = size if n > size
- result = Array.new(self)
- n.times do |i|
- r = i + Kernel.rand(size - i)
- result[i], result[r] = result[r], result[i]
- end
- result[n..size] = []
- result
- end unless method_defined? :sample
-end
diff --git a/activesupport/lib/active_support/core_ext/array/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb
index 9c5f97b0e9..ac3dedc0e3 100644
--- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb
+++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb
@@ -1,16 +1,22 @@
class Array
- # Returns an unique array based on the criteria given as a +Proc+.
+ # *DEPRECATED*: Use +Array#uniq+ instead.
+ #
+ # Returns a unique array based on the criteria in the block.
#
# [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2]
#
- def uniq_by
- hash, array = {}, []
- each { |i| hash[yield(i)] ||= (array << i) }
- array
+ def uniq_by(&block)
+ ActiveSupport::Deprecation.warn "uniq_by " \
+ "is deprecated. Use Array#uniq instead", caller
+ uniq(&block)
end
- # Same as uniq_by, but modifies self.
- def uniq_by!
- replace(uniq_by{ |i| yield(i) })
+ # *DEPRECATED*: Use +Array#uniq!+ instead.
+ #
+ # Same as +uniq_by+, but modifies +self+.
+ def uniq_by!(&block)
+ ActiveSupport::Deprecation.warn "uniq_by! " \
+ "is deprecated. Use Array#uniq! instead", caller
+ uniq!(&block)
end
end
diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
index 391bdc925d..3ec7e576c8 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -1,29 +1,9 @@
require 'bigdecimal'
-
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
class BigDecimal
- YAML_TAG = 'tag:yaml.org,2002:float'
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
- # This emits the number without any scientific notation.
- # This is better than self.to_f.to_s since it doesn't lose precision.
- #
- # Note that reconstituting YAML floats to native floats may lose precision.
- def to_yaml(opts = {})
- return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
- YAML.quick_emit(nil, opts) do |out|
- string = to_s
- out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain)
- end
- end
-
def encode_with(coder)
string = to_s
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index 45bec264ff..305ed4964b 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -110,6 +110,6 @@ class Class
private
def singleton_class?
- !name || '' == name
+ ancestors.first != self
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 46e9daaa8f..74ea047c24 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -7,7 +7,7 @@ class Class #:nodoc:
def descendants
descendants = []
- ObjectSpace.each_object(class << self; self; end) do |k|
+ ObjectSpace.each_object(singleton_class) do |k|
descendants.unshift k unless k == self
end
descendants
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 26a99658cc..af78226c21 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -7,24 +7,6 @@ require 'active_support/core_ext/time/zones'
class Date
DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
- if RUBY_VERSION < '1.9'
- undef :>>
-
- # Backported from 1.9. The one in 1.8 leads to incorrect next_month and
- # friends for dates where the calendar reform is involved. It additionally
- # prevents an infinite loop fixed in r27013.
- def >>(n)
- y, m = (year * 12 + (mon - 1) + n).divmod(12)
- m, = (m + 1) .divmod(1)
- d = mday
- until jd2 = self.class.valid_civil?(y, m, d, start)
- d -= 1
- raise ArgumentError, 'invalid date' unless d > 0
- end
- self + (jd2 - jd)
- end
- end
-
class << self
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
def yesterday
@@ -154,51 +136,54 @@ class Date
advance(:years => years)
end
- # Shorthand for years_ago(1)
- def prev_year
- years_ago(1)
- end unless method_defined?(:prev_year)
-
- # Shorthand for years_since(1)
- def next_year
- years_since(1)
- end unless method_defined?(:next_year)
-
- # Shorthand for months_ago(1)
- def prev_month
- months_ago(1)
- end unless method_defined?(:prev_month)
-
- # Shorthand for months_since(1)
- def next_month
- months_since(1)
- end unless method_defined?(:next_month)
+ # Returns number of days to start of this week. Week is assumed to start on
+ # +start_day+, default is +:monday+.
+ def days_to_week_start(start_day = :monday)
+ start_day_number = DAYS_INTO_WEEK[start_day]
+ current_day_number = wday != 0 ? wday - 1 : 6
+ (current_day_number - start_day_number) % 7
+ end
- # 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
- days_to_monday = self.wday!=0 ? self.wday-1 : 6
- result = self - days_to_monday
- self.acts_like?(:time) ? result.midnight : result
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
+ # have their time set to 0:00.
+ def beginning_of_week(start_day = :monday)
+ days_to_start = days_to_week_start(start_day)
+ result = self - days_to_start
+ acts_like?(:time) ? result.midnight : result
end
- alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
- # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59).
- def end_of_week
- days_to_sunday = self.wday!=0 ? 7-self.wday : 0
- result = self + days_to_sunday.days
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
+ # have their time set to 23:59:59.
+ def end_of_week(start_day = :monday)
+ days_to_end = 6 - days_to_week_start(start_day)
+ result = self + days_to_end.days
self.acts_like?(:time) ? result.end_of_day : result
end
- alias :sunday :end_of_week
alias :at_end_of_week :end_of_week
- # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday).
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the given +day+ in the previous
+ # week. Default is +:monday+. +DateTime+ objects have their time set to 0:00.
def prev_week(day = :monday)
result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day]
self.acts_like?(:time) ? result.change(:hour => 0) : result
end
- # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
+ # Returns a new Date/DateTime representing the start of the given day in next week (default is :monday).
def next_week(day = :monday)
result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
self.acts_like?(:time) ? result.change(:hour => 0) : result
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index 338104fd05..3262c254f7 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -63,12 +63,6 @@ class Date
alias_method :default_inspect, :inspect
alias_method :inspect, :readable_inspect
- # A method to keep Time, Date and DateTime instances interchangeable on conversions.
- # In this case, it simply returns +self+.
- def to_date
- self
- end if RUBY_VERSION < '1.9'
-
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
# The timezone can be either :local or :utc (default :local).
#
@@ -83,23 +77,6 @@ class Date
::Time.send("#{form}_time", year, month, day)
end
- # Converts a Date instance to a DateTime, where the time is set to the beginning of the day
- # and UTC offset is set to 0.
- #
- # ==== Examples
- # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
- #
- # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000
- def to_datetime
- ::DateTime.civil(year, month, day, 0, 0, 0, 0)
- end if RUBY_VERSION < '1.9'
-
- def iso8601
- strftime('%F')
- end if RUBY_VERSION < '1.9'
-
- alias_method :rfc3339, :iso8601 if RUBY_VERSION < '1.9'
-
def xmlschema
to_time_in_current_zone.xmlschema
end
diff --git a/activesupport/lib/active_support/core_ext/date/freeze.rb b/activesupport/lib/active_support/core_ext/date/freeze.rb
deleted file mode 100644
index a731f8345e..0000000000
--- a/activesupport/lib/active_support/core_ext/date/freeze.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# Date memoizes some instance methods using metaprogramming to wrap
-# the methods with one that caches the result in an instance variable.
-#
-# If a Date is frozen but the memoized method hasn't been called, the
-# first call will result in a frozen object error since the memo
-# instance variable is uninitialized.
-#
-# Work around by eagerly memoizing before the first freeze.
-#
-# Ruby 1.9 uses a preinitialized instance variable so it's unaffected.
-# This hack is as close as we can get to feature detection:
-if RUBY_VERSION < '1.9'
- require 'date'
- begin
- ::Date.today.freeze.jd
- rescue => frozen_object_error
- if frozen_object_error.message =~ /frozen/
- class Date #:nodoc:
- def freeze
- unless frozen?
- self.class.private_instance_methods(false).each do |m|
- if m.to_s =~ /\A__\d+__\Z/
- instance_variable_set(:"@#{m}", [send(m)])
- end
- end
- end
-
- super
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 48cf1a435d..6f730e4b6f 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -1,10 +1,12 @@
-require 'rational' unless RUBY_VERSION >= '1.9.2'
+require 'active_support/deprecation'
class DateTime
class << self
- # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
+ # *DEPRECATED*: Use +DateTime.civil_from_format+ directly.
def local_offset
- ::Time.local(2007).utc_offset.to_r / 86400
+ ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. ' \
+ 'Use DateTime.civil_from_format directly.', caller
+ ::Time.local(2012).utc_offset.to_r / 86400
end
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>.
@@ -81,29 +83,6 @@ class DateTime
change(:hour => 23, :min => 59, :sec => 59)
end
- # 1.9.3 defines + and - on DateTime, < 1.9.3 do not.
- if DateTime.public_instance_methods(false).include?(:+)
- def plus_with_duration(other) #:nodoc:
- if ActiveSupport::Duration === other
- other.since(self)
- else
- plus_without_duration(other)
- end
- end
- alias_method :plus_without_duration, :+
- alias_method :+, :plus_with_duration
-
- def minus_with_duration(other) #:nodoc:
- if ActiveSupport::Duration === other
- plus_with_duration(-other)
- else
- minus_without_duration(other)
- end
- end
- alias_method :minus_without_duration, :-
- alias_method :-, :minus_with_duration
- end
-
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0
#
# Example:
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 ca899c714c..dc55e9c33c 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -6,7 +6,7 @@ require 'active_support/values/time_zone'
class DateTime
# Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
# DateTimes outside the range of what can be created with Time.
- remove_method :to_time if instance_methods.include?(:to_time)
+ remove_method :to_time
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
#
@@ -58,32 +58,27 @@ class DateTime
alias_method :default_inspect, :inspect
alias_method :inspect, :readable_inspect
- # Converts self to a Ruby Date object; time portion is discarded.
- def to_date
- ::Date.new(year, month, day)
- end unless instance_methods(false).include?(: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.
def to_time
- self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
+ self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * 1000000) : self
end
- # To be able to keep Times, Dates and DateTimes interchangeable on conversions.
- def to_datetime
- self
- end unless instance_methods(false).include?(:to_datetime)
-
+ # Returns DateTime with local offset for given year if format is local else offset is zero
+ #
+ # DateTime.civil_from_format :local, 2012
+ # # => Sun, 01 Jan 2012 00:00:00 +0300
+ # DateTime.civil_from_format :local, 2012, 12, 17
+ # # => Mon, 17 Dec 2012 00:00:00 +0000
def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
- offset = utc_or_local.to_sym == :local ? local_offset : 0
+ if utc_or_local.to_sym == :local
+ offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
+ else
+ offset = 0
+ end
civil(year, month, day, hour, min, sec, offset)
end
- # Converts datetime to an appropriate format for use in XML.
- def xmlschema
- strftime("%Y-%m-%dT%H:%M:%S%Z")
- end unless instance_methods(false).include?(:xmlschema)
-
# Converts self to a floating-point number of seconds since the Unix epoch.
def to_f
seconds_since_unix_epoch.to_f
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 9343bb7106..77a5087981 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -1,41 +1,4 @@
-require 'active_support/ordered_hash'
-
module Enumerable
- # Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it.
- remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9'
-
- # Collect an enumerable into sets, grouped by the result of a block. Useful,
- # for example, for grouping records by date.
- #
- # Example:
- #
- # latest_transcripts.group_by(&:day).each do |day, transcripts|
- # p "#{day} -> #{transcripts.map(&:class).join(', ')}"
- # end
- # "2006-03-01 -> Transcript"
- # "2006-02-28 -> Transcript"
- # "2006-02-27 -> Transcript, Transcript"
- # "2006-02-26 -> Transcript, Transcript"
- # "2006-02-25 -> Transcript"
- # "2006-02-24 -> Transcript, Transcript"
- # "2006-02-23 -> Transcript"
- def group_by
- return to_enum :group_by unless block_given?
- assoc = ActiveSupport::OrderedHash.new
-
- each do |element|
- key = yield(element)
-
- if assoc.has_key?(key)
- assoc[key] << element
- else
- assoc[key] = [element]
- end
- end
-
- assoc
- end unless [].respond_to?(:group_by)
-
# Calculates a sum from the elements. Examples:
#
# payments.sum { |p| p.price * p.tax_rate }
@@ -63,27 +26,6 @@ module Enumerable
end
end
- # Iterates over a collection, passing the current element *and* the
- # +memo+ to the block. Handy for building up hashes or
- # reducing collections down to one object. Examples:
- #
- # %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
- # # => {'foo' => 'FOO', 'bar' => 'BAR'}
- #
- # *Note* that you can't use immutable objects like numbers, true or false as
- # the memo. You would think the following returns 120, but since the memo is
- # never changed, it does not.
- #
- # (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
- #
- def each_with_object(memo)
- return to_enum :each_with_object, memo unless block_given?
- each do |element|
- yield element, memo
- end
- memo
- end unless [].respond_to?(:each_with_object)
-
# Convert an enumerable to a hash. Examples:
#
# people.index_by(&:login)
diff --git a/activesupport/lib/active_support/core_ext/exception.rb b/activesupport/lib/active_support/core_ext/exception.rb
index ef801e713d..ba7757ea07 100644
--- a/activesupport/lib/active_support/core_ext/exception.rb
+++ b/activesupport/lib/active_support/core_ext/exception.rb
@@ -1,3 +1,3 @@
module ActiveSupport
- FrozenObjectError = RUBY_VERSION < '1.9' ? TypeError : RuntimeError
+ FrozenObjectError = RuntimeError
end
diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb
index a763447566..dc24afbe7f 100644
--- a/activesupport/lib/active_support/core_ext/file.rb
+++ b/activesupport/lib/active_support/core_ext/file.rb
@@ -1,2 +1 @@
require 'active_support/core_ext/file/atomic'
-require 'active_support/core_ext/file/path'
diff --git a/activesupport/lib/active_support/core_ext/file/path.rb b/activesupport/lib/active_support/core_ext/file/path.rb
deleted file mode 100644
index b5feab80ae..0000000000
--- a/activesupport/lib/active_support/core_ext/file/path.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class File
- unless File.allocate.respond_to?(:to_path)
- alias to_path path
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/float.rb b/activesupport/lib/active_support/core_ext/float.rb
deleted file mode 100644
index 7570471b95..0000000000
--- a/activesupport/lib/active_support/core_ext/float.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'active_support/core_ext/float/rounding'
diff --git a/activesupport/lib/active_support/core_ext/float/rounding.rb b/activesupport/lib/active_support/core_ext/float/rounding.rb
deleted file mode 100644
index 0d4fb87665..0000000000
--- a/activesupport/lib/active_support/core_ext/float/rounding.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class Float
- alias precisionless_round round
- private :precisionless_round
-
- # Rounds the float with the specified precision.
- #
- # x = 1.337
- # x.round # => 1
- # x.round(1) # => 1.3
- # x.round(2) # => 1.34
- def round(precision = nil)
- if precision
- magnitude = 10.0 ** precision
- (self * magnitude).round / magnitude
- else
- precisionless_round
- end
- end
-end if RUBY_VERSION < '1.9'
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index f4cb445444..7d54c9fae6 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -14,7 +14,7 @@ class Hash
# #with_indifferent_access. This method will be called on the current object
# by the enclosing object and is aliased to #with_indifferent_access by
# default. Subclasses of Hash may overwrite this method to return +self+ if
- # converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be
+ # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
# desirable.
#
# b = {:b => 1}
diff --git a/activesupport/lib/active_support/core_ext/integer/inflections.rb b/activesupport/lib/active_support/core_ext/integer/inflections.rb
index 0e606056c0..1e30687166 100644
--- a/activesupport/lib/active_support/core_ext/integer/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/integer/inflections.rb
@@ -14,4 +14,18 @@ class Integer
def ordinalize
ActiveSupport::Inflector.ordinalize(self)
end
+
+ # Ordinal returns the suffix used to denote the position
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # 1.ordinal # => "st"
+ # 2.ordinal # => "nd"
+ # 1002.ordinal # => "nd"
+ # 1003.ordinal # => "rd"
+ # -11.ordinal # => "th"
+ # -1001.ordinal # => "st"
+ #
+ def ordinal
+ ActiveSupport::Inflector.ordinal(self)
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/io.rb b/activesupport/lib/active_support/core_ext/io.rb
deleted file mode 100644
index 75f1055191..0000000000
--- a/activesupport/lib/active_support/core_ext/io.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-if RUBY_VERSION < '1.9.2'
-
-# :stopdoc:
-class IO
- def self.binread(name, length = nil, offset = nil)
- return File.read name unless length || offset
- File.open(name, 'rb') { |f|
- f.seek offset if offset
- f.read length
- }
- end
-end
-# :startdoc:
-
-end
diff --git a/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/activesupport/lib/active_support/core_ext/kernel/debugger.rb
index 7516f41e0b..d5b590e9f0 100644
--- a/activesupport/lib/active_support/core_ext/kernel/debugger.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/debugger.rb
@@ -2,7 +2,7 @@ module Kernel
unless respond_to?(:debugger)
# Starts a debugging session if ruby-debug has been loaded (call rails server --debugger to do load it).
def debugger
- message = "\n***** Debugger requested, but was not available (ensure ruby-debug is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
+ message = "\n***** Debugger requested, but was not available (ensure ruby-debug19 is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
end
alias breakpoint debugger unless respond_to?(:breakpoint)
diff --git a/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb b/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb
index 33612155fb..9bbf1bbd73 100644
--- a/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb
@@ -1,11 +1,4 @@
module Kernel
- # Returns the object's singleton class.
- def singleton_class
- class << self
- self
- end
- end unless respond_to?(:singleton_class) # exists in 1.9.2
-
# class_eval on an object acts like singleton_class.class_eval.
def class_eval(*args, &block)
singleton_class.class_eval(*args, &block)
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index e63a0a9ed9..a51818d2b2 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -1,4 +1,7 @@
require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/deprecation'
+
+ActiveSupport::Deprecation.warn 'this file is deprecated and will be removed'
# Adds the 'around_level' method to Logger.
class Logger #:nodoc:
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb
index f399fce410..f2d4887df6 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -5,8 +5,6 @@ require 'active_support/core_ext/module/reachable'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
-require 'active_support/core_ext/module/synchronization'
require 'active_support/core_ext/module/deprecation'
require 'active_support/core_ext/module/remove_method'
-require 'active_support/core_ext/module/method_names'
-require 'active_support/core_ext/module/qualified_const' \ No newline at end of file
+require 'active_support/core_ext/module/qualified_const'
diff --git a/activesupport/lib/active_support/core_ext/module/anonymous.rb b/activesupport/lib/active_support/core_ext/module/anonymous.rb
index 3982c9c586..0a9e791030 100644
--- a/activesupport/lib/active_support/core_ext/module/anonymous.rb
+++ b/activesupport/lib/active_support/core_ext/module/anonymous.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/blank'
-
class Module
# A module may or may not have a name.
#
@@ -7,7 +5,7 @@ class Module
# M.name # => "M"
#
# m = Module.new
- # m.name # => ""
+ # m.name # => nil
#
# A module gets a name when it is first assigned to a constant. Either
# via the +module+ or +class+ keyword or by an explicit assignment:
@@ -17,8 +15,6 @@ class Module
# m.name # => "M"
#
def anonymous?
- # Uses blank? because the name of an anonymous class is an empty
- # string in 1.8, and nil in 1.9.
- name.blank?
+ name.nil?
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 7de824a77f..ac2a63d3a1 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -81,25 +81,25 @@ class Module
# no matter whether +nil+ responds to the delegated method. You can get a
# +nil+ instead with the +:allow_nil+ option.
#
- # class Foo
- # attr_accessor :bar
- # def initialize(bar = nil)
- # @bar = bar
- # end
- # delegate :zoo, :to => :bar
- # end
- #
- # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
- #
- # class Foo
- # attr_accessor :bar
- # def initialize(bar = nil)
- # @bar = bar
- # end
- # delegate :zoo, :to => :bar, :allow_nil => true
- # end
- #
- # Foo.new.zoo # returns nil
+ # class Foo
+ # attr_accessor :bar
+ # def initialize(bar = nil)
+ # @bar = bar
+ # end
+ # delegate :zoo, :to => :bar
+ # end
+ #
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
+ #
+ # class Foo
+ # attr_accessor :bar
+ # def initialize(bar = nil)
+ # @bar = bar
+ # end
+ # delegate :zoo, :to => :bar, :allow_nil => true
+ # end
+ #
+ # Foo.new.zoo # returns nil
#
def delegate(*methods)
options = methods.pop
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index c08ad251dd..743db47bac 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -57,32 +57,23 @@ class Module
parents
end
- if RUBY_VERSION < '1.9'
- # Returns the constants that have been defined locally by this object and
- # not in an ancestor. This method is exact if running under Ruby 1.9. In
- # previous versions it may miss some constants if their definition in some
- # ancestor is identical to their definition in the receiver.
- def local_constants
- inherited = {}
-
- ancestors.each do |anc|
- next if anc == self
- anc.constants.each { |const| inherited[const] = anc.const_get(const) }
- end
-
- constants.select do |const|
- !inherited.key?(const) || inherited[const].object_id != const_get(const).object_id
- end
- end
- else
- def local_constants #:nodoc:
- constants(false)
- end
+ def local_constants #:nodoc:
+ constants(false)
end
- # Returns the names of the constants defined locally rather than the
- # constants themselves. See <tt>local_constants</tt>.
+ # *DEPRECATED*: Use +local_constants+ instead.
+ #
+ # Returns the names of the constants defined locally as strings.
+ #
+ # module M
+ # X = 1
+ # end
+ # M.local_constant_names # => ["X"]
+ #
+ # This method is useful for forward compatibility, since Ruby 1.8 returns
+ # constant names as strings, whereas 1.9 returns them as symbols.
def local_constant_names
+ ActiveSupport::Deprecation.warn('Module#local_constant_names is deprecated, use Module#local_constants instead', caller)
local_constants.map { |c| c.to_s }
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/method_names.rb b/activesupport/lib/active_support/core_ext/module/method_names.rb
deleted file mode 100644
index 2eb40a83ab..0000000000
--- a/activesupport/lib/active_support/core_ext/module/method_names.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class Module
- if instance_methods[0].is_a?(Symbol)
- def instance_method_names(*args)
- instance_methods(*args).map(&:to_s)
- end
-
- def method_names(*args)
- methods(*args).map(&:to_s)
- end
- else
- alias_method :instance_method_names, :instance_methods
- alias_method :method_names, :methods
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
index d1a0ee2f83..8adf050b6b 100644
--- a/activesupport/lib/active_support/core_ext/module/qualified_const.rb
+++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
@@ -23,26 +23,14 @@ end
# Object.const_get("::String") raises NameError and so does qualified_const_get.
#++
class Module
- if method(:const_defined?).arity == 1
- def qualified_const_defined?(path)
- QualifiedConstUtils.raise_if_absolute(path)
-
- QualifiedConstUtils.names(path).inject(self) do |mod, name|
- return unless mod.const_defined?(name)
- mod.const_get(name)
- end
- return true
- end
- else
- def qualified_const_defined?(path, search_parents=true)
- QualifiedConstUtils.raise_if_absolute(path)
+ def qualified_const_defined?(path, search_parents=true)
+ QualifiedConstUtils.raise_if_absolute(path)
- QualifiedConstUtils.names(path).inject(self) do |mod, name|
- return unless mod.const_defined?(name, search_parents)
- mod.const_get(name)
- end
- return true
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ return unless mod.const_defined?(name, search_parents)
+ mod.const_get(name)
end
+ return true
end
def qualified_const_get(path)
diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
deleted file mode 100644
index ed16c2f71b..0000000000
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'thread'
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/core_ext/array/extract_options'
-
-class Module
- # Synchronize access around a method, delegating synchronization to a
- # particular mutex. A mutex (either a Mutex, or any object that responds to
- # #synchronize and yields to a block) must be provided as a final :with option.
- # The :with option should be a symbol or string, and can represent a method,
- # constant, or instance or class variable.
- # Example:
- # class SharedCache
- # @@lock = Mutex.new
- # def expire
- # ...
- # end
- # synchronize :expire, :with => :@@lock
- # end
- def synchronize(*methods)
- options = methods.extract_options!
- unless options.is_a?(Hash) && with = options[:with]
- raise ArgumentError, "Synchronization needs a mutex. Supply an options hash with a :with key as the last argument (e.g. synchronize :hello, :with => :@mutex)."
- end
-
- methods.each do |method|
- aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
-
- if method_defined?("#{aliased_method}_without_synchronization#{punctuation}")
- raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported."
- end
-
- module_eval(<<-EOS, __FILE__, __LINE__ + 1)
- def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
- #{with}.synchronize do # @@lock.synchronize do
- #{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)
- end # end
- end # end
- EOS
-
- alias_method_chain method, :synchronization
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index fe27f45295..d67711f3b8 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -1,5 +1,4 @@
# encoding: utf-8
-require 'active_support/core_ext/string/encoding'
class Object
# An object is blank if it's false, empty, or a whitespace string.
@@ -89,9 +88,6 @@ class Hash
end
class String
- # 0x3000: fullwidth whitespace
- NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
-
# A string is blank if it's empty or contains whitespaces only:
#
# "".blank? # => true
@@ -100,12 +96,7 @@ class String
# " something here ".blank? # => false
#
def blank?
- # 1.8 does not takes [:space:] properly
- if encoding_aware?
- self !~ /[^[:space:]]/
- else
- self !~ NON_WHITESPACE_REGEXP
- end
+ self !~ /[^[:space:]]/
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb
index b5671f66d0..f611cdd606 100644
--- a/activesupport/lib/active_support/core_ext/object/inclusion.rb
+++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb
@@ -1,15 +1,25 @@
class Object
- # Returns true if this object is included in the argument. Argument must be
- # any object which responds to +#include?+. Usage:
+ # Returns true if this object is included in the argument(s). Argument must be
+ # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage:
#
# characters = ["Konata", "Kagami", "Tsukasa"]
# "Konata".in?(characters) # => true
+ #
+ # character = "Konata"
+ # character.in?("Konata", "Kagami", "Tsukasa") # => true
#
- # This will throw an ArgumentError if the argument doesn't respond
+ # This will throw an ArgumentError if a single argument is passed in and it doesn't respond
# to +#include?+.
- def in?(another_object)
- another_object.include?(self)
- rescue NoMethodError
- raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
+ def in?(*args)
+ if args.length > 1
+ args.include? self
+ else
+ another_object = args.first
+ if another_object.respond_to? :include?
+ another_object.include? self
+ else
+ raise ArgumentError.new("The single parameter passed to #in? must respond to #include?")
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
index eda9694614..91fdf93eb2 100644
--- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb
+++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
@@ -10,7 +10,7 @@ class Object
#
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
def instance_values #:nodoc:
- Hash[instance_variables.map { |name| [name.to_s[1..-1], instance_variable_get(name)] }]
+ Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
end
# Returns an array of instance variable names including "@". They are strings
@@ -23,11 +23,7 @@ class Object
# end
#
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
- if RUBY_VERSION >= '1.9'
- def instance_variable_names
- instance_variables.map { |var| var.to_s }
- end
- else
- alias_method :instance_variable_names, :instance_variables
+ def instance_variable_names
+ instance_variables.map { |var| var.to_s }
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb
index 14ef27340e..e7dc60a612 100644
--- a/activesupport/lib/active_support/core_ext/object/to_json.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_json.rb
@@ -10,10 +10,10 @@ end
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
- klass.class_eval <<-RUBY, __FILE__, __LINE__
+ klass.class_eval do
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
- RUBY
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/process.rb b/activesupport/lib/active_support/core_ext/process.rb
deleted file mode 100644
index 0b0bc6dc69..0000000000
--- a/activesupport/lib/active_support/core_ext/process.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'active_support/core_ext/process/daemon'
diff --git a/activesupport/lib/active_support/core_ext/process/daemon.rb b/activesupport/lib/active_support/core_ext/process/daemon.rb
deleted file mode 100644
index f5202ddee4..0000000000
--- a/activesupport/lib/active_support/core_ext/process/daemon.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Process
- def self.daemon(nochdir = nil, noclose = nil)
- exit if fork # Parent exits, child continues.
- Process.setsid # Become session leader.
- exit if fork # Zap session leader. See [1].
-
- unless nochdir
- Dir.chdir "/" # Release old working directory.
- end
-
- File.umask 0000 # Ensure sensible umask. Adjust as needed.
-
- unless noclose
- STDIN.reopen "/dev/null" # Free file descriptors and
- STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
- STDERR.reopen '/dev/null', 'a'
- end
-
- trap("TERM") { exit }
-
- return 0
- end unless respond_to?(:daemon)
-end
diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb
index 2428a02242..c0736f3a44 100644
--- a/activesupport/lib/active_support/core_ext/range.rb
+++ b/activesupport/lib/active_support/core_ext/range.rb
@@ -2,4 +2,3 @@ require 'active_support/core_ext/range/blockless_step'
require 'active_support/core_ext/range/conversions'
require 'active_support/core_ext/range/include_range'
require 'active_support/core_ext/range/overlaps'
-require 'active_support/core_ext/range/cover'
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 db42ef5c47..f687287f0d 100644
--- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb
+++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb
@@ -1,27 +1,11 @@
require 'active_support/core_ext/module/aliasing'
class Range
- begin
- (1..2).step
- # Range#step doesn't return an Enumerator
- rescue LocalJumpError
- # Return an array when step is called without a block.
- def step_with_blockless(*args, &block)
- if block_given?
- step_without_blockless(*args, &block)
- else
- array = []
- step_without_blockless(*args) { |step| array << step }
- array
- end
- end
- else
- def step_with_blockless(*args, &block) #:nodoc:
- if block_given?
- step_without_blockless(*args, &block)
- else
- step_without_blockless(*args).to_a
- end
+ def step_with_blockless(*args, &block) #:nodoc:
+ if block_given?
+ step_without_blockless(*args, &block)
+ else
+ step_without_blockless(*args).to_a
end
end
diff --git a/activesupport/lib/active_support/core_ext/range/cover.rb b/activesupport/lib/active_support/core_ext/range/cover.rb
deleted file mode 100644
index 3a182cddd2..0000000000
--- a/activesupport/lib/active_support/core_ext/range/cover.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class Range
- alias_method(:cover?, :include?) unless instance_methods.include?(:cover?)
-end
diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb
index 0246627467..684b7cbc4a 100644
--- a/activesupport/lib/active_support/core_ext/range/include_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/include_range.rb
@@ -9,9 +9,9 @@ class Range
# (5..9).include?(11) # => false
def include_with_range?(value)
if value.is_a?(::Range)
- operator = exclude_end? ? :< : :<=
- end_value = value.exclude_end? ? last.succ : last
- include_without_range?(value.first) && (value.last <=> end_value).send(operator, 0)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ include_without_range?(value.first) && value.last.send(operator, last)
else
include_without_range?(value)
end
diff --git a/activesupport/lib/active_support/core_ext/range/overlaps.rb b/activesupport/lib/active_support/core_ext/range/overlaps.rb
index 7df653b53f..603657c180 100644
--- a/activesupport/lib/active_support/core_ext/range/overlaps.rb
+++ b/activesupport/lib/active_support/core_ext/range/overlaps.rb
@@ -3,6 +3,6 @@ class Range
# (1..5).overlaps?(4..6) # => true
# (1..5).overlaps?(7..9) # => false
def overlaps?(other)
- include?(other.first) || other.include?(first)
+ cover?(other.first) || other.cover?(first)
end
end
diff --git a/activesupport/lib/active_support/core_ext/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb
deleted file mode 100644
index 0419ebc84b..0000000000
--- a/activesupport/lib/active_support/core_ext/rexml.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'active_support/core_ext/kernel/reporting'
-
-# Fixes the rexml vulnerability disclosed at:
-# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
-# This fix is identical to rexml-expansion-fix version 1.0.1.
-#
-# We still need to distribute this fix because albeit the REXML
-# in recent 1.8.7s is patched, it wasn't in early patchlevels.
-require 'rexml/rexml'
-
-# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION
-unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2"
- silence_warnings { require 'rexml/document' }
-
- # REXML in 1.8.7 has the patch but early patchlevels didn't update Version from 3.1.7.2.
- unless REXML::Document.respond_to?(:entity_expansion_limit=)
- silence_warnings { require 'rexml/entity' }
-
- module REXML #:nodoc:
- class Entity < Child #:nodoc:
- undef_method :unnormalized
- def unnormalized
- document.record_entity_expansion! if document
- v = value()
- return nil if v.nil?
- @unnormalized = Text::unnormalize(v, parent)
- @unnormalized
- end
- end
- class Document < Element #:nodoc:
- @@entity_expansion_limit = 10_000
- def self.entity_expansion_limit= val
- @@entity_expansion_limit = val
- end
-
- def record_entity_expansion!
- @number_of_expansions ||= 0
- @number_of_expansions += 1
- if @number_of_expansions > @@entity_expansion_limit
- raise "Number of entity expansions exceeded, processing aborted."
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index 72522d395c..ab49af55bf 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -9,6 +9,5 @@ require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/string/interpolation'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/exclude'
-require 'active_support/core_ext/string/encoding'
require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/inquiry'
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index c0d5cdf2d5..9b5266c58c 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -1,99 +1,35 @@
require "active_support/multibyte"
class String
- unless '1.9'.respond_to?(:force_encoding)
- # Returns the character at the +position+ treating the string as an array (where 0 is the first character).
- #
- # Examples:
- # "hello".at(0) # => "h"
- # "hello".at(4) # => "o"
- # "hello".at(10) # => ERROR if < 1.9, nil in 1.9
- def at(position)
- mb_chars[position, 1].to_s
- end
-
- # Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
- #
- # Examples:
- # "hello".from(0) # => "hello"
- # "hello".from(2) # => "llo"
- # "hello".from(10) # => "" if < 1.9, nil in 1.9
- def from(position)
- mb_chars[position..-1].to_s
- end
-
- # Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
- #
- # Examples:
- # "hello".to(0) # => "h"
- # "hello".to(2) # => "hel"
- # "hello".to(10) # => "hello"
- def to(position)
- mb_chars[0..position].to_s
- end
-
- # Returns the first character of the string or the first +limit+ characters.
- #
- # Examples:
- # "hello".first # => "h"
- # "hello".first(2) # => "he"
- # "hello".first(10) # => "hello"
- def first(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- mb_chars[0...limit].to_s
- end
- end
-
- # Returns the last character of the string or the last +limit+ characters.
- #
- # Examples:
- # "hello".last # => "o"
- # "hello".last(2) # => "lo"
- # "hello".last(10) # => "hello"
- def last(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- mb_chars[(-limit)..-1].to_s
- end
- end
- else
- def at(position)
- self[position]
- end
+ def at(position)
+ self[position]
+ end
- def from(position)
- self[position..-1]
- end
+ def from(position)
+ self[position..-1]
+ end
- def to(position)
- self[0..position]
- end
+ def to(position)
+ self[0..position]
+ end
- def first(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- to(limit - 1)
- end
+ def first(limit = 1)
+ if limit == 0
+ ''
+ elsif limit >= size
+ self
+ else
+ to(limit - 1)
end
+ end
- def last(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- from(-limit)
- end
+ def last(limit = 1)
+ if limit == 0
+ ''
+ elsif limit >= size
+ self
+ else
+ from(-limit)
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 0f8933b658..541f969faa 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -1,37 +1,7 @@
-# encoding: utf-8
require 'date'
-require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/core_ext/time/calculations'
class String
- # Returns the codepoint of the first character of the string, assuming a
- # single-byte character encoding:
- #
- # "a".ord # => 97
- # "à".ord # => 224, in ISO-8859-1
- #
- # This method is defined in Ruby 1.8 for Ruby 1.9 forward compatibility on
- # these character encodings.
- #
- # <tt>ActiveSupport::Multibyte::Chars#ord</tt> is forward compatible with
- # Ruby 1.9 on UTF8 strings:
- #
- # "a".mb_chars.ord # => 97
- # "à".mb_chars.ord # => 224, in UTF8
- #
- # Note that the 224 is different in both examples. In ISO-8859-1 "à" is
- # represented as a single byte, 224. In UTF8 it is represented with two
- # bytes, namely 195 and 160, but its Unicode codepoint is 224. If we
- # call +ord+ on the UTF8 string "à" the return value will be 195. That is
- # not an error, because UTF8 is unsupported, the call itself would be
- # bogus.
- def ord
- self[0]
- end unless method_defined?(:ord)
-
- # +getbyte+ backport from Ruby 1.9
- alias_method :getbyte, :[] unless method_defined?(:getbyte)
-
# Form can be either :utc (default) or :local.
def to_time(form = :utc)
return nil if self.blank?
diff --git a/activesupport/lib/active_support/core_ext/string/encoding.rb b/activesupport/lib/active_support/core_ext/string/encoding.rb
index d4781bfe0c..dc635ed6a5 100644
--- a/activesupport/lib/active_support/core_ext/string/encoding.rb
+++ b/activesupport/lib/active_support/core_ext/string/encoding.rb
@@ -1,11 +1,8 @@
+require 'active_support/deprecation'
+
class String
- if defined?(Encoding) && "".respond_to?(:encode)
- def encoding_aware?
- true
- end
- else
- def encoding_aware?
- false
- end
+ def encoding_aware?
+ ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated', caller
+ true
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index d478ee0ef6..1a34e88a87 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -36,14 +36,13 @@ class String
# "And they found that many people were sleeping better.".truncate(25, :omission => "... (continued)")
# # => "And they f... (continued)"
def truncate(length, options = {})
- text = self.dup
- options[:omission] ||= "..."
+ return self.dup unless self.length > length
- length_with_room_for_omission = length - options[:omission].mb_chars.length
- chars = text.mb_chars
+ options[:omission] ||= "..."
+ length_with_room_for_omission = length - options[:omission].length
stop = options[:separator] ?
- (chars.rindex(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission
+ (rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission
- (chars.length > length ? chars[0...stop] + options[:omission] : text).to_s
+ self[0...stop] + options[:omission]
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 1e57b586d9..2194dafe4d 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -100,8 +100,8 @@ class String
#
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
#
- # "ActiveRecord".underscore # => "active_record"
- # "ActiveRecord::Errors".underscore # => active_record/errors
+ # "ActiveModel".underscore # => "active_model"
+ # "ActiveModel::Errors".underscore # => "active_model/errors"
def underscore
ActiveSupport::Inflector.underscore(self)
end
diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb
index aae1cfccf2..4e7824ad74 100644
--- a/activesupport/lib/active_support/core_ext/string/multibyte.rb
+++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb
@@ -2,71 +2,55 @@
require 'active_support/multibyte'
class String
- if RUBY_VERSION >= "1.9"
- # == Multibyte proxy
- #
- # +mb_chars+ is a multibyte safe proxy for string methods.
- #
- # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
- # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
- # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
- #
- # name = 'Claus Müller'
- # name.reverse # => "rell??M sualC"
- # name.length # => 13
- #
- # name.mb_chars.reverse.to_s # => "rellüM sualC"
- # name.mb_chars.length # => 12
- #
- # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
- # it becomes easy to run one version of your code on multiple Ruby versions.
- #
- # == Method chaining
- #
- # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
- # method chaining on the result of any of these methods.
- #
- # name.mb_chars.reverse.length # => 12
- #
- # == Interoperability and configuration
- #
- # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
- # String and Char work like expected. The bang! methods change the internal string representation in the Chars
- # object. Interoperability problems can be resolved easily with a +to_s+ call.
- #
- # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
- # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
- def mb_chars
- if ActiveSupport::Multibyte.proxy_class.consumes?(self)
- ActiveSupport::Multibyte.proxy_class.new(self)
- else
- self
- end
- end
-
- def is_utf8? #:nodoc
- case encoding
- when Encoding::UTF_8
- valid_encoding?
- when Encoding::ASCII_8BIT, Encoding::US_ASCII
- dup.force_encoding(Encoding::UTF_8).valid_encoding?
- else
- false
- end
- end
- else
- def mb_chars
- if ActiveSupport::Multibyte.proxy_class.wants?(self)
- ActiveSupport::Multibyte.proxy_class.new(self)
- else
- self
- end
+ # == Multibyte proxy
+ #
+ # +mb_chars+ is a multibyte safe proxy for string methods.
+ #
+ # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
+ #
+ # name = 'Claus Müller'
+ # name.reverse # => "rell??M sualC"
+ # name.length # => 13
+ #
+ # name.mb_chars.reverse.to_s # => "rellüM sualC"
+ # name.mb_chars.length # => 12
+ #
+ # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
+ # it becomes easy to run one version of your code on multiple Ruby versions.
+ #
+ # == Method chaining
+ #
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
+ # method chaining on the result of any of these methods.
+ #
+ # name.mb_chars.reverse.length # => 12
+ #
+ # == Interoperability and configuration
+ #
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
+ #
+ # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
+ # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
+ def mb_chars
+ if ActiveSupport::Multibyte.proxy_class.consumes?(self)
+ ActiveSupport::Multibyte.proxy_class.new(self)
+ else
+ self
end
+ end
- # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
- # them), returns false otherwise.
- def is_utf8?
- ActiveSupport::Multibyte::Chars.consumes?(self)
+ def is_utf8?
+ case encoding
+ when Encoding::UTF_8
+ valid_encoding?
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
+ dup.force_encoding(Encoding::UTF_8).valid_encoding?
+ else
+ false
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 5d7f74bb65..dd780da157 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -5,6 +5,8 @@ class ERB
module Util
HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
+ HTML_ESCAPE_ONCE_REGEXP = /[\"><]|&(?!([a-zA-Z]+|(#\d+));)/
+ JSON_ESCAPE_REGEXP = /[&"><]/
# A utility method for escaping HTML tag characters.
# This method is also aliased as <tt>h</tt>.
@@ -20,7 +22,7 @@ class ERB
if s.html_safe?
s
else
- s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;").html_safe
+ s.encode(s.encoding, :xml => :attr)[1...-1].html_safe
end
end
@@ -33,6 +35,21 @@ class ERB
singleton_class.send(:remove_method, :html_escape)
module_function :html_escape
+ # A utility method for escaping HTML without affecting existing escaped entities.
+ #
+ # ==== Examples
+ # html_escape_once("1 < 2 &amp; 3")
+ # # => "1 &lt; 2 &amp; 3"
+ #
+ # html_escape_once("&lt;&lt; Accept & Checkout")
+ # # => "&lt;&lt; Accept &amp; Checkout"
+ def html_escape_once(s)
+ result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] }
+ s.html_safe? ? result.html_safe : result
+ end
+
+ module_function :html_escape_once
+
# A utility method for escaping HTML entities in JSON strings
# using \uXXXX JavaScript escape sequences for string literals:
#
@@ -51,7 +68,7 @@ class ERB
# <%=j @person.to_json %>
#
def json_escape(s)
- result = s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
+ result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
s.html_safe? ? result.html_safe : result
end
@@ -136,11 +153,6 @@ module ActiveSupport #:nodoc:
coder.represent_scalar nil, to_str
end
- def to_yaml(*args)
- return super() if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
- to_str.to_yaml(*args)
- end
-
UNSAFE_STRING_METHODS.each do |unsafe_method|
if 'String'.respond_to?(unsafe_method)
class_eval <<-EOT, __FILE__, __LINE__ + 1
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 372dd69212..4f300329f5 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -78,7 +78,7 @@ class Time
options[:hour] || hour,
options[:min] || (options[:hour] ? 0 : min),
options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec),
- options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : usec)
+ options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
)
end
@@ -161,27 +161,46 @@ class Time
months_since(1)
end
- # Returns a new Time representing the "start" of this week (Monday, 0:00)
- def beginning_of_week
- days_to_monday = wday!=0 ? wday-1 : 6
- (self - days_to_monday.days).midnight
+ # Returns number of days to start of this week, week starts on start_day (default is :monday).
+ def days_to_week_start(start_day = :monday)
+ start_day_number = DAYS_INTO_WEEK[start_day]
+ current_day_number = wday != 0 ? wday - 1 : 6
+ days_span = current_day_number - start_day_number
+ days_span >= 0 ? days_span : 7 + days_span
+ end
+
+ # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00).
+ def beginning_of_week(start_day = :monday)
+ days_to_start = days_to_week_start(start_day)
+ (self - days_to_start.days).midnight
end
- alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
- # Returns a new Time representing the end of this week, (end of Sunday)
- def end_of_week
- days_to_sunday = wday!=0 ? 7-wday : 0
- (self + days_to_sunday.days).end_of_day
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week
+ end
+
+ # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday).
+ def end_of_week(start_day = :monday)
+ days_to_end = 6 - days_to_week_start(start_day)
+ (self + days_to_end.days).end_of_day
end
alias :at_end_of_week :end_of_week
- # Returns a new Time representing the start of the given day in the previous week (default is Monday).
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week
+ end
+
+ # Returns a new Time representing the start of the given day in the previous week (default is :monday).
def prev_week(day = :monday)
ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
end
- # Returns a new Time representing the start of the given day in next week (default is Monday).
+ # Returns a new Time representing the start of the given day in next week (default is :monday).
def next_week(day = :monday)
since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
end
@@ -254,9 +273,9 @@ class Time
beginning_of_day..end_of_day
end
- # Returns a Range representing the whole week of the current time.
- def all_week
- beginning_of_week..end_of_week
+ # Returns a Range representing the whole week of the current time. Week starts on start_day (default is :monday, i.e. end of Sunday).
+ def all_week(start_day = :monday)
+ beginning_of_week(start_day)..end_of_week(start_day)
end
# Returns a Range representing the whole month of the current time.
@@ -312,4 +331,14 @@ class Time
end
alias_method :compare_without_coercion, :<=>
alias_method :<=>, :compare_with_coercion
+
+ # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
+ # can be eql? to an equivalent Time
+ def eql_with_coercion(other)
+ # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
+ other = other.comparable_time if other.respond_to?(:comparable_time)
+ eql_without_coercion(other)
+ end
+ alias_method :eql_without_coercion, :eql?
+ alias_method :eql?, :eql_with_coercion
end
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index 49ac18d245..0fdcd383f0 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -1,5 +1,4 @@
require 'active_support/inflector/methods'
-require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/values/time_zone'
class Time
@@ -54,32 +53,4 @@ class Time
def formatted_offset(colon = true, alternate_utc_string = nil)
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
-
- # Converts a Time object to a Date, dropping hour, minute, and second precision.
- #
- # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007
- # my_time.to_date # => Mon, 12 Nov 2007
- #
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
- # your_time.to_date # => Tue, 13 Jan 2009
- def to_date
- ::Date.new(year, month, day)
- 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 unless method_defined?(:to_time)
-
- # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
- #
- # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007
- # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500
- #
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
- # 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 unless method_defined?(:to_datetime)
end
diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb
index 457d3f5b62..1bf622d6a6 100644
--- a/activesupport/lib/active_support/core_ext/time/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/time/marshal.rb
@@ -1,30 +1,3 @@
-# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
-# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load
-# methods so that utc instances can be flagged on dump, and coerced back to utc on load.
-if !Marshal.load(Marshal.dump(Time.now.utc)).utc?
- class Time
- class << self
- alias_method :_load_without_utc_flag, :_load
- def _load(marshaled_time)
- time = _load_without_utc_flag(marshaled_time)
- time.instance_eval do
- if defined?(@marshal_with_utc_coercion)
- val = remove_instance_variable("@marshal_with_utc_coercion")
- end
- val ? utc : self
- end
- end
- end
-
- alias_method :_dump_without_utc_flag, :_dump
- def _dump(*args)
- obj = dup
- obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
- obj._dump_without_utc_flag(*args)
- end
- end
-end
-
# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
# preserves utc_offset. Preserve zone also, even though it may not
# work in some edge cases.
diff --git a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb b/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb
deleted file mode 100644
index e1878d3c20..0000000000
--- a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'date'
-
-class Time
- # Ruby 1.8-cvs and early 1.9 series define private Time#to_date
- %w(to_date to_datetime).each do |method|
- if private_instance_methods.include?(method) || private_instance_methods.include?(method.to_sym)
- public method
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index ee991e3439..bfe0832b37 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -1,22 +1,18 @@
# encoding: utf-8
-if RUBY_VERSION >= '1.9'
- require 'uri'
+require 'uri'
+str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
+parser = URI::Parser.new
- str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-
- parser = URI::Parser.new
-
- unless str == parser.unescape(parser.escape(str))
- URI::Parser.class_eval do
- remove_method :unescape
- def unescape(str, escaped = /%[a-fA-F\d]{2}/)
- # TODO: Are we actually sure that ASCII == UTF-8?
- # YK: My initial experiments say yes, but let's be sure please
- enc = str.encoding
- enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
- end
+unless str == parser.unescape(parser.escape(str))
+ URI::Parser.class_eval do
+ remove_method :unescape
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
+ # TODO: Are we actually sure that ASCII == UTF-8?
+ # YK: My initial experiments say yes, but let's be sure please
+ enc = str.encoding
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
+ str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
end
end
end
@@ -24,7 +20,7 @@ end
module URI
class << self
def parser
- @parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ @parser ||= URI::Parser.new
end
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index b3ac271778..2c5950edf5 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -71,14 +71,24 @@ module ActiveSupport #:nodoc:
#
# This is handled by walking back up the watch stack and adding the constants
# found by child.rb to the list of original constants in parent.rb
- class WatchStack < Hash
+ class WatchStack
+ include Enumerable
+
# @watching is a stack of lists of constants being watched. For instance,
# if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb
# then requires namespace/child.rb, the stack will look like [[Object], [Namespace]].
def initialize
@watching = []
- super { |h,k| h[k] = [] }
+ @stack = Hash.new { |h,k| h[k] = [] }
+ end
+
+ def each(&block)
+ @stack.each(&block)
+ end
+
+ def watching?
+ !@watching.empty?
end
# return a list of new constants found since the last call to watch_namespaces
@@ -89,20 +99,20 @@ module ActiveSupport #:nodoc:
@watching.last.each do |namespace|
# Retrieve the constants that were present under the namespace when watch_namespaces
# was originally called
- original_constants = self[namespace].last
+ original_constants = @stack[namespace].last
mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
next unless mod.is_a?(Module)
# Get a list of the constants that were added
- new_constants = mod.local_constant_names - original_constants
+ new_constants = mod.local_constants - original_constants
# self[namespace] returns an Array of the constants that are being evaluated
# for that namespace. For instance, if parent.rb requires child.rb, the first
# element of self[Object] will be an Array of the constants that were present
# before parent.rb was required. The second element will be an Array of the
# constants that were present before child.rb was required.
- self[namespace].each do |namespace_constants|
+ @stack[namespace].each do |namespace_constants|
namespace_constants.concat(new_constants)
end
@@ -123,16 +133,17 @@ module ActiveSupport #:nodoc:
namespaces.map do |namespace|
module_name = Dependencies.to_constant_name(namespace)
original_constants = Dependencies.qualified_const_defined?(module_name) ?
- Inflector.constantize(module_name).local_constant_names : []
+ Inflector.constantize(module_name).local_constants : []
watching << module_name
- self[module_name] << original_constants
+ @stack[module_name] << original_constants
end
@watching << watching
end
+ private
def pop_modules(modules)
- modules.each { |mod| self[mod].pop }
+ modules.each { |mod| @stack[mod].pop }
end
end
@@ -219,8 +230,8 @@ module ActiveSupport #:nodoc:
end
def load_dependency(file)
- if Dependencies.load?
- Dependencies.new_constants_in(Object) { yield }.presence
+ if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
+ Dependencies.new_constants_in(Object) { yield }
else
yield
end
@@ -229,13 +240,13 @@ module ActiveSupport #:nodoc:
raise
end
- def load(file, *)
+ def load(file, wrap = false)
result = false
load_dependency(file) { result = super }
result
end
- def require(file, *)
+ def require(file)
result = false
load_dependency(file) { result = super }
result
@@ -358,26 +369,12 @@ module ActiveSupport #:nodoc:
end
# Is the provided constant path defined?
- if Module.method(:const_defined?).arity == 1
- def qualified_const_defined?(path)
- Object.qualified_const_defined?(path.sub(/^::/, ''))
- end
- else
- def qualified_const_defined?(path)
- Object.qualified_const_defined?(path.sub(/^::/, ''), false)
- end
+ def qualified_const_defined?(path)
+ Object.qualified_const_defined?(path.sub(/^::/, ''), false)
end
- if Module.method(:const_defined?).arity == 1
- # Does this module define this constant?
- # Wrapper to accommodate changing Module#const_defined? in Ruby 1.9
- def local_const_defined?(mod, const)
- mod.const_defined?(const)
- end
- else
- def local_const_defined?(mod, const) #:nodoc:
- mod.const_defined?(const, false)
- end
+ def local_const_defined?(mod, const) #:nodoc:
+ mod.const_defined?(const, false)
end
# Given +path+, a filesystem path to a ruby file, return an array of constant
@@ -428,7 +425,7 @@ module ActiveSupport #:nodoc:
end
# Attempt to autoload the provided module name by searching for a directory
- # matching the expect path suffix. If found, the module is created and assigned
+ # matching the expected path suffix. If found, the module is created and assigned
# to +into+'s constants with the name +const_name+. Provided that the directory
# was loaded from a reloadable base path, it is added to the set of constants
# that are to be unloaded.
@@ -527,7 +524,7 @@ module ActiveSupport #:nodoc:
class ClassCache
def initialize
- @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
+ @store = Hash.new
end
def empty?
@@ -538,23 +535,24 @@ module ActiveSupport #:nodoc:
@store.key?(key)
end
- def []=(key, value)
- return unless key.respond_to?(:name)
-
- raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank?
-
- @store[key.name] = value
+ def get(key)
+ key = key.name if key.respond_to?(:name)
+ @store[key] ||= Inflector.constantize(key)
end
+ alias :[] :get
- def [](key)
+ def safe_get(key)
key = key.name if key.respond_to?(:name)
-
- @store[key]
+ @store[key] || begin
+ klass = Inflector.safe_constantize(key)
+ @store[key] = klass
+ end
end
- alias :get :[]
- def store(name)
- self[name] = name
+ def store(klass)
+ return self unless klass.respond_to?(:name)
+ raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
+ @store[klass.name] = klass
self
end
@@ -571,10 +569,17 @@ module ActiveSupport #:nodoc:
end
# Get the reference for class named +name+.
+ # Raises an exception if referenced class does not exist.
def constantize(name)
Reference.get(name)
end
+ # Get the reference for class named +name+ if one exists.
+ # Otherwise returns nil.
+ def safe_constantize(name)
+ Reference.safe_get(name)
+ end
+
# Determine if the given constant has been automatically loaded.
def autoloaded?(desc)
# No name => anonymous module.
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index f9505a247c..94f8d7133e 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -1,5 +1,4 @@
require "active_support/notifications"
-require "active_support/core_ext/array/wrap"
module ActiveSupport
module Deprecation
@@ -19,7 +18,7 @@ module ActiveSupport
# ActiveSupport::Deprecation.behavior = :stderr
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
def behavior=(behavior)
- @behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
+ @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
end
end
@@ -34,8 +33,8 @@ module ActiveSupport
if defined?(Rails) && Rails.logger
Rails.logger
else
- require 'logger'
- Logger.new($stderr)
+ require 'active_support/logger'
+ ActiveSupport::Logger.new($stderr)
end
logger.warn message
logger.debug callstack.join("\n ") if debug
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 89b0923882..00c67a470d 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -10,7 +10,6 @@ module ActiveSupport
# 1.month.ago # equivalent to Time.now.advance(:months => -1)
class Duration < BasicObject
attr_accessor :value, :parts
- delegate :duplicable?, :to => :value # required when using ActiveSupport's BasicObject on 1.8
def initialize(value, parts) #:nodoc:
@value, @parts = value, parts
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index f76ddff038..2ede084e95 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -1,8 +1,27 @@
+require "active_support/core_ext/array/extract_options"
+
module ActiveSupport
- # This class is responsible to track files and invoke the given block
- # whenever one of these files are changed. For example, this class
- # is used by Rails to reload the I18n framework whenever they are
- # changed upon a new request.
+ # \FileUpdateChecker specifies the API used by Rails to watch files
+ # and control reloading. The API depends on four methods:
+ #
+ # * +initialize+ which expects two parameters and one block as
+ # described below;
+ #
+ # * +updated?+ which returns a boolean if there were updates in
+ # the filesystem or not;
+ #
+ # * +execute+ which executes the given block on initialization
+ # and updates the counter to the latest timestamp;
+ #
+ # * +execute_if_updated+ which just executes the block if it was updated;
+ #
+ # After initialization, a call to +execute_if_updated+ must execute
+ # the block only if there was really a change in the filesystem.
+ #
+ # == Examples
+ #
+ # This class is used by Rails to reload the I18n framework whenever
+ # they are changed upon a new request.
#
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
# I18n.reload!
@@ -13,24 +32,89 @@ module ActiveSupport
# end
#
class FileUpdateChecker
- attr_reader :paths, :last_update_at
-
- def initialize(paths, calculate=false, &block)
- @paths = paths
+ # It accepts two parameters on initialization. The first is an array
+ # of files and the second is an optional hash of directories. The hash must
+ # have directories as keys and the value is an array of extensions to be
+ # watched under that directory.
+ #
+ # This method must also receive a block that will be called once a path changes.
+ #
+ # == Implementation details
+ #
+ # This particular implementation checks for added and updated files,
+ # but not removed files. Directories lookup are compiled to a glob for
+ # performance. Therefore, while someone can add new files to the +files+
+ # array after initialization (and parts of Rails do depend on this feature),
+ # adding new directories after initialization is not allowed.
+ #
+ # Notice that other objects that implements FileUpdateChecker API may
+ # not even allow new files to be added after initialization. If this
+ # is the case, we recommend freezing the +files+ after initialization to
+ # avoid changes that won't make effect.
+ def initialize(files, dirs={}, &block)
+ @files = files
+ @glob = compile_glob(dirs)
@block = block
- @last_update_at = calculate ? updated_at : nil
+ @updated_at = nil
+ @last_update_at = updated_at
+ end
+
+ # Check if any of the entries were updated. If so, the updated_at
+ # value is cached until the block is executed via +execute+ or +execute_if_updated+
+ def updated?
+ current_updated_at = updated_at
+ if @last_update_at < current_updated_at
+ @updated_at = updated_at
+ true
+ else
+ false
+ end
end
- def updated_at
- paths.map { |path| File.mtime(path) }.max
+ # Executes the given block and updates the counter to latest timestamp.
+ def execute
+ @last_update_at = updated_at
+ @block.call
+ ensure
+ @updated_at = nil
end
+ # Execute the block given if updated.
def execute_if_updated
- current_update_at = self.updated_at
- if @last_update_at != current_update_at
- @last_update_at = current_update_at
- @block.call
+ if updated?
+ execute
+ true
+ else
+ false
+ end
+ end
+
+ private
+
+ def updated_at #:nodoc:
+ @updated_at || begin
+ all = []
+ all.concat @files.select { |f| File.exists?(f) }
+ all.concat Dir[@glob] if @glob
+ all.map { |path| File.mtime(path) }.max || Time.at(0)
end
end
+
+ def compile_glob(hash) #:nodoc:
+ hash.freeze # Freeze so changes aren't accidently pushed
+ return if hash.empty?
+
+ globs = []
+ hash.each do |key, value|
+ globs << "#{key}/**/*#{compile_ext(value)}"
+ end
+ "{#{globs.join(",")}}"
+ end
+
+ def compile_ext(array) #:nodoc:
+ array = Array(array)
+ return if array.empty?
+ ".{#{array.join(",")}}"
+ end
end
end
diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb
index 9651f02c73..420b965c87 100644
--- a/activesupport/lib/active_support/gzip.rb
+++ b/activesupport/lib/active_support/gzip.rb
@@ -1,6 +1,5 @@
require 'zlib'
require 'stringio'
-require 'active_support/core_ext/string/encoding'
module ActiveSupport
# A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip.
@@ -8,7 +7,7 @@ module ActiveSupport
class Stream < StringIO
def initialize(*)
super
- set_encoding "BINARY" if "".encoding_aware?
+ set_encoding "BINARY"
end
def close; rewind; end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 636f019cd5..674e4acfd6 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -16,6 +16,10 @@ module ActiveSupport
dup
end
+ def nested_under_indifferent_access
+ self
+ end
+
def initialize(constructor = {})
if constructor.is_a?(Hash)
super()
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 4c59fe9ac9..bbeb8d82c6 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -10,14 +10,19 @@ module I18n
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
def self.reloader
- @reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
+ @reloader ||= ActiveSupport::FileUpdateChecker.new(reloader_paths){ I18n.reload! }
+ end
+
+ def self.reloader_paths
+ @reloader_paths ||= []
end
# Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
# point, no path was added to the reloader, I18n.reload! is not triggered
# on to_prepare callbacks. This will only happen on the config.after_initialize
# callback below.
- initializer "i18n.callbacks" do
+ initializer "i18n.callbacks" do |app|
+ app.reloaders << I18n::Railtie.reloader
ActionDispatch::Reloader.to_prepare do
I18n::Railtie.reloader.execute_if_updated
end
@@ -58,8 +63,8 @@ module I18n
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
- reloader.paths.concat I18n.load_path
- reloader.execute_if_updated
+ reloader_paths.concat I18n.load_path
+ reloader.execute
@i18n_inited = true
end
diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb
index daf2a1e1d9..b3eb1333ca 100644
--- a/activesupport/lib/active_support/inflections.rb
+++ b/activesupport/lib/active_support/inflections.rb
@@ -2,7 +2,7 @@ module ActiveSupport
Inflector.inflections do |inflect|
inflect.plural(/$/, 's')
inflect.plural(/s$/i, 's')
- inflect.plural(/(ax|test)is$/i, '\1es')
+ inflect.plural(/^(ax|test)is$/i, '\1es')
inflect.plural(/(octop|vir)us$/i, '\1i')
inflect.plural(/(octop|vir)i$/i, '\1i')
inflect.plural(/(alias|status)$/i, '\1es')
@@ -16,17 +16,18 @@ module ActiveSupport
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
- inflect.plural(/([m|l])ouse$/i, '\1ice')
- inflect.plural(/([m|l])ice$/i, '\1ice')
+ inflect.plural(/(m|l)ouse$/i, '\1ice')
+ inflect.plural(/(m|l)ice$/i, '\1ice')
inflect.plural(/^(ox)$/i, '\1en')
inflect.plural(/^(oxen)$/i, '\1')
inflect.plural(/(quiz)$/i, '\1zes')
inflect.singular(/s$/i, '')
+ inflect.singular(/(ss)$/i, '\1')
inflect.singular(/(n)ews$/i, '\1ews')
inflect.singular(/([ti])a$/i, '\1um')
- inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
- inflect.singular(/(^analy)ses$/i, '\1sis')
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1\2sis')
+ inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
inflect.singular(/([^f])ves$/i, '\1fe')
inflect.singular(/(hive)s$/i, '\1')
inflect.singular(/(tive)s$/i, '\1')
@@ -35,13 +36,14 @@ module ActiveSupport
inflect.singular(/(s)eries$/i, '\1eries')
inflect.singular(/(m)ovies$/i, '\1ovie')
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
- inflect.singular(/([m|l])ice$/i, '\1ouse')
- inflect.singular(/(bus)es$/i, '\1')
+ inflect.singular(/(m|l)ice$/i, '\1ouse')
+ inflect.singular(/(bus)(es)?$/i, '\1')
inflect.singular(/(o)es$/i, '\1')
inflect.singular(/(shoe)s$/i, '\1')
- inflect.singular(/(cris|ax|test)es$/i, '\1is')
- inflect.singular(/(octop|vir)i$/i, '\1us')
- inflect.singular(/(alias|status)es$/i, '\1')
+ inflect.singular(/(cris|test)(is|es)$/i, '\1is')
+ inflect.singular(/^(a)x[ie]s$/i, '\1xis')
+ inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
+ inflect.singular(/(alias|status)(es)?$/i, '\1')
inflect.singular(/^(ox)en/i, '\1')
inflect.singular(/(vert|ind)ices$/i, '\1ex')
inflect.singular(/(matr)ices$/i, '\1ix')
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 90bb62f57b..8cd96fe2d1 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/prepend_and_append'
+
module ActiveSupport
module Inflector
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
@@ -82,7 +84,7 @@ module ActiveSupport
def plural(rule, replacement)
@uncountables.delete(rule) if rule.is_a?(String)
@uncountables.delete(replacement)
- @plurals.insert(0, [rule, replacement])
+ @plurals.prepend([rule, replacement])
end
# Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
@@ -90,7 +92,7 @@ module ActiveSupport
def singular(rule, replacement)
@uncountables.delete(rule) if rule.is_a?(String)
@uncountables.delete(replacement)
- @singulars.insert(0, [rule, replacement])
+ @singulars.prepend([rule, replacement])
end
# Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
@@ -134,7 +136,7 @@ module ActiveSupport
# human /_cnt$/i, '\1_count'
# human "legacy_col_person_name", "Name"
def human(rule, replacement)
- @humans.insert(0, [rule, replacement])
+ @humans.prepend([rule, replacement])
end
# Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index e76ee60dd7..c630bd21b1 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -21,14 +21,7 @@ module ActiveSupport
# "words".pluralize # => "words"
# "CamelOctopus".pluralize # => "CamelOctopi"
def pluralize(word)
- result = word.to_s.dup
-
- if word.empty? || inflections.uncountables.include?(result.downcase)
- result
- else
- inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
- result
- end
+ apply_inflections(word, inflections.plurals)
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
@@ -40,14 +33,7 @@ module ActiveSupport
# "word".singularize # => "word"
# "CamelOctopi".singularize # => "CamelOctopus"
def singularize(word)
- result = word.to_s.dup
-
- if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
- result
- else
- inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
- result
- end
+ apply_inflections(word, inflections.singulars)
end
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
@@ -56,10 +42,10 @@ module ActiveSupport
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
#
# Examples:
- # "active_record".camelize # => "ActiveRecord"
- # "active_record".camelize(:lower) # => "activeRecord"
- # "active_record/errors".camelize # => "ActiveRecord::Errors"
- # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
+ # "active_model".camelize # => "ActiveModel"
+ # "active_model".camelize(:lower) # => "activeModel"
+ # "active_model/errors".camelize # => "ActiveModel::Errors"
+ # "active_model/errors".camelize(:lower) # => "activeModel::Errors"
#
# As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
# though there are cases where that does not hold:
@@ -80,8 +66,8 @@ module ActiveSupport
# Changes '::' to '/' to convert namespaces to paths.
#
# Examples:
- # "ActiveRecord".underscore # => "active_record"
- # "ActiveRecord::Errors".underscore # => active_record/errors
+ # "ActiveModel".underscore # => "active_model"
+ # "ActiveModel::Errors".underscore # => "active_model/errors"
#
# As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
# though there are cases where that does not hold:
@@ -106,9 +92,12 @@ module ActiveSupport
# "author_id" # => "Author"
def humanize(lower_case_and_underscored_word)
result = lower_case_and_underscored_word.to_s.dup
- inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
+ inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
result.gsub!(/_id$/, "")
- result.gsub(/(_)?([a-z\d]*)/i) { "#{$1 && ' '}#{inflections.acronyms[$2] || $2.downcase}" }.gsub(/^\w/) { $&.upcase }
+ result.tr!('_', ' ')
+ result.gsub(/([a-z\d]*)/i) { |match|
+ "#{inflections.acronyms[match] || match.downcase}"
+ }.gsub(/^\w/) { $&.upcase }
end
# Capitalizes all the words and replaces some characters in the string to create
@@ -157,7 +146,7 @@ module ActiveSupport
# Example:
# "puni_puni" # => "puni-puni"
def dasherize(underscored_word)
- underscored_word.gsub(/_/, '-')
+ underscored_word.tr('_', '-')
end
# Removes the module part from the expression in the string:
@@ -200,47 +189,32 @@ module ActiveSupport
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
end
- # Ruby 1.9 introduces an inherit argument for Module#const_get and
- # #const_defined? and changes their default behavior.
- if Module.method(:const_get).arity == 1
- # Tries to find a constant with the name specified in the argument string:
- #
- # "Module".constantize # => Module
- # "Test::Unit".constantize # => Test::Unit
- #
- # The name is assumed to be the one of a top-level constant, no matter whether
- # it starts with "::" or not. No lexical context is taken into account:
- #
- # C = 'outside'
- # module M
- # C = 'inside'
- # C # => 'inside'
- # "C".constantize # => 'outside', same as ::C
- # end
- #
- # NameError is raised when the name is not in CamelCase or the constant is
- # unknown.
- def constantize(camel_cased_word)
- names = camel_cased_word.split('::')
- names.shift if names.empty? || names.first.empty?
-
- constant = Object
- names.each do |name|
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
- end
- constant
- end
- else
- def constantize(camel_cased_word) #:nodoc:
- names = camel_cased_word.split('::')
- names.shift if names.empty? || names.first.empty?
+ # Tries to find a constant with the name specified in the argument string:
+ #
+ # "Module".constantize # => Module
+ # "Test::Unit".constantize # => Test::Unit
+ #
+ # The name is assumed to be the one of a top-level constant, no matter whether
+ # it starts with "::" or not. No lexical context is taken into account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # "C".constantize # => 'outside', same as ::C
+ # end
+ #
+ # NameError is raised when the name is not in CamelCase or the constant is
+ # unknown.
+ def constantize(camel_cased_word) #:nodoc:
+ names = camel_cased_word.split('::')
+ names.shift if names.empty? || names.first.empty?
- constant = Object
- names.each do |name|
- constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
- end
- constant
+ constant = Object
+ names.each do |name|
+ constant = constant.const_get(name, false)
end
+ constant
end
# Tries to find a constant with the name specified in the argument string:
@@ -269,13 +243,36 @@ module ActiveSupport
begin
constantize(camel_cased_word)
rescue NameError => e
- raise unless e.message =~ /uninitialized constant #{const_regexp(camel_cased_word)}$/ ||
+ raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
e.name.to_s == camel_cased_word.to_s
rescue ArgumentError => e
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
end
end
+ # Returns the suffix that should be added to a number to denote the position
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
+ #
+ # Examples:
+ # ordinal(1) # => "st"
+ # ordinal(2) # => "nd"
+ # ordinal(1002) # => "nd"
+ # ordinal(1003) # => "rd"
+ # ordinal(-11) # => "th"
+ # ordinal(-1021) # => "st"
+ def ordinal(number)
+ if (11..13).include?(number.to_i.abs % 100)
+ "th"
+ else
+ case number.to_i.abs % 10
+ when 1; "st"
+ when 2; "nd"
+ when 3; "rd"
+ else "th"
+ end
+ end
+ end
+
# Turns a number into an ordinal string used to denote the position in an
# ordered sequence such as 1st, 2nd, 3rd, 4th.
#
@@ -287,16 +284,7 @@ module ActiveSupport
# ordinalize(-11) # => "-11th"
# ordinalize(-1021) # => "-1021st"
def ordinalize(number)
- if (11..13).include?(number.to_i.abs % 100)
- "#{number}th"
- else
- case number.to_i.abs % 10
- when 1; "#{number}st"
- when 2; "#{number}nd"
- when 3; "#{number}rd"
- else "#{number}th"
- end
- end
+ "#{number}#{ordinal(number)}"
end
private
@@ -311,5 +299,21 @@ module ActiveSupport
part.empty? ? acc : "#{part}(::#{acc})?"
end
end
+
+ # Applies inflection rules for +singularize+ and +pluralize+.
+ #
+ # Examples:
+ # apply_inflections("post", inflections.plurals) # => "posts"
+ # apply_inflections("posts", inflections.singulars) # => "post"
+ def apply_inflections(word, rules)
+ result = word.to_s.dup
+
+ if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
+ result
+ else
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
+ result
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 469ae69258..b2adfea273 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -1,11 +1,9 @@
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/module/delegation'
require 'active_support/json/variable'
-require 'active_support/ordered_hash'
require 'bigdecimal'
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/instance_variables'
@@ -50,9 +48,9 @@ module ActiveSupport
end
# like encode, but only calls as_json, without encoding to string
- def as_json(value)
+ def as_json(value, use_options = true)
check_for_circular_references(value) do
- value.as_json(options_for(value))
+ use_options ? value.as_json(options_for(value)) : value.as_json
end
end
@@ -119,9 +117,7 @@ module ActiveSupport
end
def escape(string)
- if string.respond_to?(:force_encoding)
- string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
- end
+ string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
json = string.
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
gsub(/([\xC0-\xDF][\x80-\xBF]|
@@ -130,7 +126,7 @@ module ActiveSupport
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
}
json = %("#{json}")
- json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
+ json.force_encoding(::Encoding::UTF_8)
json
end
end
@@ -151,8 +147,8 @@ class Object
end
end
-class Struct
- def as_json(options = nil) #:nodoc:
+class Struct #:nodoc:
+ def as_json(options = nil)
Hash[members.zip(values)]
end
end
@@ -208,11 +204,15 @@ module Enumerable
end
end
+class Range
+ def as_json(options = nil) to_s end #:nodoc:
+end
+
class Array
def as_json(options = nil) #:nodoc:
# use encoder as a proxy to call as_json on all elements, to protect from circular references
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
- map { |v| encoder.as_json(v) }
+ map { |v| encoder.as_json(v, options) }
end
def encode_json(encoder) #:nodoc:
@@ -226,9 +226,9 @@ class Hash
# create a subset of the hash by applying :only or :except
subset = if options
if attrs = options[:only]
- slice(*Array.wrap(attrs))
+ slice(*Array(attrs))
elsif attrs = options[:except]
- except(*Array.wrap(attrs))
+ except(*Array(attrs))
else
self
end
@@ -238,8 +238,7 @@ class Hash
# use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
- result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash
- result[subset.map { |k, v| [k.to_s, encoder.as_json(v)] }]
+ Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
end
def encode_json(encoder)
diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index 6296c1d4b8..58938cdc3d 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -92,7 +92,7 @@ module ActiveSupport
begin
send(method, ActiveSupport::Notifications::Event.new(message, *args))
rescue Exception => e
- logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message}"
+ logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
end
end
@@ -100,9 +100,9 @@ module ActiveSupport
%w(info debug warn error fatal unknown).each do |level|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
- def #{level}(*args, &block)
+ def #{level}(progname = nil, &block)
return unless logger
- logger.#{level}(*args, &block)
+ logger.#{level}(progname, &block)
end
METHOD
end
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index dcfcf0b63c..7b7fc81e6c 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -50,7 +50,7 @@ module ActiveSupport
end
class MockLogger
- include ActiveSupport::BufferedLogger::Severity
+ include ActiveSupport::Logger::Severity
attr_reader :flush_count
attr_accessor :level
@@ -73,7 +73,7 @@ module ActiveSupport
@flush_count += 1
end
- ActiveSupport::BufferedLogger::Severity.constants.each do |severity|
+ ActiveSupport::Logger::Severity.constants.each do |severity|
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}?
#{severity} >= @level
diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb
new file mode 100644
index 0000000000..d055767eab
--- /dev/null
+++ b/activesupport/lib/active_support/logger.rb
@@ -0,0 +1,53 @@
+require 'logger'
+
+module ActiveSupport
+ class Logger < ::Logger
+ # Broadcasts logs to multiple loggers
+ def self.broadcast(logger) # :nodoc:
+ Module.new do
+ define_method(:add) do |*args, &block|
+ logger.add(*args, &block)
+ super(*args, &block)
+ end
+
+ define_method(:<<) do |x|
+ logger << x
+ super(x)
+ end
+
+ define_method(:close) do
+ logger.close
+ super()
+ end
+
+ define_method(:progname=) do |name|
+ logger.progname = name
+ super(name)
+ end
+
+ define_method(:formatter=) do |formatter|
+ logger.formatter = formatter
+ super(formatter)
+ end
+
+ define_method(:level=) do |level|
+ logger.level = level
+ super(level)
+ end
+ end
+ end
+
+ def initialize(*args)
+ super
+ @formatter = SimpleFormatter.new
+ end
+
+ # Simple formatter which only displays the message.
+ class SimpleFormatter < ::Logger::Formatter
+ # This method is invoked when a log event occurs
+ def call(severity, timestamp, progname, msg)
+ "#{String === msg ? msg : msg.inspect}\n"
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
deleted file mode 100644
index 4c67676ad5..0000000000
--- a/activesupport/lib/active_support/memoizable.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/deprecation'
-
-module ActiveSupport
- module Memoizable
- def self.extended(base)
- ActiveSupport::Deprecation.warn "ActiveSupport::Memoizable is deprecated and will be removed in future releases," \
- "simply use Ruby memoization pattern instead.", caller
- super
- end
-
- def self.memoized_ivar_for(symbol)
- "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
- end
-
- module InstanceMethods
- def self.included(base)
- base.class_eval do
- unless base.method_defined?(:freeze_without_memoizable)
- alias_method_chain :freeze, :memoizable
- end
- end
- end
-
- def freeze_with_memoizable
- memoize_all unless frozen?
- freeze_without_memoizable
- end
-
- def memoize_all
- prime_cache ".*"
- end
-
- def unmemoize_all
- flush_cache ".*"
- end
-
- def prime_cache(*syms)
- syms.each do |sym|
- methods.each do |m|
- if m.to_s =~ /^_unmemoized_(#{sym})/
- if method(m).arity == 0
- __send__($1)
- else
- ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
- instance_variable_set(ivar, {})
- end
- end
- end
- end
- end
-
- def flush_cache(*syms)
- syms.each do |sym|
- (methods + private_methods + protected_methods).each do |m|
- if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/
- ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
- instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
- end
- end
- end
- end
- end
-
- def memoize(*symbols)
- symbols.each do |symbol|
- original_method = :"_unmemoized_#{symbol}"
- memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
-
- class_eval <<-EOS, __FILE__, __LINE__ + 1
- include InstanceMethods # include InstanceMethods
- #
- if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
- raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
- end # end
- alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
- #
- if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
- def #{symbol}(reload = false) # def mime_type(reload = false)
- if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
- #{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type]
- end # end
- #{memoized_ivar}[0] # @_memoized_mime_type[0]
- end # end
- else # else
- def #{symbol}(*args) # def mime_type(*args)
- #{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
- args_length = method(:#{original_method}).arity # args_length = method(:_unmemoized_mime_type).arity
- if args.length == args_length + 1 && # if args.length == args_length + 1 &&
- (args.last == true || args.last == :reload) # (args.last == true || args.last == :reload)
- reload = args.pop # reload = args.pop
- end # end
- #
- if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
- if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
- #{memoized_ivar}[args] # @_memoized_mime_type[args]
- elsif #{memoized_ivar} # elsif @_memoized_mime_type
- #{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args)
- end # end
- else # else
- #{original_method}(*args) # _unmemoized_mime_type(*args)
- end # end
- end # end
- end # end
- #
- if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
- private #{symbol.inspect} # private :mime_type
- elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
- protected #{symbol.inspect} # protected :mime_type
- end # end
- EOS
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index e14386a85d..ada2e79ccb 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -1,5 +1,5 @@
require 'openssl'
-require 'active_support/base64'
+require 'base64'
module ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored somewhere
@@ -9,22 +9,56 @@ module ActiveSupport
#
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but where you don't
# want users to be able to determine the value of the payload.
+ #
+ # key = OpenSSL::Digest::SHA256.new('password').digest # => "\x89\xE0\x156\xAC..."
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
class MessageEncryptor
+ module NullSerializer #:nodoc:
+ def self.load(value)
+ value
+ end
+
+ def self.dump(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
+ # Initialize a new MessageEncryptor.
+ # +secret+ must be at least as long as the cipher key size. For the default 'aes-256-cbc' cipher,
+ # this is 256 bits. If you are using a user-entered secret, you can generate a suitable key with
+ # <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or similar.
+ #
+ # Options:
+ # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'
+ # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
+ #
def initialize(secret, options = {})
- unless options.is_a?(Hash)
- ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :cipher => 'algorithm' to specify the cipher algorithm."
- options = { :cipher => options }
- end
-
@secret = secret
@cipher = options[:cipher] || 'aes-256-cbc'
+ @verifier = MessageVerifier.new(@secret, :serializer => NullSerializer)
@serializer = options[:serializer] || Marshal
end
- def encrypt(value)
+ # Encrypt and sign a message. We need to sign the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def encrypt_and_sign(value)
+ verifier.generate(_encrypt(value))
+ end
+
+ # Decrypt and verify a message. We need to verify the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def decrypt_and_verify(value)
+ _decrypt(verifier.verify(value))
+ end
+
+ private
+
+ def _encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
@@ -36,12 +70,12 @@ module ActiveSupport
encrypted_data = cipher.update(@serializer.dump(value))
encrypted_data << cipher.final
- [encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
+ [encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
end
- def decrypt(encrypted_message)
+ def _decrypt(encrypted_message)
cipher = new_cipher
- encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
+ encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.decode64(v)}
cipher.decrypt
cipher.key = @secret
@@ -55,23 +89,12 @@ module ActiveSupport
raise InvalidMessage
end
- def encrypt_and_sign(value)
- verifier.generate(encrypt(value))
+ def new_cipher
+ OpenSSL::Cipher::Cipher.new(@cipher)
end
- def decrypt_and_verify(value)
- decrypt(verifier.verify(value))
+ def verifier
+ @verifier
end
-
-
-
- private
- def new_cipher
- OpenSSL::Cipher::Cipher.new(@cipher)
- end
-
- def verifier
- MessageVerifier.new(@secret)
- end
end
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 9d7c81142a..3b27089fa0 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -1,4 +1,4 @@
-require 'active_support/base64'
+require 'base64'
require 'active_support/core_ext/object/blank'
module ActiveSupport
@@ -18,7 +18,7 @@ module ActiveSupport
# self.current_user = User.find(id)
# end
#
- # By default it uses Marshal to serialize the message. If you want to use another
+ # By default it uses Marshal to serialize the message. If you want to use another
# serialization method, you can set the serializer attribute to something that responds
# to dump and load, e.g.:
#
@@ -27,11 +27,6 @@ module ActiveSupport
class InvalidSignature < StandardError; end
def initialize(secret, options = {})
- unless options.is_a?(Hash)
- ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :digest => 'algorithm' to specify the digest algorithm."
- options = { :digest => options }
- end
-
@secret = secret
@digest = options[:digest] || 'SHA1'
@serializer = options[:serializer] || Marshal
@@ -42,14 +37,14 @@ module ActiveSupport
data, digest = signed_message.split("--")
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
- @serializer.load(ActiveSupport::Base64.decode64(data))
+ @serializer.load(::Base64.decode64(data))
else
raise InvalidSignature
end
end
def generate(value)
- data = ActiveSupport::Base64.encode64s(@serializer.dump(value))
+ data = ::Base64.strict_encode64(@serializer.dump(value))
"#{data}--#{generate_digest(data)}"
end
diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb
index 57e8e24bf4..5efe13c537 100644
--- a/activesupport/lib/active_support/multibyte.rb
+++ b/activesupport/lib/active_support/multibyte.rb
@@ -1,9 +1,5 @@
-# encoding: utf-8
-require 'active_support/core_ext/module/attribute_accessors'
-
module ActiveSupport #:nodoc:
module Multibyte
- autoload :EncodingError, 'active_support/multibyte/exceptions'
autoload :Chars, 'active_support/multibyte/chars'
autoload :Unicode, 'active_support/multibyte/unicode'
@@ -21,24 +17,5 @@ module ActiveSupport #:nodoc:
def self.proxy_class
@proxy_class ||= ActiveSupport::Multibyte::Chars
end
-
- # Regular expressions that describe valid byte sequences for a character
- VALID_CHARACTER = {
- # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
- 'UTF-8' => /\A(?:
- [\x00-\x7f] |
- [\xc2-\xdf] [\x80-\xbf] |
- \xe0 [\xa0-\xbf] [\x80-\xbf] |
- [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
- \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
- [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
- \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn,
- # Quick check for valid Shift-JIS characters, disregards the odd-even pairing
- 'Shift_JIS' => /\A(?:
- [\x00-\x7e\xa1-\xdf] |
- [\x81-\x9f\xe0-\xef] [\x40-\x7e\x80-\x9e\x9f-\xfc])\z /xn
- }
end
-end
-
-require 'active_support/multibyte/utils' \ No newline at end of file
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index b78d92f599..9a748dfa60 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -1,6 +1,8 @@
# encoding: utf-8
+require 'active_support/json'
require 'active_support/core_ext/string/access'
require 'active_support/core_ext/string/behavior'
+require 'active_support/core_ext/module/delegation'
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
@@ -34,27 +36,24 @@ module ActiveSupport #:nodoc:
#
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
class Chars
+ include Comparable
attr_reader :wrapped_string
alias to_s wrapped_string
alias to_str wrapped_string
- if RUBY_VERSION >= "1.9"
- # Creates a new Chars instance by wrapping _string_.
- def initialize(string)
- @wrapped_string = string
- @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
- end
- else
- def initialize(string) #:nodoc:
- @wrapped_string = string
- end
+ delegate :<=>, :=~, :acts_like_string?, :to => :wrapped_string
+
+ # Creates a new Chars instance by wrapping _string_.
+ def initialize(string)
+ @wrapped_string = string
+ @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
end
# Forward all undefined methods to the wrapped string.
def method_missing(method, *args, &block)
if method.to_s =~ /!$/
- @wrapped_string.__send__(method, *args, &block)
- self
+ result = @wrapped_string.__send__(method, *args, &block)
+ self if result
else
result = @wrapped_string.__send__(method, *args, &block)
result.kind_of?(String) ? chars(result) : result
@@ -67,178 +66,9 @@ module ActiveSupport #:nodoc:
super || @wrapped_string.respond_to?(method, include_private)
end
- # Enable more predictable duck-typing on String-like classes. See Object#acts_like?.
- def acts_like_string?
- true
- end
-
# Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
def self.consumes?(string)
- # Unpack is a little bit faster than regular expressions.
- string.unpack('U*')
- true
- rescue ArgumentError
- false
- end
-
- include Comparable
-
- # Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
- # equal or after the object on the right side of the operation. It accepts any object
- # that implements +to_s+:
- #
- # 'é'.mb_chars <=> 'ü'.mb_chars # => -1
- #
- # See <tt>String#<=></tt> for more details.
- def <=>(other)
- @wrapped_string <=> other.to_s
- end
-
- if RUBY_VERSION < "1.9"
- # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
- # +false+ otherwise.
- def self.wants?(string)
- $KCODE == 'UTF8' && consumes?(string)
- end
-
- # Returns a new Chars object containing the _other_ object concatenated to the string.
- #
- # Example:
- # ('Café'.mb_chars + ' périferôl').to_s # => "Café périferôl"
- def +(other)
- chars(@wrapped_string + other)
- end
-
- # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
- #
- # Example:
- # 'Café périferôl'.mb_chars =~ /ô/ # => 12
- def =~(other)
- translate_offset(@wrapped_string =~ other)
- end
-
- # Inserts the passed string at specified codepoint offsets.
- #
- # Example:
- # 'Café'.mb_chars.insert(4, ' périferôl').to_s # => "Café périferôl"
- def insert(offset, fragment)
- unpacked = Unicode.u_unpack(@wrapped_string)
- unless offset > unpacked.length
- @wrapped_string.replace(
- Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
- )
- else
- raise IndexError, "index #{offset} out of string"
- end
- self
- end
-
- # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
- #
- # Example:
- # 'Café'.mb_chars.include?('é') # => true
- def include?(other)
- # We have to redefine this method because Enumerable defines it.
- @wrapped_string.include?(other)
- end
-
- # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
- #
- # Example:
- # 'Café périferôl'.mb_chars.index('ô') # => 12
- # 'Café périferôl'.mb_chars.index(/\w/u) # => 0
- def index(needle, offset=0)
- wrapped_offset = first(offset).wrapped_string.length
- index = @wrapped_string.index(needle, wrapped_offset)
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
- end
-
- # Returns the position _needle_ in the string, counting in
- # codepoints, searching backward from _offset_ or the end of the
- # string. Returns +nil+ if _needle_ isn't found.
- #
- # Example:
- # 'Café périferôl'.mb_chars.rindex('é') # => 6
- # 'Café périferôl'.mb_chars.rindex(/\w/u) # => 13
- def rindex(needle, offset=nil)
- offset ||= length
- wrapped_offset = first(offset).wrapped_string.length
- index = @wrapped_string.rindex(needle, wrapped_offset)
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
- end
-
- # Returns the number of codepoints in the string
- def size
- Unicode.u_unpack(@wrapped_string).size
- end
- alias_method :length, :size
-
- # Strips entire range of Unicode whitespace from the right of the string.
- def rstrip
- chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
- end
-
- # Strips entire range of Unicode whitespace from the left of the string.
- def lstrip
- chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
- end
-
- # Strips entire range of Unicode whitespace from the right and left of the string.
- def strip
- rstrip.lstrip
- end
-
- # Returns the codepoint of the first character in the string.
- #
- # Example:
- # 'こんにちは'.mb_chars.ord # => 12371
- def ord
- Unicode.u_unpack(@wrapped_string)[0]
- end
-
- # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
- #
- # Example:
- #
- # "¾ cup".mb_chars.rjust(8).to_s
- # # => " ¾ cup"
- #
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
- # # => "   ¾ cup"
- def rjust(integer, padstr=' ')
- justify(integer, :right, padstr)
- end
-
- # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
- #
- # Example:
- #
- # "¾ cup".mb_chars.rjust(8).to_s
- # # => "¾ cup "
- #
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
- # # => "¾ cup   "
- def ljust(integer, padstr=' ')
- justify(integer, :left, padstr)
- end
-
- # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
- #
- # Example:
- #
- # "¾ cup".mb_chars.center(8).to_s
- # # => " ¾ cup "
- #
- # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace
- # # => " ¾ cup  "
- def center(integer, padstr=' ')
- justify(integer, :center, padstr)
- end
-
- else
- def =~(other)
- @wrapped_string =~ other
- end
+ string.encoding == Encoding::UTF_8
end
# Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
@@ -250,45 +80,10 @@ module ActiveSupport #:nodoc:
@wrapped_string.split(*args).map { |i| i.mb_chars }
end
- # Like <tt>String#[]=</tt>, except instead of byte offsets you specify character offsets.
- #
- # Example:
- #
- # s = "Müller"
- # s.mb_chars[2] = "e" # Replace character with offset 2
- # s
- # # => "Müeler"
- #
- # s = "Müller"
- # s.mb_chars[1, 2] = "ö" # Replace 2 characters at character offset 1
- # s
- # # => "Möler"
- def []=(*args)
- replace_by = args.pop
- # Indexed replace with regular expressions already works
- if args.first.is_a?(Regexp)
- @wrapped_string[*args] = replace_by
- else
- result = Unicode.u_unpack(@wrapped_string)
- case args.first
- when Fixnum
- raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
- min = args[0]
- max = args[1].nil? ? min : (min + args[1] - 1)
- range = Range.new(min, max)
- replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
- when Range
- raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
- range = args[0]
- else
- needle = args[0].to_s
- min = index(needle)
- max = min + Unicode.u_unpack(needle).length - 1
- range = Range.new(min, max)
- end
- result[range] = Unicode.u_unpack(replace_by)
- @wrapped_string.replace(result.pack('U*'))
- end
+ # Works like like <tt>String#slice!</tt>, but returns an instance of Chars, or nil if the string was not
+ # modified.
+ def slice!(*args)
+ chars(@wrapped_string.slice!(*args))
end
# Reverses all characters in the string.
@@ -296,38 +91,10 @@ module ActiveSupport #:nodoc:
# Example:
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
def reverse
- chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
+ chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*'))
end
- # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
- # character.
- #
- # Example:
- # 'こんにちは'.mb_chars.slice(2..3).to_s # => "にち"
- def slice(*args)
- if args.size > 2
- raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
- elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
- raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
- elsif (args.size == 2 && !args[1].is_a?(Numeric))
- raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
- elsif args[0].kind_of? Range
- cps = Unicode.u_unpack(@wrapped_string).slice(*args)
- result = cps.nil? ? nil : cps.pack('U*')
- elsif args[0].kind_of? Regexp
- result = @wrapped_string.slice(*args)
- elsif args.size == 1 && args[0].kind_of?(Numeric)
- character = Unicode.u_unpack(@wrapped_string)[args[0]]
- result = character && [character].pack('U')
- else
- cps = Unicode.u_unpack(@wrapped_string).slice(*args)
- result = cps && cps.pack('U*')
- end
- result && chars(result)
- end
- alias_method :[], :slice
-
- # Limit the byte size of the string to a number of bytes without breaking characters. Usable
+ # Limits the byte size of the string to a number of bytes without breaking characters. Usable
# when the storage for a string is limited for some reason.
#
# Example:
@@ -336,20 +103,28 @@ module ActiveSupport #:nodoc:
slice(0...translate_offset(limit))
end
- # Convert characters in the string to uppercase.
+ # Converts characters in the string to uppercase.
#
# Example:
# 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
def upcase
- chars(Unicode.apply_mapping @wrapped_string, :uppercase_mapping)
+ chars Unicode.upcase(@wrapped_string)
end
- # Convert characters in the string to lowercase.
+ # Converts characters in the string to lowercase.
#
# Example:
# 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
def downcase
- chars(Unicode.apply_mapping @wrapped_string, :lowercase_mapping)
+ chars Unicode.downcase(@wrapped_string)
+ end
+
+ # Converts characters in the string to the opposite case.
+ #
+ # Example:
+ # 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN"
+ def swapcase
+ chars Unicode.swapcase(@wrapped_string)
end
# Converts the first character to uppercase and the remainder to lowercase.
@@ -366,7 +141,7 @@ module ActiveSupport #:nodoc:
# "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
# "日本語".mb_chars.titleize # => "日本語"
def titleize
- chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.apply_mapping $1, :uppercase_mapping })
+ chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.upcase($1)})
end
alias_method :titlecase, :titleize
@@ -386,7 +161,7 @@ module ActiveSupport #:nodoc:
# 'é'.length # => 2
# 'é'.mb_chars.decompose.to_s.length # => 3
def decompose
- chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
+ chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack('U*'))
end
# Performs composition on all the characters.
@@ -395,16 +170,16 @@ module ActiveSupport #:nodoc:
# 'é'.length # => 3
# 'é'.mb_chars.compose.to_s.length # => 2
def compose
- chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
+ chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack('U*'))
end
# Returns the number of grapheme clusters in the string.
#
# Example:
# 'क्षि'.mb_chars.length # => 4
- # 'क्षि'.mb_chars.g_length # => 3
- def g_length
- Unicode.g_unpack(@wrapped_string).length
+ # 'क्षि'.mb_chars.grapheme_length # => 3
+ def grapheme_length
+ Unicode.unpack_graphemes(@wrapped_string).length
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
@@ -414,14 +189,14 @@ module ActiveSupport #:nodoc:
chars(Unicode.tidy_bytes(@wrapped_string, force))
end
- %w(capitalize downcase lstrip reverse rstrip slice strip tidy_bytes upcase).each do |method|
- # Only define a corresponding bang method for methods defined in the proxy; On 1.9 the proxy will
- # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
- if public_method_defined?(method)
- define_method("#{method}!") do |*args|
- @wrapped_string = send(args.nil? ? method : method, *args).to_s
- self
- end
+ def as_json(options = nil) #:nodoc:
+ to_s.as_json(options)
+ end
+
+ %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
+ define_method("#{method}!") do |*args|
+ @wrapped_string = send(method, *args).to_s
+ self
end
end
@@ -431,43 +206,14 @@ module ActiveSupport #:nodoc:
return nil if byte_offset.nil?
return 0 if @wrapped_string == ''
- if @wrapped_string.respond_to?(:force_encoding)
- @wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT)
- end
-
begin
- @wrapped_string[0...byte_offset].unpack('U*').length
+ @wrapped_string.byteslice(0...byte_offset).unpack('U*').length
rescue ArgumentError
byte_offset -= 1
retry
end
end
- def justify(integer, way, padstr=' ') #:nodoc:
- raise ArgumentError, "zero width padding" if padstr.length == 0
- padsize = integer - size
- padsize = padsize > 0 ? padsize : 0
- case way
- when :right
- result = @wrapped_string.dup.insert(0, padding(padsize, padstr))
- when :left
- result = @wrapped_string.dup.insert(-1, padding(padsize, padstr))
- when :center
- lpad = padding((padsize / 2.0).floor, padstr)
- rpad = padding((padsize / 2.0).ceil, padstr)
- result = @wrapped_string.dup.insert(0, lpad).insert(-1, rpad)
- end
- chars(result)
- end
-
- def padding(padsize, padstr=' ') #:nodoc:
- if padsize != 0
- chars(padstr * ((padsize / Unicode.u_unpack(padstr).size) + 1)).slice(0, padsize)
- else
- ''
- end
- end
-
def chars(string) #:nodoc:
self.class.new(string)
end
diff --git a/activesupport/lib/active_support/multibyte/exceptions.rb b/activesupport/lib/active_support/multibyte/exceptions.rb
deleted file mode 100644
index 62066e3c71..0000000000
--- a/activesupport/lib/active_support/multibyte/exceptions.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# encoding: utf-8
-
-module ActiveSupport #:nodoc:
- module Multibyte #:nodoc:
- # Raised when a problem with the encoding was found.
- class EncodingError < StandardError; end
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 754ca9290b..cb89d45c92 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -10,7 +10,7 @@ module ActiveSupport
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
# The Unicode version that is supported by the implementation
- UNICODE_VERSION = '5.2.0'
+ UNICODE_VERSION = '6.1.0'
# The default normalization used for operations that require normalization. It can be set to any of the
# normalizations in NORMALIZATION_FORMS.
@@ -61,19 +61,6 @@ module ActiveSupport
TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
- # Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
- # valid UTF-8.
- #
- # Example:
- # Unicode.u_unpack('Café') # => [67, 97, 102, 233]
- def u_unpack(string)
- begin
- string.unpack 'U*'
- rescue ArgumentError
- raise EncodingError, 'malformed UTF-8 character'
- end
- end
-
# Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
# character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
# <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
@@ -86,10 +73,10 @@ module ActiveSupport
# Unpack the string at grapheme boundaries. Returns a list of character lists.
#
# Example:
- # Unicode.g_unpack('क्षि') # => [[2325, 2381], [2359], [2367]]
- # Unicode.g_unpack('Café') # => [[67], [97], [102], [233]]
- def g_unpack(string)
- codepoints = u_unpack(string)
+ # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
+ # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
+ def unpack_graphemes(string)
+ codepoints = string.codepoints.to_a
unpacked = []
pos = 0
marker = 0
@@ -118,12 +105,12 @@ module ActiveSupport
unpacked
end
- # Reverse operation of g_unpack.
+ # Reverse operation of unpack_graphemes.
#
# Example:
- # Unicode.g_pack(Unicode.g_unpack('क्षि')) # => 'क्षि'
- def g_pack(unpacked)
- (unpacked.flatten).pack('U*')
+ # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
+ def pack_graphemes(unpacked)
+ unpacked.flatten.pack('U*')
end
# Re-order codepoints so the string becomes canonical.
@@ -143,7 +130,7 @@ module ActiveSupport
end
# Decompose composed characters to the decomposed form.
- def decompose_codepoints(type, codepoints)
+ def decompose(type, codepoints)
codepoints.inject([]) do |decomposed, cp|
# if it's a hangul syllable starter character
if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
@@ -156,7 +143,7 @@ module ActiveSupport
decomposed.concat ncp
# if the codepoint is decomposable in with the current decomposition type
elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatability)
- decomposed.concat decompose_codepoints(type, ncp.dup)
+ decomposed.concat decompose(type, ncp.dup)
else
decomposed << cp
end
@@ -164,7 +151,7 @@ module ActiveSupport
end
# Compose decomposed characters to the composed form.
- def compose_codepoints(codepoints)
+ def compose(codepoints)
pos = 0
eoa = codepoints.length - 1
starter_pos = 0
@@ -283,35 +270,40 @@ module ActiveSupport
def normalize(string, form=nil)
form ||= @default_normalization_form
# See http://www.unicode.org/reports/tr15, Table 1
- codepoints = u_unpack(string)
+ codepoints = string.codepoints.to_a
case form
when :d
- reorder_characters(decompose_codepoints(:canonical, codepoints))
+ reorder_characters(decompose(:canonical, codepoints))
when :c
- compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints)))
+ compose(reorder_characters(decompose(:canonical, codepoints)))
when :kd
- reorder_characters(decompose_codepoints(:compatability, codepoints))
+ reorder_characters(decompose(:compatability, codepoints))
when :kc
- compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints)))
+ compose(reorder_characters(decompose(:compatability, codepoints)))
else
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end.pack('U*')
end
- def apply_mapping(string, mapping) #:nodoc:
- u_unpack(string).map do |codepoint|
- cp = database.codepoints[codepoint]
- if cp and (ncp = cp.send(mapping)) and ncp > 0
- ncp
- else
- codepoint
- end
- end.pack('U*')
+ def downcase(string)
+ apply_mapping string, :lowercase_mapping
+ end
+
+ def upcase(string)
+ apply_mapping string, :uppercase_mapping
+ end
+
+ def swapcase(string)
+ apply_mapping string, :swapcase_mapping
end
# Holds data about a codepoint in the Unicode database
class Codepoint
attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
+
+ def swapcase_mapping
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
+ end
end
# Holds static data from the Unicode database
@@ -374,6 +366,17 @@ module ActiveSupport
private
+ def apply_mapping(string, mapping) #:nodoc:
+ string.each_codepoint.map do |codepoint|
+ cp = database.codepoints[codepoint]
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
+ ncp
+ else
+ codepoint
+ end
+ end.pack('U*')
+ end
+
def tidy_byte(byte)
if byte < 160
[database.cp1252[byte] || byte].pack("U").unpack("C*")
diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb
deleted file mode 100644
index 94b393cee2..0000000000
--- a/activesupport/lib/active_support/multibyte/utils.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# encoding: utf-8
-
-module ActiveSupport #:nodoc:
- module Multibyte #:nodoc:
- if Kernel.const_defined?(:Encoding)
- # Returns a regular expression that matches valid characters in the current encoding
- def self.valid_character
- VALID_CHARACTER[Encoding.default_external.to_s]
- end
- else
- def self.valid_character
- case $KCODE
- when 'UTF8'
- VALID_CHARACTER['UTF-8']
- when 'SJIS'
- VALID_CHARACTER['Shift_JIS']
- end
- end
- end
-
- if 'string'.respond_to?(:valid_encoding?)
- # Verifies the encoding of a string
- def self.verify(string)
- string.valid_encoding?
- end
- else
- def self.verify(string)
- if expression = valid_character
- # Splits the string on character boundaries, which are determined based on $KCODE.
- string.split(//).all? { |c| expression =~ c }
- else
- true
- end
- end
- end
-
- # Verifies the encoding of the string and raises an exception when it's not valid
- def self.verify!(string)
- raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
- end
-
- if 'string'.respond_to?(:force_encoding)
- # Removes all invalid characters from the string.
- #
- # Note: this method is a no-op in Ruby 1.9
- def self.clean(string)
- string
- end
- else
- def self.clean(string)
- if expression = valid_character
- # Splits the string on character boundaries, which are determined based on $KCODE.
- string.split(//).grep(expression).join
- else
- string
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index b5a70d5933..13f675c654 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -1,36 +1,106 @@
module ActiveSupport
- # Notifications provides an instrumentation API for Ruby. To instrument an
- # action in Ruby you just need to do:
+ # = Notifications
#
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
+ # +ActiveSupport::Notifications+ provides an instrumentation API for Ruby.
+ #
+ # == Instrumenters
+ #
+ # To instrument an event you just need to do:
+ #
+ # ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
# end
#
+ # That executes the block first and notifies all subscribers once done.
+ #
+ # In the example above "render" is the name of the event, and the rest is called
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
+ # extra information to subscribers. Payloads consist of a hash whose contents
+ # are arbitrary and generally depend on the event.
+ #
+ # == Subscribers
+ #
# You can consume those events and the information they provide by registering
- # a log subscriber. For instance, let's store all instrumented events in an array:
+ # a subscriber. For instance, let's store all "render" events in an array:
#
- # @events = []
+ # events = []
#
- # ActiveSupport::Notifications.subscribe do |*args|
- # @events << ActiveSupport::Notifications::Event.new(*args)
+ # ActiveSupport::Notifications.subscribe("render") do |*args|
+ # events << ActiveSupport::Notifications::Event.new(*args)
# end
#
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
+ # That code returns right away, you are just subscribing to "render" events.
+ # The block will be called asynchronously whenever someone instruments "render":
+ #
+ # ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
# end
#
- # event = @events.first
- # event.name # => :render
+ # event = events.first
+ # event.name # => "render"
# event.duration # => 10 (in milliseconds)
# event.payload # => { :extra => :information }
#
- # When subscribing to Notifications, you can pass a pattern, to only consume
- # events that match the pattern:
+ # The block in the +subscribe+ call gets the name of the event, start
+ # timestamp, end timestamp, a string with a unique identifier for that event
+ # (something like "535801666f04d0298cd6"), and a hash with the payload, in
+ # that order.
+ #
+ # If an exception happens during that particular instrumentation the payload will
+ # have a key +:exception+ with an array of two elements as value: a string with
+ # the name of the exception class, and the exception message.
+ #
+ # As the previous example depicts, the class +ActiveSupport::Notifications::Event+
+ # is able to take the arguments as they come and provide an object-oriented
+ # interface to that data.
+ #
+ # You can also subscribe to all events whose name matches a certain regexp:
+ #
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
+ # ...
+ # end
+ #
+ # and even pass no argument to +subscribe+, in which case you are subscribing
+ # to all events.
+ #
+ # == Temporary Subscriptions
#
- # ActiveSupport::Notifications.subscribe(/render/) do |event|
- # @render_events << event
+ # Sometimes you do not want to subscribe to an event for the entire life of
+ # the application. There are two ways to unsubscribe.
+ #
+ # WARNING: The instrumentation framework is designed for long-running subscribers,
+ # use this feature sparingly because it wipes some internal caches and that has
+ # a negative impact on performance.
+ #
+ # === Subscribe While a Block Runs
+ #
+ # You can subscribe to some event temporarily while some block runs. For
+ # example, in
+ #
+ # callback = lambda {|*args| ... }
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
+ # ...
+ # end
+ #
+ # the callback will be called for all "sql.active_record" events instrumented
+ # during the execution of the block. The callback is unsubscribed automatically
+ # after that.
+ #
+ # === Manual Unsubscription
+ #
+ # The +subscribe+ method returns a subscriber object:
+ #
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
+ # ...
# end
#
+ # To prevent that block from being called anymore, just unsubscribe passing
+ # that reference:
+ #
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
+ #
+ # == Default Queue
+ #
# Notifications ships with a queue implementation that consumes and publish events
# to log subscribers in a thread. You can use any queue implementation you want.
#
@@ -62,6 +132,13 @@ module ActiveSupport
end
end
+ def subscribed(callback, *args, &block)
+ subscriber = subscribe(*args, &callback)
+ yield
+ ensure
+ unsubscribe(subscriber)
+ end
+
def unsubscribe(args)
notifier.unsubscribe(args)
@instrumenters.clear
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 0264133581..8edd3960c7 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -1,8 +1,3 @@
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
YAML.add_builtin_type("omap") do |type, val|
@@ -20,7 +15,7 @@ module ActiveSupport
# oh.keys # => [:a, :b], this order is guaranteed
#
# <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations.
- class OrderedHash < ::Hash #:nodoc:
+ class OrderedHash < ::Hash
def to_yaml_type
"!tag:yaml.org,2002:omap"
end
@@ -29,20 +24,6 @@ module ActiveSupport
coder.represent_seq '!omap', map { |k,v| { k => v } }
end
- def to_yaml(opts = {})
- if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
- return super
- end
-
- YAML.quick_emit(self, opts) do |out|
- out.seq(taguri) do |seq|
- each do |k, v|
- seq.add(k => v)
- end
- end
- end
- end
-
def nested_under_indifferent_access
self
end
@@ -51,172 +32,5 @@ module ActiveSupport
def extractable_options?
true
end
-
- # Hash is ordered in Ruby 1.9!
- if RUBY_VERSION < '1.9'
-
- # In MRI the Hash class is core and written in C. In particular, methods are
- # programmed with explicit C function calls and polymorphism is not honored.
- #
- # For example, []= is crucial in this implementation to maintain the @keys
- # array but hash.c invokes rb_hash_aset() originally. This prevents method
- # reuse through inheritance and forces us to reimplement stuff.
- #
- # For instance, we cannot use the inherited #merge! because albeit the algorithm
- # itself would work, our []= is not being called at all by the C code.
-
- def initialize(*args, &block)
- super
- @keys = []
- end
-
- def self.[](*args)
- ordered_hash = new
-
- if (args.length == 1 && args.first.is_a?(Array))
- args.first.each do |key_value_pair|
- next unless (key_value_pair.is_a?(Array))
- ordered_hash[key_value_pair[0]] = key_value_pair[1]
- end
-
- return ordered_hash
- end
-
- unless (args.size % 2 == 0)
- raise ArgumentError.new("odd number of arguments for Hash")
- end
-
- args.each_with_index do |val, ind|
- next if (ind % 2 != 0)
- ordered_hash[val] = args[ind + 1]
- end
-
- ordered_hash
- end
-
- def initialize_copy(other)
- super
- # make a deep copy of keys
- @keys = other.keys
- end
-
- def []=(key, value)
- @keys << key unless has_key?(key)
- super
- end
-
- def delete(key)
- if has_key? key
- index = @keys.index(key)
- @keys.delete_at index
- end
- super
- end
-
- def delete_if
- super
- sync_keys!
- self
- end
-
- def reject!
- super
- sync_keys!
- self
- end
-
- def reject(&block)
- dup.reject!(&block)
- end
-
- def keys
- @keys.dup
- end
-
- def values
- @keys.collect { |key| self[key] }
- end
-
- def to_hash
- self
- end
-
- def to_a
- @keys.map { |key| [ key, self[key] ] }
- end
-
- def each_key
- return to_enum(:each_key) unless block_given?
- @keys.each { |key| yield key }
- self
- end
-
- def each_value
- return to_enum(:each_value) unless block_given?
- @keys.each { |key| yield self[key]}
- self
- end
-
- def each
- return to_enum(:each) unless block_given?
- @keys.each {|key| yield [key, self[key]]}
- self
- end
-
- def each_pair
- return to_enum(:each_pair) unless block_given?
- @keys.each {|key| yield key, self[key]}
- self
- end
-
- alias_method :select, :find_all
-
- def clear
- super
- @keys.clear
- self
- end
-
- def shift
- k = @keys.first
- v = delete(k)
- [k, v]
- end
-
- def merge!(other_hash)
- if block_given?
- other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
- else
- other_hash.each { |k, v| self[k] = v }
- end
- self
- end
-
- alias_method :update, :merge!
-
- def merge(other_hash, &block)
- dup.merge!(other_hash, &block)
- end
-
- # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
- def replace(other)
- super
- @keys = other.keys
- self
- end
-
- def invert
- OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}]
- end
-
- def inspect
- "#<OrderedHash #{super}>"
- end
-
- private
- def sync_keys!
- @keys.delete_if {|k| !has_key?(k)}
- end
- end
end
end
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index bf81567d22..9e5a5d0246 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -30,8 +30,9 @@ module ActiveSupport #:nodoc:
end
def method_missing(name, *args)
- if name.to_s =~ /(.*)=$/
- self[$1] = args.first
+ name_string = name.to_s
+ if name_string.chomp!('=')
+ self[name_string] = args.first
else
self[name]
end
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 1638512af0..f696716cc8 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -5,12 +5,6 @@ module ActiveSupport
class Railtie < Rails::Railtie
config.active_support = ActiveSupport::OrderedOptions.new
- # Loads support for "whiny nil" (noisy warnings when methods are invoked
- # on +nil+ values) if Configuration#whiny_nils is true.
- initializer "active_support.initialize_whiny_nils" do |app|
- require 'active_support/whiny_nil' if app.config.whiny_nils
- end
-
initializer "active_support.deprecation_behavior" do |app|
if deprecation = app.config.active_support.deprecation
ActiveSupport::Deprecation.behavior = deprecation
diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb
index 608b3fe4b9..13e96b3596 100644
--- a/activesupport/lib/active_support/ruby/shim.rb
+++ b/activesupport/lib/active_support/ruby/shim.rb
@@ -3,20 +3,13 @@
#
# Date next_year, next_month
# DateTime to_date, to_datetime, xmlschema
-# Enumerable group_by, each_with_object, none?
-# Process Process.daemon
-# REXML security fix
+# Enumerable group_by, none?
# String ord
# 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/process/daemon'
require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/string/interpolation'
-require 'active_support/core_ext/string/encoding'
-require 'active_support/core_ext/rexml'
require 'active_support/core_ext/time/conversions'
-require 'active_support/core_ext/file/path'
-require 'active_support/core_ext/module/method_names' \ No newline at end of file
diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb
index e6b1f39225..f3f3909a90 100644
--- a/activesupport/lib/active_support/string_inquirer.rb
+++ b/activesupport/lib/active_support/string_inquirer.rb
@@ -11,8 +11,8 @@ module ActiveSupport
#
class StringInquirer < String
def method_missing(method_name, *arguments)
- if method_name.to_s[-1,1] == "?"
- self == method_name.to_s[0..-2]
+ if method_name[-1, 1] == "?"
+ self == method_name[0..-2]
else
super
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index a59fc26d5d..f6ad861353 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -1,63 +1,58 @@
require 'active_support/core_ext/object/blank'
require 'logger'
+require 'active_support/logger'
module ActiveSupport
- # Wraps any standard Logger class to provide tagging capabilities. Examples:
+ # Wraps any standard Logger object to provide tagging capabilities. Examples:
#
- # Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
- # Logger.tagged("BCX") { Logger.info "Stuff" } # Logs "[BCX] Stuff"
- # Logger.tagged("BCX", "Jason") { Logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff"
- # Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff"
+ # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
+ # logger.tagged("BCX") { logger.info "Stuff" } # Logs "[BCX] Stuff"
+ # logger.tagged("BCX", "Jason") { logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff"
+ # logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff"
#
# This is used by the default Rails.logger as configured by Railties to make it easy to stamp log lines
# with subdomains, request ids, and anything else to aid debugging of multi-user production applications.
- class TaggedLogging
- def initialize(logger)
- @logger = logger
- @tags = Hash.new { |h,k| h[k] = [] }
- end
+ module TaggedLogging
+ class Formatter < ActiveSupport::Logger::SimpleFormatter # :nodoc:
+ # This method is invoked when a log event occurs
+ def call(severity, timestamp, progname, msg)
+ super(severity, timestamp, progname, "#{tags_text}#{msg}")
+ end
- def tagged(*new_tags)
- tags = current_tags
- new_tags = Array.wrap(new_tags).flatten.reject(&:blank?)
- tags.concat new_tags
- yield
- ensure
- new_tags.size.times { tags.pop }
- end
+ def clear!
+ current_tags.clear
+ end
- def add(severity, message = nil, progname = nil, &block)
- @logger.add(severity, "#{tags_text}#{message}", progname, &block)
- end
+ def current_tags
+ Thread.current[:activesupport_tagged_logging_tags] ||= []
+ end
- %w( fatal error warn info debug unkown ).each do |severity|
- eval <<-EOM, nil, __FILE__, __LINE__ + 1
- def #{severity}(progname = nil, &block)
- add(Logger::#{severity.upcase}, progname, &block)
+ private
+ def tags_text
+ tags = current_tags
+ if tags.any?
+ tags.collect { |tag| "[#{tag}] " }.join
end
- EOM
- end
-
- def flush(*args)
- @tags.delete(Thread.current)
- @logger.flush(*args) if @logger.respond_to?(:flush)
+ end
end
- def method_missing(method, *args)
- @logger.send(method, *args)
+ def self.new(logger)
+ logger.formatter = Formatter.new
+ logger.extend(self)
end
- protected
-
- def tags_text
- tags = current_tags
- if tags.any?
- tags.collect { |tag| "[#{tag}]" }.join(" ") + " "
- end
+ def tagged(*new_tags)
+ tags = formatter.current_tags
+ new_tags = new_tags.flatten.reject(&:blank?)
+ tags.concat new_tags
+ yield
+ ensure
+ tags.pop(new_tags.size)
end
- def current_tags
- @tags[Thread.current]
+ def flush
+ formatter.clear!
+ super if defined?(super)
end
end
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 8d6c27e381..9a52c916ec 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -1,34 +1,55 @@
-require 'test/unit/testcase'
+require 'minitest/spec'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
require 'active_support/testing/deprecation'
require 'active_support/testing/declarative'
-require 'active_support/testing/pending'
require 'active_support/testing/isolation'
require 'active_support/testing/mochaing'
require 'active_support/core_ext/kernel/reporting'
module ActiveSupport
- class TestCase < ::Test::Unit::TestCase
- if defined? MiniTest
- Assertion = MiniTest::Assertion
- alias_method :method_name, :name if method_defined? :name
- alias_method :method_name, :__name__ if method_defined? :__name__
- else
- Assertion = Test::Unit::AssertionFailedError
-
- undef :default_test
+ class TestCase < ::MiniTest::Spec
+
+ if MiniTest::Unit::VERSION < '2.6.1'
+ class << self
+ alias :name :to_s
+ end
+ end
+
+ # Use AS::TestCase for the base class when describing a model
+ register_spec_type(self) do |desc|
+ desc < ActiveRecord::Model
end
+ Assertion = MiniTest::Assertion
+ alias_method :method_name, :name if method_defined? :name
+ alias_method :method_name, :__name__ if method_defined? :__name__
+
$tags = {}
def self.for_tag(tag)
yield if $tags[tag]
end
+ # FIXME: we have tests that depend on run order, we should fix that and
+ # remove this method.
+ def self.test_order # :nodoc:
+ :sorted
+ end
+
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
- include ActiveSupport::Testing::Pending
extend ActiveSupport::Testing::Declarative
+
+ # test/unit backwards compatibility methods
+ alias :assert_raise :assert_raises
+ alias :assert_not_nil :refute_nil
+ alias :assert_not_equal :refute_equal
+ alias :assert_no_match :refute_match
+ alias :assert_not_same :refute_same
+
+ def assert_nothing_raised(*args)
+ yield
+ end
end
end
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index f3629ada5b..d84595fa8f 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
module ActiveSupport
@@ -45,7 +44,7 @@ module ActiveSupport
# post :delete, :id => ...
# end
def assert_difference(expression, difference = 1, message = nil, &block)
- expressions = Array.wrap expression
+ expressions = Array(expression)
exps = expressions.map { |e|
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
@@ -87,7 +86,7 @@ module ActiveSupport
# Test if an expression is not blank. Passes if object.present? is true.
#
- # assert_present {:data => 'x' } # => true
+ # assert_present({:data => 'x' }) # => true
def assert_present(object, message=nil)
message ||= "#{object.inspect} is blank"
assert object.present?, message
diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb
index 0135185a47..a8342904dc 100644
--- a/activesupport/lib/active_support/testing/deprecation.rb
+++ b/activesupport/lib/active_support/testing/deprecation.rb
@@ -34,22 +34,3 @@ module ActiveSupport
end
end
end
-
-begin
- require 'test/unit/error'
-rescue LoadError
- # Using miniunit, ignore.
-else
- module Test
- module Unit
- class Error #:nodoc:
- # Silence warnings when reporting test errors.
- def message_with_silenced_deprecation
- ActiveSupport::Deprecation.silence { message_without_silenced_deprecation }
- end
- alias_method :message_without_silenced_deprecation, :message
- alias_method :message, :message_with_silenced_deprecation
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 6b29ba4c10..1a0681e850 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -37,14 +37,6 @@ module ActiveSupport
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
end
- def self.included(base)
- if defined?(::MiniTest) && base < ::MiniTest::Unit::TestCase
- base.send :include, MiniTest
- elsif defined?(Test::Unit)
- base.send :include, TestUnit
- end
- end
-
def _run_class_setup # class setup method should only happen in parent
unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
self.class.setup if self.class.respond_to?(:setup)
@@ -52,42 +44,16 @@ module ActiveSupport
end
end
- module TestUnit
- def run(result)
- _run_class_setup
-
- yield(Test::Unit::TestCase::STARTED, name)
-
- @_result = result
-
- serialized = run_in_isolation do |proxy|
- begin
- super(proxy) { }
- rescue Exception => e
- proxy.add_error(Test::Unit::Error.new(name, e))
- end
- end
-
- retval, proxy = Marshal.load(serialized)
- proxy.__replay__(@_result)
+ def run(runner)
+ _run_class_setup
- yield(Test::Unit::TestCase::FINISHED, name)
- retval
+ serialized = run_in_isolation do |isolated_runner|
+ super(isolated_runner)
end
- end
-
- module MiniTest
- def run(runner)
- _run_class_setup
- serialized = run_in_isolation do |isolated_runner|
- super(isolated_runner)
- end
-
- retval, proxy = Marshal.load(serialized)
- proxy.__replay__(runner)
- retval
- end
+ retval, proxy = Marshal.load(serialized)
+ proxy.__replay__(runner)
+ retval
end
module Forking
@@ -145,13 +111,3 @@ module ActiveSupport
end
end
end
-
-# Only in subprocess for windows / jruby.
-if ENV['ISOLATION_TEST']
- require "test/unit/collector/objectspace"
- class Test::Unit::Collector::ObjectSpace
- def include?(test)
- super && test.method_name == ENV['ISOLATION_TEST']
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb
deleted file mode 100644
index feac7bc347..0000000000
--- a/activesupport/lib/active_support/testing/pending.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# Some code from jeremymcanally's "pending"
-# https://github.com/jeremymcanally/pending/tree/master
-
-module ActiveSupport
- module Testing
- module Pending
-
- unless defined?(Spec)
-
- @@pending_cases = []
- @@at_exit = false
-
- def pending(description = "", &block)
- if defined?(::MiniTest)
- skip(description.blank? ? nil : description)
- else
- if description.is_a?(Symbol)
- is_pending = $tags[description]
- return block.call unless is_pending
- end
-
- if block_given?
- failed = false
-
- begin
- block.call
- rescue Exception
- failed = true
- end
-
- flunk("<#{description}> did not fail.") unless failed
- end
-
- caller[0] =~ (/(.*):(.*):in `(.*)'/)
- @@pending_cases << "#{$3} at #{$1}, line #{$2}"
- print "P"
-
- @@at_exit ||= begin
- at_exit do
- puts "\nPending Cases:"
- @@pending_cases.each do |test_case|
- puts test_case
- end
- end
- end
- end
- end
- end
-
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
index dd23f8d82d..244ee1a224 100644
--- a/activesupport/lib/active_support/testing/performance.rb
+++ b/activesupport/lib/active_support/testing/performance.rb
@@ -13,12 +13,6 @@ module ActiveSupport
included do
superclass_delegating_accessor :profile_options
self.profile_options = {}
-
- if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
- include ForMiniTest
- else
- include ForClassicTestUnit
- end
end
# each implementation should define metrics and freeze the defaults
@@ -41,82 +35,38 @@ module ActiveSupport
"#{self.class.name}##{method_name}"
end
- module ForMiniTest
- def run(runner)
- @runner = runner
+ def run(runner)
+ @runner = runner
- run_warmup
- if full_profile_options && metrics = full_profile_options[:metrics]
- metrics.each do |metric_name|
- if klass = Metrics[metric_name.to_sym]
- run_profile(klass.new)
- end
+ run_warmup
+ if full_profile_options && metrics = full_profile_options[:metrics]
+ metrics.each do |metric_name|
+ if klass = Metrics[metric_name.to_sym]
+ run_profile(klass.new)
end
end
-
- return
end
- def run_test(metric, mode)
- result = '.'
- begin
- run_callbacks :setup
- setup
- metric.send(mode) { __send__ method_name }
- rescue Exception => e
- result = @runner.puke(self.class, method_name, e)
- ensure
- begin
- teardown
- run_callbacks :teardown, :enumerator => :reverse_each
- rescue Exception => e
- result = @runner.puke(self.class, method_name, e)
- end
- end
- result
- end
+ return
end
- module ForClassicTestUnit
- def run(result)
- return if method_name =~ /^default_test$/
-
- yield(self.class::STARTED, name)
- @_result = result
-
- run_warmup
- if full_profile_options && metrics = full_profile_options[:metrics]
- metrics.each do |metric_name|
- if klass = Metrics[metric_name.to_sym]
- run_profile(klass.new)
- result.add_run
- else
- puts '%20s: unsupported' % metric_name
- end
- end
- end
-
- yield(self.class::FINISHED, name)
- end
-
- def run_test(metric, mode)
+ def run_test(metric, mode)
+ result = '.'
+ begin
run_callbacks :setup
setup
- metric.send(mode) { __send__ @method_name }
- rescue ::Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError => e
- add_error(e)
+ metric.send(mode) { __send__ method_name }
+ rescue Exception => e
+ result = @runner.puke(self.class, method_name, e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
- rescue ::Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError => e
- add_error(e)
+ rescue Exception => e
+ result = @runner.puke(self.class, method_name, e)
end
end
+ result
end
protected
@@ -208,8 +158,7 @@ module ActiveSupport
end
end
- ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
- ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
+ ruby = "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
@env = [app, rails, ruby, RUBY_PLATFORM] * ','
end
@@ -306,7 +255,6 @@ module ActiveSupport
end
end
-RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8
case RUBY_ENGINE
when 'ruby' then require 'active_support/testing/performance/ruby'
when 'rbx' then require 'active_support/testing/performance/rubinius'
diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb
index 7d6d047ef6..b7a34ea279 100644
--- a/activesupport/lib/active_support/testing/performance/ruby.rb
+++ b/activesupport/lib/active_support/testing/performance/ruby.rb
@@ -86,9 +86,12 @@ module ActiveSupport
end
protected
- # overridden by each implementation
def with_gc_stats
+ GC::Profiler.enable
+ GC.start
yield
+ ensure
+ GC::Profiler.disable
end
end
@@ -124,29 +127,42 @@ module ActiveSupport
class Memory < DigitalInformationUnit
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
+
+ # Ruby 1.9 + GCdata patch
+ if GC.respond_to?(:malloc_allocated_size)
+ def measure
+ GC.malloc_allocated_size
+ end
+ end
end
class Objects < Amount
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
+
+ # Ruby 1.9 + GCdata patch
+ if GC.respond_to?(:malloc_allocations)
+ def measure
+ GC.malloc_allocations
+ end
+ end
end
class GcRuns < Amount
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
+
+ def measure
+ GC.count
+ end
end
class GcTime < Time
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
+
+ def measure
+ GC::Profiler.total_time
+ end
end
end
end
end
end
-
-if RUBY_VERSION.between?('1.9.2', '2.0')
- require 'active_support/testing/performance/ruby/yarv'
-elsif RUBY_VERSION.between?('1.8.6', '1.9')
- require 'active_support/testing/performance/ruby/mri'
-else
- $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.'
- exit
-end
diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb
deleted file mode 100644
index 142279dd6e..0000000000
--- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module ActiveSupport
- module Testing
- module Performance
- module Metrics
- class Base
- protected
- # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker)
- if GC.respond_to?(:enable_stats)
- def with_gc_stats
- GC.enable_stats
- GC.start
- yield
- ensure
- GC.disable_stats
- end
- end
- end
-
- class Memory < DigitalInformationUnit
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_memory)
- def measure
- RubyProf.measure_memory
- end
- end
- end
-
- class Objects < Amount
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_allocations)
- def measure
- RubyProf.measure_allocations
- end
- end
- end
-
- class GcRuns < Amount
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_gc_runs)
- def measure
- RubyProf.measure_gc_runs
- end
- end
- end
-
- class GcTime < Time
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_gc_time)
- def measure
- RubyProf.measure_gc_time / 1000.0 / 1000.0
- end
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb
deleted file mode 100644
index 7873262331..0000000000
--- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module ActiveSupport
- module Testing
- module Performance
- module Metrics
- class Base
- protected
- # Ruby 1.9 with GC::Profiler
- if defined?(GC::Profiler)
- def with_gc_stats
- GC::Profiler.enable
- GC.start
- yield
- ensure
- GC::Profiler.disable
- end
- end
- end
-
- class Memory < DigitalInformationUnit
- # Ruby 1.9 + GCdata patch
- if GC.respond_to?(:malloc_allocated_size)
- def measure
- GC.malloc_allocated_size
- end
- end
- end
-
- class Objects < Amount
- # Ruby 1.9 + GCdata patch
- if GC.respond_to?(:malloc_allocations)
- def measure
- GC.malloc_allocations
- end
- end
- end
-
- class GcRuns < Amount
- # Ruby 1.9
- if GC.respond_to?(:count)
- def measure
- GC.count
- end
- end
- end
-
- class GcTime < Time
- # Ruby 1.9 with GC::Profiler
- if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time)
- def measure
- GC::Profiler.total_time
- end
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb
index 22e41fa905..772c7b4209 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -10,11 +10,6 @@ module ActiveSupport
include ActiveSupport::Callbacks
define_callbacks :setup, :teardown
- if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
- include ForMiniTest
- else
- include ForClassicTestUnit
- end
end
module ClassMethods
@@ -27,83 +22,22 @@ module ActiveSupport
end
end
- module ForMiniTest
- def run(runner)
- result = '.'
+ def run(runner)
+ result = '.'
+ begin
+ run_callbacks :setup do
+ result = super
+ end
+ rescue Exception => e
+ result = runner.puke(self.class, method_name, e)
+ ensure
begin
- run_callbacks :setup do
- result = super
- end
+ run_callbacks :teardown
rescue Exception => e
result = runner.puke(self.class, method_name, e)
- ensure
- begin
- run_callbacks :teardown
- rescue Exception => e
- result = runner.puke(self.class, method_name, e)
- end
- end
- result
- end
- end
-
- module ForClassicTestUnit
- # For compatibility with Ruby < 1.8.6
- PASSTHROUGH_EXCEPTIONS = Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS rescue [NoMemoryError, SignalException, Interrupt, SystemExit]
-
- # This redefinition is unfortunate but test/unit shows us no alternative.
- # Doubly unfortunate: hax to support Mocha's hax.
- def run(result)
- return if @method_name.to_s == "default_test"
-
- mocha_counter = retrieve_mocha_counter(result)
- yield(Test::Unit::TestCase::STARTED, name)
- @_result = result
-
- begin
- begin
- run_callbacks :setup do
- setup
- __send__(@method_name)
- mocha_verify(mocha_counter) if mocha_counter
- end
- rescue Mocha::ExpectationError => e
- add_failure(e.message, e.backtrace)
- rescue Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue Exception => e
- raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
- add_error(e)
- ensure
- begin
- teardown
- run_callbacks :teardown
- rescue Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue Exception => e
- raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
- add_error(e)
- end
- end
- ensure
- mocha_teardown if mocha_counter
- end
-
- result.add_run
- yield(Test::Unit::TestCase::FINISHED, name)
- end
-
- protected
-
- def retrieve_mocha_counter(result) #:nodoc:
- if respond_to?(:mocha_verify) # using mocha
- if defined?(Mocha::TestCaseAdapter::AssertionCounter)
- Mocha::TestCaseAdapter::AssertionCounter.new(result)
- else
- Mocha::Integration::TestUnit::AssertionCounter.new(result)
- end
end
end
+ result
end
end
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
index 86f057d676..9634b52ecf 100644
--- a/activesupport/lib/active_support/time.rb
+++ b/activesupport/lib/active_support/time.rb
@@ -13,7 +13,6 @@ end
require 'date'
require 'time'
-require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/core_ext/time/marshal'
require 'active_support/core_ext/time/acts_like'
require 'active_support/core_ext/time/calculations'
@@ -21,7 +20,6 @@ require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/time/zones'
require 'active_support/core_ext/date/acts_like'
-require 'active_support/core_ext/date/freeze'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/date/zones'
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 63279d0e6d..1cb71012ef 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,4 +1,4 @@
-require "active_support/values/time_zone"
+require 'active_support/values/time_zone'
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/inclusion'
@@ -146,12 +146,6 @@ module ActiveSupport
end
end
- def to_yaml(options = {})
- return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
- utc.to_yaml(options)
- end
-
def httpdate
utc.httpdate
end
@@ -203,7 +197,11 @@ module ActiveSupport
end
def eql?(other)
- utc == other
+ utc.eql?(other)
+ end
+
+ def hash
+ utc.hash
end
def +(other)
@@ -277,7 +275,6 @@ module ActiveSupport
def to_i
utc.to_i
end
- alias_method :hash, :to_i
alias_method :tv_sec, :to_i
# A TimeWithZone acts like a Time, so just return +self+.
diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat
index 4fe0268cca..df17a8cccf 100644
--- a/activesupport/lib/active_support/values/unicode_tables.dat
+++ b/activesupport/lib/active_support/values/unicode_tables.dat
Binary files differ
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index bd8e7f907a..8a8f8f946d 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -1,7 +1,7 @@
module ActiveSupport
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb
deleted file mode 100644
index 577db5018e..0000000000
--- a/activesupport/lib/active_support/whiny_nil.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# Extensions to +nil+ which allow for more helpful error messages for people who
-# are new to Rails.
-#
-# Ruby raises NoMethodError if you invoke a method on an object that does not
-# respond to it:
-#
-# $ ruby -e nil.destroy
-# -e:1: undefined method `destroy' for nil:NilClass (NoMethodError)
-#
-# With these extensions, if the method belongs to the public interface of the
-# classes in NilClass::WHINERS the error message suggests which could be the
-# actual intended class:
-#
-# $ rails runner nil.destroy
-# ...
-# You might have expected an instance of ActiveRecord::Base.
-# ...
-#
-# NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental
-# method of Active Record models NilClass#id is redefined as well to raise a RuntimeError
-# and warn the user. She probably wanted a model database identifier and the 4
-# returned by the original method could result in obscure bugs.
-#
-# The flag <tt>config.whiny_nils</tt> determines whether this feature is enabled.
-# By default it is on in development and test modes, and it is off in production
-# mode.
-class NilClass
- METHOD_CLASS_MAP = Hash.new
-
- def self.add_whiner(klass)
- methods = klass.public_instance_methods - public_instance_methods
- class_name = klass.name
- methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
- end
-
- add_whiner ::Array
-
- # Raises a RuntimeError when you attempt to call +id+ on +nil+.
- def id
- raise RuntimeError, "Called id for nil, which would mistakenly be #{object_id} -- if you really wanted the id of nil, use object_id", caller
- end
-
- private
- def method_missing(method, *args)
- if klass = METHOD_CLASS_MAP[method]
- raise_nil_warning_for klass, method, caller
- else
- super
- end
- end
-
- # Raises a NoMethodError when you attempt to call a method on +nil+.
- def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil)
- message = "You have a nil object when you didn't expect it!"
- message << "\nYou might have expected an instance of #{class_name}." if class_name
- message << "\nThe error occurred while evaluating nil.#{selector}" if selector
-
- raise NoMethodError, message, with_caller || caller
- end
-end
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 1ea9a9d7e1..677e9910bb 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -1,4 +1,5 @@
require 'time'
+require 'base64'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections'
@@ -48,7 +49,7 @@ module ActiveSupport
"symbol" => Proc.new { |symbol| symbol.to_s },
"date" => Proc.new { |date| date.to_s(:db) },
"datetime" => Proc.new { |time| time.xmlschema },
- "binary" => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) },
+ "binary" => Proc.new { |binary| ::Base64.encode64(binary) },
"yaml" => Proc.new { |yaml| yaml.to_yaml }
} unless defined?(FORMATTING)
@@ -64,7 +65,7 @@ module ActiveSupport
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
"string" => Proc.new { |string| string.to_s },
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
- "base64Binary" => Proc.new { |bin| ActiveSupport::Base64.decode64(bin) },
+ "base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
"binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
"file" => Proc.new { |file, entity| _parse_file(file, entity) }
}
@@ -148,14 +149,14 @@ module ActiveSupport
def _parse_binary(bin, entity) #:nodoc:
case entity['encoding']
when 'base64'
- ActiveSupport::Base64.decode64(bin)
+ ::Base64.decode64(bin)
else
bin
end
end
def _parse_file(file, entity)
- f = StringIO.new(ActiveSupport::Base64.decode64(file))
+ f = StringIO.new(::Base64.decode64(file))
f.extend(FileLike)
f.original_filename = entity['name']
f.content_type = entity['content_type']
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 6c222b83ba..dbb6c71907 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -71,7 +71,7 @@ module ActiveSupport
child_nodes = element.child_nodes
if child_nodes.length > 0
- for i in 0...child_nodes.length
+ (0...child_nodes.length).each do |i|
child = child_nodes.item(i)
merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE
end
@@ -133,7 +133,7 @@ module ActiveSupport
def get_attributes(element)
attribute_hash = {}
attributes = element.attributes
- for i in 0...attributes.length
+ (0...attributes.length).each do |i|
attribute_hash[CONTENT_KEY] ||= ''
attribute_hash[attributes.item(i).name] = attributes.item(i).value
end
@@ -147,7 +147,7 @@ module ActiveSupport
def texts(element)
texts = []
child_nodes = element.child_nodes
- for i in 0...child_nodes.length
+ (0...child_nodes.length).each do |i|
item = child_nodes.item(i)
if item.node_type == Node.TEXT_NODE
texts << item.get_data
@@ -163,7 +163,7 @@ module ActiveSupport
def empty_content?(element)
text = ''
child_nodes = element.child_nodes
- for i in 0...child_nodes.length
+ (0...child_nodes.length).each do |i|
item = child_nodes.item(i)
if item.node_type == Node.TEXT_NODE
text << item.get_data.strip
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 0382739871..40e25ce0cd 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -11,18 +11,14 @@ lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
require 'active_support/core_ext/kernel/reporting'
-
require 'active_support/core_ext/string/encoding'
-if "ruby".encoding_aware?
- # These are the normal settings that will be set up by Railties
- # TODO: Have these tests support other combinations of these values
- silence_warnings do
- Encoding.default_internal = "UTF-8"
- Encoding.default_external = "UTF-8"
- end
+
+silence_warnings do
+ Encoding.default_internal = "UTF-8"
+ Encoding.default_external = "UTF-8"
end
-require 'test/unit'
+require 'minitest/autorun'
require 'empty_bool'
silence_warnings { require 'mocha' }
@@ -30,9 +26,6 @@ silence_warnings { require 'mocha' }
ENV['NO_RELOAD'] = '1'
require 'active_support'
-# Include shims until we get off 1.8.6
-require 'active_support/ruby/shim' if RUBY_VERSION < '1.8.7'
-
def uses_memcached(test_name)
require 'memcache'
begin
@@ -43,22 +36,5 @@ def uses_memcached(test_name)
end
end
-def with_kcode(code)
- if RUBY_VERSION < '1.9'
- begin
- old_kcode, $KCODE = $KCODE, code
- yield
- ensure
- $KCODE = old_kcode
- end
- else
- yield
- end
-end
-
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
-
-if RUBY_VERSION < '1.9'
- $KCODE = 'UTF8'
-end
diff --git a/activesupport/test/benchmarkable_test.rb b/activesupport/test/benchmarkable_test.rb
index 06f5172e1f..04d4f5e503 100644
--- a/activesupport/test/benchmarkable_test.rb
+++ b/activesupport/test/benchmarkable_test.rb
@@ -3,8 +3,23 @@ require 'abstract_unit'
class BenchmarkableTest < ActiveSupport::TestCase
include ActiveSupport::Benchmarkable
- def teardown
- logger.send(:clear_buffer)
+ attr_reader :buffer, :logger
+
+ class Buffer
+ include Enumerable
+
+ def initialize; @lines = []; end
+ def each(&block); @lines.each(&block); end
+ def write(x); @lines << x; end
+ def close; end
+ def last; @lines.last; end
+ def size; @lines.size; end
+ def empty?; @lines.empty?; end
+ end
+
+ def setup
+ @buffer = Buffer.new
+ @logger = ActiveSupport::Logger.new(@buffer)
end
def test_without_block
@@ -27,48 +42,20 @@ class BenchmarkableTest < ActiveSupport::TestCase
end
def test_within_level
- logger.level = ActiveSupport::BufferedLogger::DEBUG
+ logger.level = ActiveSupport::Logger::DEBUG
benchmark('included_debug_run', :level => :debug) { }
assert_last_logged 'included_debug_run'
end
def test_outside_level
- logger.level = ActiveSupport::BufferedLogger::ERROR
+ logger.level = ActiveSupport::Logger::ERROR
benchmark('skipped_debug_run', :level => :debug) { }
assert_no_match(/skipped_debug_run/, buffer.last)
ensure
- logger.level = ActiveSupport::BufferedLogger::DEBUG
- end
-
- def test_without_silencing
- benchmark('debug_run', :silence => false) do
- logger.info "not silenced!"
- end
-
- assert_equal 2, buffer.size
- end
-
- def test_with_silencing
- benchmark('debug_run', :silence => true) do
- logger.info "silenced!"
- end
-
- assert_equal 1, buffer.size
+ logger.level = ActiveSupport::Logger::DEBUG
end
private
- def logger
- @logger ||= begin
- logger = ActiveSupport::BufferedLogger.new(StringIO.new)
- logger.auto_flushing = false
- logger
- end
- end
-
- def buffer
- logger.send(:buffer)
- end
-
def assert_last_logged(message = 'Benchmarking')
assert_match(/^#{message} \(.*\)$/, buffer.last)
end
diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb
new file mode 100644
index 0000000000..6d4e3b74f7
--- /dev/null
+++ b/activesupport/test/broadcast_logger_test.rb
@@ -0,0 +1,82 @@
+require 'abstract_unit'
+
+module ActiveSupport
+ class BroadcastLoggerTest < TestCase
+ attr_reader :logger, :log1, :log2
+ def setup
+ @log1 = FakeLogger.new
+ @log2 = FakeLogger.new
+ @log1.extend Logger.broadcast @log2
+ @logger = @log1
+ end
+
+ def test_debug
+ logger.debug "foo"
+ assert_equal 'foo', log1.adds.first[2]
+ assert_equal 'foo', log2.adds.first[2]
+ end
+
+ def test_close
+ logger.close
+ assert log1.closed, 'should be closed'
+ assert log2.closed, 'should be closed'
+ end
+
+ def test_chevrons
+ logger << "foo"
+ assert_equal %w{ foo }, log1.chevrons
+ assert_equal %w{ foo }, log2.chevrons
+ end
+
+ def test_level
+ assert_nil logger.level
+ logger.level = 10
+ assert_equal 10, log1.level
+ assert_equal 10, log2.level
+ end
+
+ def test_progname
+ assert_nil logger.progname
+ logger.progname = 10
+ assert_equal 10, log1.progname
+ assert_equal 10, log2.progname
+ end
+
+ def test_formatter
+ assert_nil logger.formatter
+ logger.formatter = 10
+ assert_equal 10, log1.formatter
+ assert_equal 10, log2.formatter
+ end
+
+ class FakeLogger
+ attr_reader :adds, :closed, :chevrons
+ attr_accessor :level, :progname, :formatter
+
+ def initialize
+ @adds = []
+ @closed = false
+ @chevrons = []
+ @level = nil
+ @progname = nil
+ @formatter = nil
+ end
+
+ def debug msg, &block
+ add(:omg, nil, msg, &block)
+ end
+
+ def << x
+ @chevrons << x
+ end
+
+ def add(*args)
+ @adds << args
+ end
+
+ def close
+ @closed = true
+ end
+ end
+ end
+end
diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb
index 386006677b..615635607c 100644
--- a/activesupport/test/buffered_logger_test.rb
+++ b/activesupport/test/buffered_logger_test.rb
@@ -3,12 +3,13 @@ require 'multibyte_test_helpers'
require 'stringio'
require 'fileutils'
require 'tempfile'
-require 'active_support/buffered_logger'
+require 'active_support/testing/deprecation'
-class BufferedLoggerTest < Test::Unit::TestCase
+class BufferedLoggerTest < ActiveSupport::TestCase
include MultibyteTestHelpers
+ include ActiveSupport::Testing::Deprecation
- Logger = ActiveSupport::BufferedLogger
+ Logger = ActiveSupport::Logger
def setup
@message = "A debug message"
@@ -23,16 +24,16 @@ class BufferedLoggerTest < Test::Unit::TestCase
t.write 'hi mom!'
t.close
- logger = Logger.new t.path
+ f = File.open(t.path, 'w')
+ f.binmode
+
+ logger = Logger.new f
logger.level = Logger::DEBUG
str = "\x80"
- if str.respond_to?(:force_encoding)
- str.force_encoding("ASCII-8BIT")
- end
+ str.force_encoding("ASCII-8BIT")
logger.add Logger::DEBUG, str
- logger.flush
ensure
logger.close
t.close true
@@ -40,16 +41,17 @@ class BufferedLoggerTest < Test::Unit::TestCase
def test_write_binary_data_create_file
fname = File.join Dir.tmpdir, 'lol', 'rofl.log'
- logger = Logger.new fname
+ FileUtils.mkdir_p File.dirname(fname)
+ f = File.open(fname, 'w')
+ f.binmode
+
+ logger = Logger.new f
logger.level = Logger::DEBUG
str = "\x80"
- if str.respond_to?(:force_encoding)
- str.force_encoding("ASCII-8BIT")
- end
+ str.force_encoding("ASCII-8BIT")
logger.add Logger::DEBUG, str
- logger.flush
ensure
logger.close
File.unlink fname
@@ -104,151 +106,20 @@ class BufferedLoggerTest < Test::Unit::TestCase
assert_equal message_copy, @message
end
-
- [false, nil, 0].each do |disable|
- define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_buffer_until_explicit_flush" do
- @logger.auto_flushing = disable
-
- 4.times do
- @logger.info 'wait for it..'
- assert @output.string.empty?, "@output.string should be empty but it is #{@output.string}"
- end
-
- @logger.flush
- assert !@output.string.empty?, "@logger.send(:buffer).size.to_s should not be empty but it is empty"
- end
-
- define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_flush_at_max_buffer_size_as_failsafe" do
- @logger.auto_flushing = disable
- assert_equal Logger::MAX_BUFFER_SIZE, @logger.auto_flushing
-
- (Logger::MAX_BUFFER_SIZE - 1).times do
- @logger.info 'wait for it..'
- assert @output.string.empty?, "@output.string should be empty but is #{@output.string}"
- end
-
- @logger.info 'there it is.'
- assert !@output.string.empty?, "@logger.send(:buffer).size.to_s should not be empty but it is empty"
- end
- end
-
def test_should_know_if_its_loglevel_is_below_a_given_level
Logger::Severity.constants.each do |level|
+ next if level.to_s == 'UNKNOWN'
@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
- def test_should_auto_flush_every_n_messages
- @logger.auto_flushing = 5
-
- 4.times do
- @logger.info 'wait for it..'
- assert @output.string.empty?, "@output.string should be empty but it is #{@output.string}"
- end
-
- @logger.info 'there it is.'
- assert !@output.string.empty?, "@output.string should not be empty but it is empty"
- end
-
- def test_should_create_the_log_directory_if_it_doesnt_exist
- tmp_directory = File.join(File.dirname(__FILE__), "tmp")
- log_file = File.join(tmp_directory, "development.log")
- FileUtils.rm_rf(tmp_directory)
- @logger = Logger.new(log_file)
- assert File.exist?(tmp_directory)
- end
-
- def test_logger_should_maintain_separate_buffers_for_each_thread
- @logger.auto_flushing = false
-
- a = Thread.new do
- @logger.info("a"); Thread.pass;
- @logger.info("b"); Thread.pass;
- @logger.info("c"); @logger.flush
- end
-
- b = Thread.new do
- @logger.info("x"); Thread.pass;
- @logger.info("y"); Thread.pass;
- @logger.info("z"); @logger.flush
- end
-
- a.join
- b.join
-
- assert @output.string.include?("a\nb\nc\n")
- assert @output.string.include?("x\ny\nz\n")
- end
-
- def test_flush_should_remove_empty_buffers
- @logger.send :buffer
- @logger.expects :clear_buffer
- @logger.flush
- end
-
def test_buffer_multibyte
- @logger.auto_flushing = 2
@logger.info(UNICODE_STRING)
@logger.info(BYTE_STRING)
assert @output.string.include?(UNICODE_STRING)
byte_string = @output.string.dup
- if byte_string.respond_to?(:force_encoding)
- byte_string.force_encoding("ASCII-8BIT")
- end
+ byte_string.force_encoding("ASCII-8BIT")
assert byte_string.include?(BYTE_STRING)
end
-
- def test_silence_only_current_thread
- @logger.auto_flushing = true
- run_thread_a = false
-
- a = Thread.new do
- while !run_thread_a do
- sleep(0.001)
- end
- @logger.info("x")
- run_thread_a = false
- end
-
- @logger.silence do
- run_thread_a = true
- @logger.info("a")
- while run_thread_a do
- sleep(0.001)
- end
- end
-
- a.join
-
- assert @output.string.include?("x")
- assert !@output.string.include?("a")
- end
-
- def test_flush_dead_buffers
- @logger.auto_flushing = false
-
- a = Thread.new do
- @logger.info("a")
- end
-
- keep_running = true
- Thread.new do
- @logger.info("b")
- while keep_running
- sleep(0.001)
- end
- end
-
- @logger.info("x")
- a.join
- @logger.flush
-
-
- assert @output.string.include?("x")
- assert @output.string.include?("a")
- assert !@output.string.include?("b")
-
- keep_running = false
- end
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index cb5362525f..b03865da93 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -13,10 +13,10 @@ class CacheKeyTest < ActiveSupport::TestCase
ENV['RAILS_CACHE_ID'] = 'c99'
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo])
- assert_equal 'c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar])
+ assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar])
assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm)
assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm)
- assert_equal 'nm/c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm)
+ assert_equal 'nm/c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm)
ensure
ENV['RAILS_CACHE_ID'] = nil
end
@@ -42,7 +42,7 @@ class CacheKeyTest < ActiveSupport::TestCase
end
end
- def test_respond_to_cache_key
+ def test_expand_cache_key_respond_to_cache_key
key = 'foo'
def key.cache_key
:foo_key
@@ -50,6 +50,25 @@ class CacheKeyTest < ActiveSupport::TestCase
assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key)
end
+ def test_expand_cache_key_array_with_something_that_responds_to_cache_key
+ key = 'foo'
+ def key.cache_key
+ :foo_key
+ end
+ assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key])
+ end
+
+ def test_expand_cache_key_of_nil
+ assert_equal '', ActiveSupport::Cache.expand_cache_key(nil)
+ end
+
+ def test_expand_cache_key_of_false
+ assert_equal 'false', ActiveSupport::Cache.expand_cache_key(false)
+ end
+
+ def test_expand_cache_key_of_true
+ assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true)
+ end
end
class CacheStoreSettingTest < ActiveSupport::TestCase
@@ -122,8 +141,8 @@ class CacheStoreNamespaceTest < ActiveSupport::TestCase
cache.write("foo", "bar")
cache.write("fu", "baz")
cache.delete_matched(/^fo/)
- assert_equal false, cache.exist?("foo")
- assert_equal true, cache.exist?("fu")
+ assert !cache.exist?("foo")
+ assert cache.exist?("fu")
end
def test_delete_matched_key
@@ -131,15 +150,15 @@ class CacheStoreNamespaceTest < ActiveSupport::TestCase
cache.write("foo", "bar")
cache.write("fu", "baz")
cache.delete_matched(/OO/i)
- assert_equal false, cache.exist?("foo")
- assert_equal true, cache.exist?("fu")
+ assert !cache.exist?("foo")
+ assert cache.exist?("fu")
end
end
# Tests the base functionality that should be identical across all cache stores.
module CacheStoreBehavior
def test_should_read_and_write_strings
- assert_equal true, @cache.write('foo', 'bar')
+ assert @cache.write('foo', 'bar')
assert_equal 'bar', @cache.read('foo')
end
@@ -174,22 +193,22 @@ module CacheStoreBehavior
end
def test_should_read_and_write_hash
- assert_equal true, @cache.write('foo', {:a => "b"})
+ assert @cache.write('foo', {:a => "b"})
assert_equal({:a => "b"}, @cache.read('foo'))
end
def test_should_read_and_write_integer
- assert_equal true, @cache.write('foo', 1)
+ assert @cache.write('foo', 1)
assert_equal 1, @cache.read('foo')
end
def test_should_read_and_write_nil
- assert_equal true, @cache.write('foo', nil)
+ assert @cache.write('foo', nil)
assert_equal nil, @cache.read('foo')
end
def test_should_read_and_write_false
- assert_equal true, @cache.write('foo', false)
+ assert @cache.write('foo', false)
assert_equal false, @cache.read('foo')
end
@@ -199,7 +218,7 @@ module CacheStoreBehavior
@cache.write('fud', 'biz')
assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
end
-
+
def test_read_multi_with_expires
@cache.write('foo', 'bar', :expires_in => 0.001)
@cache.write('fu', 'baz')
@@ -262,19 +281,19 @@ module CacheStoreBehavior
def test_exist
@cache.write('foo', 'bar')
- assert_equal true, @cache.exist?('foo')
- assert_equal false, @cache.exist?('bar')
+ assert @cache.exist?('foo')
+ assert !@cache.exist?('bar')
end
def test_nil_exist
@cache.write('foo', nil)
- assert_equal true, @cache.exist?('foo')
+ assert @cache.exist?('foo')
end
def test_delete
@cache.write('foo', 'bar')
assert @cache.exist?('foo')
- assert_equal true, @cache.delete('foo')
+ assert @cache.delete('foo')
assert !@cache.exist?('foo')
end
@@ -346,10 +365,10 @@ module CacheStoreBehavior
def test_crazy_key_characters
crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
- assert_equal true, @cache.write(crazy_key, "1", :raw => true)
+ assert @cache.write(crazy_key, "1", :raw => true)
assert_equal "1", @cache.read(crazy_key)
assert_equal "1", @cache.fetch(crazy_key)
- assert_equal true, @cache.delete(crazy_key)
+ assert @cache.delete(crazy_key)
assert_equal "2", @cache.fetch(crazy_key, :raw => true) { "2" }
assert_equal 3, @cache.increment(crazy_key)
assert_equal 2, @cache.decrement(crazy_key)
@@ -358,12 +377,12 @@ module CacheStoreBehavior
def test_really_long_keys
key = ""
900.times{key << "x"}
- assert_equal true, @cache.write(key, "bar")
+ assert @cache.write(key, "bar")
assert_equal "bar", @cache.read(key)
assert_equal "bar", @cache.fetch(key)
assert_nil @cache.read("#{key}x")
assert_equal({key => "bar"}, @cache.read_multi(key))
- assert_equal true, @cache.delete(key)
+ assert @cache.delete(key)
end
end
@@ -371,36 +390,34 @@ end
# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special
# characters like the umlaut in UTF-8.
module EncodedKeyCacheBehavior
- if defined?(Encoding)
- Encoding.list.each do |encoding|
- define_method "test_#{encoding.name.underscore}_encoded_values" do
- key = "foo".force_encoding(encoding)
- assert_equal true, @cache.write(key, "1", :raw => true)
- assert_equal "1", @cache.read(key)
- assert_equal "1", @cache.fetch(key)
- assert_equal true, @cache.delete(key)
- assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
- assert_equal 3, @cache.increment(key)
- assert_equal 2, @cache.decrement(key)
- end
- end
-
- def test_common_utf8_values
- key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
- assert_equal true, @cache.write(key, "1", :raw => true)
+ Encoding.list.each do |encoding|
+ define_method "test_#{encoding.name.underscore}_encoded_values" do
+ key = "foo".force_encoding(encoding)
+ assert @cache.write(key, "1", :raw => true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
- assert_equal true, @cache.delete(key)
+ assert @cache.delete(key)
assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
assert_equal 3, @cache.increment(key)
assert_equal 2, @cache.decrement(key)
end
+ end
- def test_retains_encoding
- key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
- assert_equal true, @cache.write(key, "1", :raw => true)
- assert_equal Encoding::UTF_8, key.encoding
- end
+ def test_common_utf8_values
+ key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
+ assert @cache.write(key, "1", :raw => true)
+ assert_equal "1", @cache.read(key)
+ assert_equal "1", @cache.fetch(key)
+ assert @cache.delete(key)
+ assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
+ assert_equal 3, @cache.increment(key)
+ assert_equal 2, @cache.decrement(key)
+ end
+
+ def test_retains_encoding
+ key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
+ assert @cache.write(key, "1", :raw => true)
+ assert_equal Encoding::UTF_8, key.encoding
end
end
@@ -411,10 +428,10 @@ module CacheDeleteMatchedBehavior
@cache.write("foo/bar", "baz")
@cache.write("fu/baz", "bar")
@cache.delete_matched(/oo/)
- assert_equal false, @cache.exist?("foo")
- assert_equal true, @cache.exist?("fu")
- assert_equal false, @cache.exist?("foo/bar")
- assert_equal true, @cache.exist?("fu/baz")
+ assert !@cache.exist?("foo")
+ assert @cache.exist?("fu")
+ assert !@cache.exist?("foo/bar")
+ assert @cache.exist?("fu/baz")
end
end
@@ -443,7 +460,7 @@ module LocalCacheBehavior
retval = @cache.with_local_cache do
@cache.write('foo', 'bar')
end
- assert_equal true, retval
+ assert retval
assert_equal 'bar', @cache.read('foo')
end
@@ -557,12 +574,12 @@ class FileStoreTest < ActiveSupport::TestCase
key = @cache_with_pathname.send(:key_file_path, "views/index?id=1")
assert_equal "views/index?id=1", @cache_with_pathname.send(:file_path_key, key)
end
-
+
# Because file systems have a maximum filename size, filenames > max size should be split in to directories
# If filename is 'AAAAB', where max size is 4, the returned path should be AAAA/B
def test_key_transformation_max_filename_size
key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}B"
- path = @cache.send(:key_file_path, key)
+ path = @cache.send(:key_file_path, key)
assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
assert_equal 'B', File.basename(path)
end
@@ -595,11 +612,11 @@ class MemoryStoreTest < ActiveSupport::TestCase
@cache.read(2) && sleep(0.001)
@cache.read(4)
@cache.prune(@record_size * 3)
- assert_equal true, @cache.exist?(5)
- assert_equal true, @cache.exist?(4)
- assert_equal false, @cache.exist?(3)
- assert_equal true, @cache.exist?(2)
- assert_equal false, @cache.exist?(1)
+ assert @cache.exist?(5)
+ assert @cache.exist?(4)
+ assert !@cache.exist?(3)
+ assert @cache.exist?(2)
+ assert !@cache.exist?(1)
end
def test_prune_size_on_write
@@ -616,17 +633,17 @@ class MemoryStoreTest < ActiveSupport::TestCase
@cache.read(2) && sleep(0.001)
@cache.read(4) && sleep(0.001)
@cache.write(11, "llllllllll")
- assert_equal true, @cache.exist?(11)
- assert_equal true, @cache.exist?(10)
- assert_equal true, @cache.exist?(9)
- assert_equal true, @cache.exist?(8)
- assert_equal true, @cache.exist?(7)
- assert_equal false, @cache.exist?(6)
- assert_equal false, @cache.exist?(5)
- assert_equal true, @cache.exist?(4)
- assert_equal false, @cache.exist?(3)
- assert_equal true, @cache.exist?(2)
- assert_equal false, @cache.exist?(1)
+ assert @cache.exist?(11)
+ assert @cache.exist?(10)
+ assert @cache.exist?(9)
+ assert @cache.exist?(8)
+ assert @cache.exist?(7)
+ assert !@cache.exist?(6)
+ assert !@cache.exist?(5)
+ assert @cache.exist?(4)
+ assert !@cache.exist?(3)
+ assert @cache.exist?(2)
+ assert !@cache.exist?(1)
end
def test_pruning_is_capped_at_a_max_time
@@ -640,11 +657,11 @@ class MemoryStoreTest < ActiveSupport::TestCase
@cache.write(4, "dddddddddd") && sleep(0.001)
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
@cache.prune(30, 0.001)
- assert_equal true, @cache.exist?(5)
- assert_equal true, @cache.exist?(4)
- assert_equal true, @cache.exist?(3)
- assert_equal true, @cache.exist?(2)
- assert_equal false, @cache.exist?(1)
+ assert @cache.exist?(5)
+ assert @cache.exist?(4)
+ assert @cache.exist?(3)
+ assert @cache.exist?(2)
+ assert !@cache.exist?(1)
end
end
@@ -656,7 +673,7 @@ uses_memcached 'memcached backed store' do
@data = @cache.instance_variable_get(:@data)
@cache.clear
@cache.silence!
- @cache.logger = Logger.new("/dev/null")
+ @cache.logger = ActiveSupport::Logger.new("/dev/null")
end
include CacheStoreBehavior
@@ -670,14 +687,14 @@ uses_memcached 'memcached backed store' do
cache.write("foo", 2)
assert_equal "2", cache.read("foo")
end
-
+
def test_raw_values_with_marshal
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
cache.write("foo", Marshal.dump([]))
- assert_equal [], cache.read("foo")
+ assert_equal [], cache.read("foo")
end
-
+
def test_local_cache_raw_values
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
@@ -698,12 +715,70 @@ uses_memcached 'memcached backed store' do
end
end
+class NullStoreTest < ActiveSupport::TestCase
+ def setup
+ @cache = ActiveSupport::Cache.lookup_store(:null_store)
+ end
+
+ def test_clear
+ @cache.clear
+ end
+
+ def test_cleanup
+ @cache.cleanup
+ end
+
+ def test_write
+ assert_equal true, @cache.write("name", "value")
+ end
+
+ def test_read
+ @cache.write("name", "value")
+ assert_nil @cache.read("name")
+ end
+
+ def test_delete
+ @cache.write("name", "value")
+ assert_equal false, @cache.delete("name")
+ end
+
+ def test_increment
+ @cache.write("name", 1, :raw => true)
+ assert_nil @cache.increment("name")
+ end
+
+ def test_decrement
+ @cache.write("name", 1, :raw => true)
+ assert_nil @cache.increment("name")
+ end
+
+ def test_delete_matched
+ @cache.write("name", "value")
+ @cache.delete_matched(/name/)
+ end
+
+ def test_local_store_strategy
+ @cache.with_local_cache do
+ @cache.write("name", "value")
+ assert_equal "value", @cache.read("name")
+ @cache.delete("name")
+ assert_nil @cache.read("name")
+ @cache.write("name", "value")
+ end
+ assert_nil @cache.read("name")
+ end
+
+ def test_setting_nil_cache_store
+ assert ActiveSupport::Cache.lookup_store.class.name, ActiveSupport::Cache::NullStore.name
+ end
+end
+
class CacheStoreLoggerTest < ActiveSupport::TestCase
def setup
@cache = ActiveSupport::Cache.lookup_store(:memory_store)
@buffer = StringIO.new
- @cache.logger = Logger.new(@buffer)
+ @cache.logger = ActiveSupport::Logger.new(@buffer)
end
def test_logging
@@ -723,7 +798,7 @@ class CacheEntryTest < ActiveSupport::TestCase
entry = ActiveSupport::Cache::Entry.create("raw", time, :compress => false, :expires_in => 300)
assert_equal "raw", entry.raw_value
assert_equal time.to_f, entry.created_at
- assert_equal false, entry.compressed?
+ assert !entry.compressed?
assert_equal 300, entry.expires_in
end
@@ -740,7 +815,7 @@ class CacheEntryTest < ActiveSupport::TestCase
def test_compress_values
entry = ActiveSupport::Cache::Entry.new("value", :compress => true, :compress_threshold => 1)
assert_equal "value", entry.value
- assert_equal true, entry.compressed?
+ assert entry.compressed?
assert_equal "value", Marshal.load(Zlib::Inflate.inflate(entry.raw_value))
end
@@ -748,6 +823,6 @@ class CacheEntryTest < ActiveSupport::TestCase
entry = ActiveSupport::Cache::Entry.new("value")
assert_equal "value", entry.value
assert_equal "value", Marshal.load(entry.raw_value)
- assert_equal false, entry.compressed?
+ assert !entry.compressed?
end
end
diff --git a/activesupport/test/callback_inheritance_test.rb b/activesupport/test/callback_inheritance_test.rb
index 06259c648c..e5ac9511df 100644
--- a/activesupport/test/callback_inheritance_test.rb
+++ b/activesupport/test/callback_inheritance_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'test/unit'
class GrandParent
include ActiveSupport::Callbacks
@@ -10,8 +9,8 @@ class GrandParent
end
define_callbacks :dispatch
- set_callback :dispatch, :before, :before1, :before2, :per_key => {:if => proc {|c| c.action_name == "index" || c.action_name == "update" }}
- set_callback :dispatch, :after, :after1, :after2, :per_key => {:if => proc {|c| c.action_name == "update" || c.action_name == "delete" }}
+ set_callback :dispatch, :before, :before1, :before2, :if => proc {|c| c.action_name == "index" || c.action_name == "update" }
+ set_callback :dispatch, :after, :after1, :after2, :if => proc {|c| c.action_name == "update" || c.action_name == "delete" }
def before1
@log << "before1"
@@ -38,12 +37,12 @@ class GrandParent
end
class Parent < GrandParent
- skip_callback :dispatch, :before, :before2, :per_key => {:unless => proc {|c| c.action_name == "update" }}
- skip_callback :dispatch, :after, :after2, :per_key => {:unless => proc {|c| c.action_name == "delete" }}
+ skip_callback :dispatch, :before, :before2, :unless => proc {|c| c.action_name == "update" }
+ skip_callback :dispatch, :after, :after2, :unless => proc {|c| c.action_name == "delete" }
end
class Child < GrandParent
- skip_callback :dispatch, :before, :before2, :per_key => {:unless => proc {|c| c.action_name == "update" }}, :if => :state_open?
+ skip_callback :dispatch, :before, :before2, :unless => proc {|c| c.action_name == "update" }, :if => :state_open?
def state_open?
@state == :open
@@ -105,7 +104,7 @@ end
class CountingChild < CountingParent
end
-class BasicCallbacksTest < Test::Unit::TestCase
+class BasicCallbacksTest < ActiveSupport::TestCase
def setup
@index = GrandParent.new("index").dispatch
@update = GrandParent.new("update").dispatch
@@ -113,20 +112,20 @@ class BasicCallbacksTest < Test::Unit::TestCase
@unknown = GrandParent.new("unknown").dispatch
end
- def test_basic_per_key1
+ def test_basic_conditional_callback1
assert_equal %w(before1 before2 index), @index.log
end
- def test_basic_per_key2
+ def test_basic_conditional_callback2
assert_equal %w(before1 before2 update after2 after1), @update.log
end
- def test_basic_per_key3
+ def test_basic_conditional_callback3
assert_equal %w(delete after2 after1), @delete.log
end
end
-class InheritedCallbacksTest < Test::Unit::TestCase
+class InheritedCallbacksTest < ActiveSupport::TestCase
def setup
@index = Parent.new("index").dispatch
@update = Parent.new("update").dispatch
@@ -147,7 +146,7 @@ class InheritedCallbacksTest < Test::Unit::TestCase
end
end
-class InheritedCallbacksTest2 < Test::Unit::TestCase
+class InheritedCallbacksTest2 < ActiveSupport::TestCase
def setup
@update1 = Child.new("update", :open).dispatch
@update2 = Child.new("update", :closed).dispatch
@@ -162,7 +161,7 @@ class InheritedCallbacksTest2 < Test::Unit::TestCase
end
end
-class DynamicInheritedCallbacks < Test::Unit::TestCase
+class DynamicInheritedCallbacks < ActiveSupport::TestCase
def test_callbacks_looks_to_the_superclass_before_running
child = EmptyChild.new.dispatch
assert !child.performed?
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 2b4adda4d1..3c995e0793 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'test/unit'
module CallbacksTest
class Phone
@@ -96,7 +95,7 @@ module CallbacksTest
define_callbacks :dispatch
- set_callback :dispatch, :before, :log, :per_key => {:unless => proc {|c| c.action_name == :index || c.action_name == :show }}
+ set_callback :dispatch, :before, :log, :unless => proc {|c| c.action_name == :index || c.action_name == :show }
set_callback :dispatch, :after, :log2
attr_reader :action_name, :logger
@@ -121,7 +120,7 @@ module CallbacksTest
end
class Child < ParentController
- skip_callback :dispatch, :before, :log, :per_key => {:if => proc {|c| c.action_name == :update} }
+ skip_callback :dispatch, :before, :log, :if => proc {|c| c.action_name == :update}
skip_callback :dispatch, :after, :log2
end
@@ -132,10 +131,10 @@ module CallbacksTest
super
end
- before_save Proc.new {|r| r.history << [:before_save, :starts_true, :if] }, :per_key => {:if => :starts_true}
- before_save Proc.new {|r| r.history << [:before_save, :starts_false, :if] }, :per_key => {:if => :starts_false}
- before_save Proc.new {|r| r.history << [:before_save, :starts_true, :unless] }, :per_key => {:unless => :starts_true}
- before_save Proc.new {|r| r.history << [:before_save, :starts_false, :unless] }, :per_key => {:unless => :starts_false}
+ before_save Proc.new {|r| r.history << [:before_save, :starts_true, :if] }, :if => :starts_true
+ before_save Proc.new {|r| r.history << [:before_save, :starts_false, :if] }, :if => :starts_false
+ before_save Proc.new {|r| r.history << [:before_save, :starts_true, :unless] }, :unless => :starts_true
+ before_save Proc.new {|r| r.history << [:before_save, :starts_false, :unless] }, :unless => :starts_false
def starts_true
if @@starts_true
@@ -158,7 +157,7 @@ module CallbacksTest
end
end
- class OneTimeCompileTest < Test::Unit::TestCase
+ class OneTimeCompileTest < ActiveSupport::TestCase
def test_optimized_first_compile
around = OneTimeCompile.new
around.save
@@ -177,7 +176,7 @@ module CallbacksTest
end
end
- class AfterSaveConditionalPersonCallbackTest < Test::Unit::TestCase
+ class AfterSaveConditionalPersonCallbackTest < ActiveSupport::TestCase
def test_after_save_runs_in_the_reverse_order
person = AfterSaveConditionalPerson.new
person.save
@@ -330,7 +329,7 @@ module CallbacksTest
define_callbacks :save
attr_reader :stuff
- set_callback :save, :before, :action, :per_key => {:if => :yes}
+ set_callback :save, :before, :action, :if => :yes
def yes() true end
@@ -345,7 +344,55 @@ module CallbacksTest
end
end
- class AroundCallbacksTest < Test::Unit::TestCase
+ module ExtendModule
+ def self.extended(base)
+ base.class_eval do
+ set_callback :save, :before, :record3
+ end
+ end
+ def record3
+ @recorder << 3
+ end
+ end
+
+ module IncludeModule
+ def self.included(base)
+ base.class_eval do
+ set_callback :save, :before, :record2
+ end
+ end
+ def record2
+ @recorder << 2
+ end
+ end
+
+ class ExtendCallbacks
+
+ include ActiveSupport::Callbacks
+
+ define_callbacks :save
+ set_callback :save, :before, :record1
+
+ include IncludeModule
+
+ def save
+ run_callbacks :save
+ end
+
+ attr_reader :recorder
+
+ def initialize
+ @recorder = []
+ end
+
+ private
+
+ def record1
+ @recorder << 1
+ end
+ end
+
+ class AroundCallbacksTest < ActiveSupport::TestCase
def test_save_around
around = AroundPerson.new
around.save
@@ -364,7 +411,7 @@ module CallbacksTest
end
end
- class AroundCallbackResultTest < Test::Unit::TestCase
+ class AroundCallbackResultTest < ActiveSupport::TestCase
def test_save_around
around = AroundPersonResult.new
around.save
@@ -372,7 +419,7 @@ module CallbacksTest
end
end
- class SkipCallbacksTest < Test::Unit::TestCase
+ class SkipCallbacksTest < ActiveSupport::TestCase
def test_skip_person
person = PersonSkipper.new
assert_equal [], person.history
@@ -391,7 +438,7 @@ module CallbacksTest
end
end
- class CallbacksTest < Test::Unit::TestCase
+ class CallbacksTest < ActiveSupport::TestCase
def test_save_phone
phone = Phone.new
assert_raise RuntimeError do
@@ -419,7 +466,7 @@ module CallbacksTest
end
end
- class ConditionalCallbackTest < Test::Unit::TestCase
+ class ConditionalCallbackTest < ActiveSupport::TestCase
def test_save_conditional_person
person = ConditionalPerson.new
person.save
@@ -437,7 +484,7 @@ module CallbacksTest
- class ResetCallbackTest < Test::Unit::TestCase
+ class ResetCallbackTest < ActiveSupport::TestCase
def test_save_conditional_person
person = CleanPerson.new
person.save
@@ -461,7 +508,7 @@ module CallbacksTest
set_callback :save, :after, :third
- attr_reader :history, :saved
+ attr_reader :history, :saved, :halted
def initialize
@history = []
end
@@ -490,6 +537,10 @@ module CallbacksTest
@saved = true
end
end
+
+ def halted_callback_hook(filter)
+ @halted = filter
+ end
end
class CallbackObject
@@ -563,7 +614,7 @@ module CallbacksTest
end
end
- class UsingObjectTest < Test::Unit::TestCase
+ class UsingObjectTest < ActiveSupport::TestCase
def test_before_object
u = UsingObjectBefore.new
u.save
@@ -588,13 +639,19 @@ module CallbacksTest
end
end
- class CallbackTerminatorTest < Test::Unit::TestCase
+ class CallbackTerminatorTest < ActiveSupport::TestCase
def test_termination
terminator = CallbackTerminator.new
terminator.save
assert_equal ["first", "second", "third", "second", "first"], terminator.history
end
+ def test_termination_invokes_hook
+ terminator = CallbackTerminator.new
+ terminator.save
+ assert_equal ":second", terminator.halted
+ end
+
def test_block_never_called_if_terminated
obj = CallbackTerminator.new
obj.save
@@ -602,7 +659,7 @@ module CallbacksTest
end
end
- class HyphenatedKeyTest < Test::Unit::TestCase
+ class HyphenatedKeyTest < ActiveSupport::TestCase
def test_save
obj = HyphenatedCallbacks.new
obj.save
@@ -615,7 +672,7 @@ module CallbacksTest
skip_callback :save, :before, :before_save_method, :if => lambda {self.age > 21}
end
- class WriterCallbacksTest < Test::Unit::TestCase
+ class WriterCallbacksTest < ActiveSupport::TestCase
def test_skip_writer
writer = WriterSkipper.new
writer.age = 18
@@ -636,4 +693,28 @@ module CallbacksTest
end
end
+ class ExtendCallbacksTest < ActiveSupport::TestCase
+ def test_save
+ model = ExtendCallbacks.new.extend ExtendModule
+ model.save
+ assert_equal [1, 2, 3], model.recorder
+ end
+ end
+
+ class PerKeyOptionDeprecationTest < ActiveSupport::TestCase
+
+ def test_per_key_option_deprecaton
+ assert_raise NotImplementedError do
+ Phone.class_eval do
+ set_callback :save, :before, :before_save1, :per_key => {:if => "true"}
+ end
+ end
+ assert_raise NotImplementedError do
+ Phone.class_eval do
+ skip_callback :save, :before, :before_save1, :per_key => {:if => "true"}
+ end
+ end
+ end
+ end
+
end
diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb
index 752c0ee478..b96f476ce6 100644
--- a/activesupport/test/class_cache_test.rb
+++ b/activesupport/test/class_cache_test.rb
@@ -10,45 +10,58 @@ module ActiveSupport
def test_empty?
assert @cache.empty?
- @cache[ClassCacheTest] = ClassCacheTest
+ @cache.store(ClassCacheTest)
assert !@cache.empty?
end
def test_clear!
assert @cache.empty?
- @cache[ClassCacheTest] = ClassCacheTest
+ @cache.store(ClassCacheTest)
assert !@cache.empty?
@cache.clear!
assert @cache.empty?
end
def test_set_key
- @cache[ClassCacheTest] = ClassCacheTest
+ @cache.store(ClassCacheTest)
assert @cache.key?(ClassCacheTest.name)
end
- def test_set_rejects_strings
- @cache[ClassCacheTest.name] = ClassCacheTest
- assert @cache.empty?
- end
-
def test_get_with_class
- @cache[ClassCacheTest] = ClassCacheTest
- assert_equal ClassCacheTest, @cache[ClassCacheTest]
+ @cache.store(ClassCacheTest)
+ assert_equal ClassCacheTest, @cache.get(ClassCacheTest)
end
def test_get_with_name
- @cache[ClassCacheTest] = ClassCacheTest
- assert_equal ClassCacheTest, @cache[ClassCacheTest.name]
+ @cache.store(ClassCacheTest)
+ assert_equal ClassCacheTest, @cache.get(ClassCacheTest.name)
end
def test_get_constantizes
assert @cache.empty?
- assert_equal ClassCacheTest, @cache[ClassCacheTest.name]
+ assert_equal ClassCacheTest, @cache.get(ClassCacheTest.name)
end
- def test_get_is_an_alias
- assert_equal @cache[ClassCacheTest], @cache.get(ClassCacheTest.name)
+ def test_get_constantizes_fails_on_invalid_names
+ assert @cache.empty?
+ assert_raise NameError do
+ @cache.get("OmgTotallyInvalidConstantName")
+ end
+ end
+
+ def test_get_alias
+ assert @cache.empty?
+ assert_equal @cache[ClassCacheTest.name], @cache.get(ClassCacheTest.name)
+ end
+
+ def test_safe_get_constantizes
+ assert @cache.empty?
+ assert_equal ClassCacheTest, @cache.safe_get(ClassCacheTest.name)
+ end
+
+ def test_safe_get_constantizes_doesnt_fail_on_invalid_names
+ assert @cache.empty?
+ assert_equal nil, @cache.safe_get("OmgTotallyInvalidConstantName")
end
def test_new_rejects_strings
diff --git a/activesupport/test/clean_logger_test.rb b/activesupport/test/clean_logger_test.rb
index 2cc46904b4..02693a97dc 100644
--- a/activesupport/test/clean_logger_test.rb
+++ b/activesupport/test/clean_logger_test.rb
@@ -1,11 +1,11 @@
require 'abstract_unit'
require 'stringio'
-require 'active_support/core_ext/logger'
+require 'active_support/logger'
-class CleanLoggerTest < Test::Unit::TestCase
+class CleanLoggerTest < ActiveSupport::TestCase
def setup
@out = StringIO.new
- @logger = Logger.new(@out)
+ @logger = ActiveSupport::Logger.new(@out)
end
def test_format_message
@@ -13,40 +13,11 @@ class CleanLoggerTest < Test::Unit::TestCase
assert_equal "error\n", @out.string
end
- def test_silence
- # Without yielding self.
- @logger.silence do
- @logger.debug 'debug'
- @logger.info 'info'
- @logger.warn 'warn'
- @logger.error 'error'
- @logger.fatal 'fatal'
- end
-
- # Yielding self.
- @logger.silence do |logger|
- logger.debug 'debug'
- logger.info 'info'
- logger.warn 'warn'
- logger.error 'error'
- logger.fatal 'fatal'
- end
-
- # Silencer off.
- Logger.silencer = false
- @logger.silence do |logger|
- logger.warn 'unsilenced'
- end
- Logger.silencer = true
-
- assert_equal "error\nfatal\nerror\nfatal\nunsilenced\n", @out.string
- end
-
def test_datetime_format
@logger.formatter = Logger::Formatter.new
- @logger.datetime_format = "%Y-%m-%d"
+ @logger.formatter.datetime_format = "%Y-%m-%d"
@logger.debug 'debug'
- assert_equal "%Y-%m-%d", @logger.datetime_format
+ assert_equal "%Y-%m-%d", @logger.formatter.datetime_format
assert_match(/D, \[\d\d\d\d-\d\d-\d\d#\d+\] DEBUG -- : debug/, @out.string)
end
diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb
index 4cbe56a2d2..912ce30c29 100644
--- a/activesupport/test/concern_test.rb
+++ b/activesupport/test/concern_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/concern'
-class ConcernTest < Test::Unit::TestCase
+class ConcernTest < ActiveSupport::TestCase
module Baz
extend ActiveSupport::Concern
@@ -19,9 +19,6 @@ class ConcernTest < Test::Unit::TestCase
end
end
- module InstanceMethods
- end
-
included do
self.included_ran = true
end
@@ -74,7 +71,7 @@ class ConcernTest < Test::Unit::TestCase
def test_instance_methods_are_included
@klass.send(:include, Baz)
assert_equal "baz", @klass.new.baz
- assert @klass.included_modules.include?(ConcernTest::Baz::InstanceMethods)
+ assert @klass.included_modules.include?(ConcernTest::Baz)
end
def test_included_block_is_ran
@@ -92,6 +89,6 @@ class ConcernTest < Test::Unit::TestCase
def test_dependencies_with_multiple_modules
@klass.send(:include, Foo)
- assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz::InstanceMethods, ConcernTest::Baz], @klass.included_modules[0..3]
+ assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2]
end
end
diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb
index 81d200a0c8..135f894056 100644
--- a/activesupport/test/constantize_test_cases.rb
+++ b/activesupport/test/constantize_test_cases.rb
@@ -19,7 +19,7 @@ module ConstantizeTestCases
assert_raise(NameError) { yield("Ace::ConstantizeTestCases") }
assert_raise(NameError) { yield("Ace::Base::ConstantizeTestCases") }
end
-
+
def run_safe_constantize_tests_on
assert_nothing_raised { assert_equal Ace::Base::Case, yield("Ace::Base::Case") }
assert_nothing_raised { assert_equal Ace::Base::Case, yield("::Ace::Base::Case") }
@@ -33,5 +33,6 @@ module ConstantizeTestCases
assert_nothing_raised { assert_equal nil, yield("blargle") }
assert_nothing_raised { assert_equal nil, yield("Ace::ConstantizeTestCases") }
assert_nothing_raised { assert_equal nil, yield("Ace::Base::ConstantizeTestCases") }
+ assert_nothing_raised { assert_equal nil, yield("#<Class:0x7b8b718b>::Nested_1") }
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 52231aaeb0..58835c0ac5 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -6,7 +6,7 @@ require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions
require 'active_support/hash_with_indifferent_access'
-class ArrayExtAccessTests < Test::Unit::TestCase
+class ArrayExtAccessTests < ActiveSupport::TestCase
def test_from
assert_equal %w( a b c d ), %w( a b c d ).from(0)
assert_equal %w( c d ), %w( a b c d ).from(2)
@@ -30,7 +30,7 @@ class ArrayExtAccessTests < Test::Unit::TestCase
end
end
-class ArrayExtToParamTests < Test::Unit::TestCase
+class ArrayExtToParamTests < ActiveSupport::TestCase
class ToParam < String
def to_param
"#{self}1"
@@ -52,7 +52,7 @@ class ArrayExtToParamTests < Test::Unit::TestCase
end
end
-class ArrayExtToSentenceTests < Test::Unit::TestCase
+class ArrayExtToSentenceTests < ActiveSupport::TestCase
def test_plain_array_to_sentence
assert_equal "", [].to_sentence
assert_equal "one", ['one'].to_sentence
@@ -92,7 +92,7 @@ class ArrayExtToSentenceTests < Test::Unit::TestCase
end
end
-class ArrayExtToSTests < Test::Unit::TestCase
+class ArrayExtToSTests < ActiveSupport::TestCase
def test_to_s_db
collection = [
Class.new { def id() 1 end }.new,
@@ -105,7 +105,7 @@ class ArrayExtToSTests < Test::Unit::TestCase
end
end
-class ArrayExtGroupingTests < Test::Unit::TestCase
+class ArrayExtGroupingTests < ActiveSupport::TestCase
def test_in_groups_of_with_perfect_fit
groups = []
('a'..'i').to_a.in_groups_of(3) do |group|
@@ -188,7 +188,7 @@ class ArrayExtGroupingTests < Test::Unit::TestCase
end
end
-class ArraySplitTests < Test::Unit::TestCase
+class ArraySplitTests < ActiveSupport::TestCase
def test_split_with_empty_array
assert_equal [[]], [].split(0)
end
@@ -209,7 +209,7 @@ class ArraySplitTests < Test::Unit::TestCase
end
end
-class ArrayToXmlTests < Test::Unit::TestCase
+class ArrayToXmlTests < ActiveSupport::TestCase
def test_to_xml
xml = [
{ :name => "David", :age => 26, :age_in_millis => 820497600000 },
@@ -299,7 +299,7 @@ class ArrayToXmlTests < Test::Unit::TestCase
end
end
-class ArrayExtractOptionsTests < Test::Unit::TestCase
+class ArrayExtractOptionsTests < ActiveSupport::TestCase
class HashSubclass < Hash
end
@@ -341,57 +341,37 @@ class ArrayExtractOptionsTests < Test::Unit::TestCase
end
end
-class ArrayUniqByTests < Test::Unit::TestCase
+class ArrayUniqByTests < ActiveSupport::TestCase
def test_uniq_by
- assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? }
- assert_equal [1,2], [1,2,3,4].uniq_by(&:even?)
- assert_equal((-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 })
+ ActiveSupport::Deprecation.silence do
+ assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? }
+ assert_equal [1,2], [1,2,3,4].uniq_by(&:even?)
+ assert_equal((-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 })
+ end
end
def test_uniq_by!
a = [1,2,3,4]
- a.uniq_by! { |i| i.odd? }
+ ActiveSupport::Deprecation.silence do
+ a.uniq_by! { |i| i.odd? }
+ end
assert_equal [1,2], a
a = [1,2,3,4]
- a.uniq_by! { |i| i.even? }
+ ActiveSupport::Deprecation.silence do
+ a.uniq_by! { |i| i.even? }
+ end
assert_equal [1,2], a
a = (-5..5).to_a
- a.uniq_by! { |i| i**2 }
+ ActiveSupport::Deprecation.silence do
+ a.uniq_by! { |i| i**2 }
+ end
assert_equal((-5..0).to_a, a)
end
end
-class ArrayExtRandomTests < ActiveSupport::TestCase
- def test_sample_from_array
- assert_nil [].sample
- assert_equal [], [].sample(5)
- assert_equal 42, [42].sample
- assert_equal [42], [42].sample(5)
-
- a = [:foo, :bar, 42]
- s = a.sample(2)
- assert_equal 2, s.size
- assert_equal 1, (a-s).size
- assert_equal [], a-(0..20).sum{a.sample(2)}
-
- o = Object.new
- def o.to_int; 1; end
- assert_equal [0], [0].sample(o)
-
- o = Object.new
- assert_raises(TypeError) { [0].sample(o) }
-
- o = Object.new
- def o.to_int; ''; end
- assert_raises(TypeError) { [0].sample(o) }
-
- assert_raises(ArgumentError) { [0].sample(-7) }
- end
-end
-
-class ArrayWrapperTests < Test::Unit::TestCase
+class ArrayWrapperTests < ActiveSupport::TestCase
class FakeCollection
def to_ary
["foo", "bar"]
@@ -466,7 +446,7 @@ class ArrayWrapperTests < Test::Unit::TestCase
end
end
-class ArrayPrependAppendTest < Test::Unit::TestCase
+class ArrayPrependAppendTest < ActiveSupport::TestCase
def test_append
assert_equal [1, 2], [1].append(2)
end
diff --git a/activesupport/test/core_ext/base64_ext_test.rb b/activesupport/test/core_ext/base64_ext_test.rb
deleted file mode 100644
index bd0e9f843d..0000000000
--- a/activesupport/test/core_ext/base64_ext_test.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require 'abstract_unit'
-
-class Base64Test < Test::Unit::TestCase
- def test_no_newline_in_encoded_value
- assert_match(/\n/, ActiveSupport::Base64.encode64("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64"))
- assert_no_match(/\n/, ActiveSupport::Base64.encode64s("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64"))
- end
-end
diff --git a/activesupport/test/core_ext/bigdecimal_test.rb b/activesupport/test/core_ext/bigdecimal_test.rb
index b38e08a9f4..e24a089650 100644
--- a/activesupport/test/core_ext/bigdecimal_test.rb
+++ b/activesupport/test/core_ext/bigdecimal_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'bigdecimal'
require 'active_support/core_ext/big_decimal'
-class BigDecimalTest < Test::Unit::TestCase
+class BigDecimalTest < ActiveSupport::TestCase
def test_to_yaml
assert_match("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml)
assert_match("--- .Inf\n", BigDecimal.new('Infinity').to_yaml)
diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/blank_test.rb
index a2cf298905..a68c074777 100644
--- a/activesupport/test/core_ext/blank_test.rb
+++ b/activesupport/test/core_ext/blank_test.rb
@@ -3,7 +3,7 @@
require 'abstract_unit'
require 'active_support/core_ext/object/blank'
-class BlankTest < Test::Unit::TestCase
+class BlankTest < ActiveSupport::TestCase
BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', [], {} ]
NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ]
diff --git a/activesupport/test/core_ext/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb
index 6b50f8db37..3822e7af66 100644
--- a/activesupport/test/core_ext/class/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/class/attribute_accessor_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/class/attribute_accessors'
-class ClassAttributeAccessorTest < Test::Unit::TestCase
+class ClassAttributeAccessorTest < ActiveSupport::TestCase
def setup
@class = Class.new do
cattr_accessor :foo
diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb
index cbfb290c48..148f82946c 100644
--- a/activesupport/test/core_ext/class/delegating_attributes_test.rb
+++ b/activesupport/test/core_ext/class/delegating_attributes_test.rb
@@ -20,7 +20,7 @@ module DelegatingFixtures
end
end
-class DelegatingAttributesTest < Test::Unit::TestCase
+class DelegatingAttributesTest < ActiveSupport::TestCase
include DelegatingFixtures
attr_reader :single_class
diff --git a/activesupport/test/core_ext/class_test.rb b/activesupport/test/core_ext/class_test.rb
index 60ba3b8f88..9c6c579ef7 100644
--- a/activesupport/test/core_ext/class_test.rb
+++ b/activesupport/test/core_ext/class_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'active_support/core_ext/class'
require 'set'
-class ClassTest < Test::Unit::TestCase
+class ClassTest < ActiveSupport::TestCase
class Parent; end
class Foo < Parent; end
class Bar < Foo; end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index b4f848cd44..6e91fdedce 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -57,6 +57,16 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
assert_equal Date.new(2005,11,28), Date.new(2005,12,04).beginning_of_week #sunday
end
+ def test_monday
+ assert_equal Date.new(2005,11,28), Date.new(2005,11,28).monday
+ assert_equal Date.new(2005,11,28), Date.new(2005,12,01).monday
+ end
+
+ def test_sunday
+ assert_equal Date.new(2008,3,2), Date.new(2008,3,02).sunday
+ assert_equal Date.new(2008,3,2), Date.new(2008,2,29).sunday
+ end
+
def test_beginning_of_week_in_calendar_reform
assert_equal Date.new(1582,10,1), Date.new(1582,10,15).beginning_of_week #friday
end
@@ -374,16 +384,6 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
end
- if RUBY_VERSION < '1.9'
- def test_rfc3339
- assert_equal('1980-02-28', Date.new(1980, 2, 28).rfc3339)
- end
-
- def test_iso8601
- assert_equal('1980-02-28', Date.new(1980, 2, 28).iso8601)
- end
- end
-
def test_today
Date.stubs(:current).returns(Date.new(2000, 1, 1))
assert_equal false, Date.new(1999, 12, 31).today?
@@ -444,7 +444,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
end
-class DateExtBehaviorTest < Test::Unit::TestCase
+class DateExtBehaviorTest < ActiveSupport::TestCase
def test_date_acts_like_date
assert Date.new.acts_like_date?
end
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 456736cbad..57b5b95a66 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
-class DateTimeExtCalculationsTest < Test::Unit::TestCase
+class DateTimeExtCalculationsTest < ActiveSupport::TestCase
def test_to_s
datetime = DateTime.new(2005, 2, 21, 14, 30, 0, 0)
assert_equal "2005-02-21 14:30:00", datetime.to_s(:db)
@@ -34,8 +34,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
end
def test_to_time
- assert_equal Time.utc(2005, 2, 21, 10, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0, 0).to_time
- assert_equal Time.utc_time(2039, 2, 21, 10, 11, 12), DateTime.new(2039, 2, 21, 10, 11, 12, 0, 0).to_time
+ assert_equal Time.utc(2005, 2, 21, 10, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+ assert_equal Time.utc_time(2039, 2, 21, 10, 11, 12), DateTime.new(2039, 2, 21, 10, 11, 12, 0).to_time
# DateTimes with offsets other than 0 are returned unaltered
assert_equal DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)), DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).to_time
# Fractional seconds are preserved
@@ -43,8 +43,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
end
def test_civil_from_format
- assert_equal DateTime.civil(2010, 5, 4, 0, 0, 0, DateTime.local_offset), DateTime.civil_from_format(:local, 2010, 5, 4)
- assert_equal DateTime.civil(2010, 5, 4, 0, 0, 0, 0), DateTime.civil_from_format(:utc, 2010, 5, 4)
+ assert_equal Time.local(2010, 5, 4, 0, 0, 0), DateTime.civil_from_format(:local, 2010, 5, 4)
+ assert_equal Time.utc(2010, 5, 4, 0, 0, 0), DateTime.civil_from_format(:utc, 2010, 5, 4)
end
def test_seconds_since_midnight
@@ -54,6 +54,24 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert_equal 86399,DateTime.civil(2005,1,1,23,59,59).seconds_since_midnight
end
+ def test_days_to_week_start
+ assert_equal 0, Time.local(2011,11,01,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 1, Time.local(2011,11,02,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 2, Time.local(2011,11,03,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 4, Time.local(2011,11,05,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 5, Time.local(2011,11,06,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 6, Time.local(2011,11,07,0,0,0).days_to_week_start(:tuesday)
+
+ assert_equal 3, Time.local(2011,11,03,0,0,0).days_to_week_start(:monday)
+ assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 3, Time.local(2011,11,05,0,0,0).days_to_week_start(:wednesday)
+ assert_equal 3, Time.local(2011,11,06,0,0,0).days_to_week_start(:thursday)
+ assert_equal 3, Time.local(2011,11,07,0,0,0).days_to_week_start(:friday)
+ assert_equal 3, Time.local(2011,11,8,0,0,0).days_to_week_start(:saturday)
+ assert_equal 3, Time.local(2011,11,9,0,0,0).days_to_week_start(:sunday)
+ end
+
def test_beginning_of_week
assert_equal DateTime.civil(2005,1,31), DateTime.civil(2005,2,4,10,10,10).beginning_of_week
assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,11,28,0,0,0).beginning_of_week #monday
@@ -99,7 +117,7 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert_equal DateTime.civil(2005,5,1,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(5)
assert_equal DateTime.civil(2005,4,24,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(6)
assert_equal DateTime.civil(2005,2,27,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(14)
- assert_equal DateTime.civil(2004,12,25,10), DateTime.civil(2005,1,1,10,0,0).weeks_ago(1)
+ assert_equal DateTime.civil(2004,12,25,10), DateTime.civil(2005,1,1,10,0,0).weeks_ago(1)
end
def test_months_ago
@@ -313,15 +331,6 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert DateTime.new.acts_like_time?
end
- def test_local_offset
- with_env_tz 'US/Eastern' do
- assert_equal Rational(-5, 24), DateTime.local_offset
- end
- with_env_tz 'US/Central' do
- assert_equal Rational(-6, 24), DateTime.local_offset
- end
- end
-
def test_utc?
assert_equal true, DateTime.civil(2005, 2, 21, 10, 11, 12).utc?
assert_equal true, DateTime.civil(2005, 2, 21, 10, 11, 12, 0).utc?
diff --git a/activesupport/test/core_ext/duplicable_test.rb b/activesupport/test/core_ext/duplicable_test.rb
index e48e6a7c45..3e54266051 100644
--- a/activesupport/test/core_ext/duplicable_test.rb
+++ b/activesupport/test/core_ext/duplicable_test.rb
@@ -3,7 +3,7 @@ require 'bigdecimal'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/numeric/time'
-class DuplicableTest < Test::Unit::TestCase
+class DuplicableTest < ActiveSupport::TestCase
RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56'), 5.seconds]
YES = ['1', Object.new, /foo/, [], {}, Time.now]
NO = [Class.new, Module.new]
@@ -22,7 +22,7 @@ class DuplicableTest < Test::Unit::TestCase
end
RAISE_DUP.each do |v|
- assert_raises(TypeError) do
+ assert_raises(TypeError, v.class.name) do
v.dup
end
end
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index cdfa991a34..0bf48dd378 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -7,7 +7,7 @@ class SummablePayment < Payment
def +(p) self.class.new(price + p.price) end
end
-class EnumerableTests < Test::Unit::TestCase
+class EnumerableTests < ActiveSupport::TestCase
Enumerator = [].each.class
class GenericEnumerable
@@ -86,15 +86,6 @@ class EnumerableTests < Test::Unit::TestCase
assert_equal 'abc', ('a'..'c').sum
end
- def test_each_with_object
- enum = GenericEnumerable.new(%w(foo bar))
- result = enum.each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
- assert_equal({'foo' => 'FOO', 'bar' => 'BAR'}, result)
- assert_equal Enumerator, enum.each_with_object({}).class
- result2 = enum.each_with_object({}).each{|str, hsh| hsh[str] = str.upcase}
- assert_equal result, result2
- end
-
def test_index_by
payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) },
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index e1258b872e..50c9c57aa6 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/file'
-class AtomicWriteTest < Test::Unit::TestCase
+class AtomicWriteTest < ActiveSupport::TestCase
def test_atomic_write_without_errors
contents = "Atomic Text"
File.atomic_write(file_name, Dir.pwd) do |file|
@@ -57,10 +57,6 @@ class AtomicWriteTest < Test::Unit::TestCase
File.unlink(file_name) rescue nil
end
- def test_responds_to_to_path
- assert_equal __FILE__, File.open(__FILE__, "r").to_path
- end
-
private
def file_name
"atomic.file"
diff --git a/activesupport/test/core_ext/float_ext_test.rb b/activesupport/test/core_ext/float_ext_test.rb
deleted file mode 100644
index ac7e7a8ed6..0000000000
--- a/activesupport/test/core_ext/float_ext_test.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/float/rounding'
-
-class FloatExtRoundingTests < Test::Unit::TestCase
- def test_round_for_positive_number
- assert_equal 1, 1.4.round
- assert_equal 2, 1.6.round
- assert_equal 2, 1.6.round(0)
- assert_equal 1.4, 1.4.round(1)
- assert_equal 1.4, 1.4.round(3)
- assert_equal 1.5, 1.45.round(1)
- assert_equal 1.45, 1.445.round(2)
- end
-
- def test_round_for_negative_number
- assert_equal( -1, -1.4.round )
- assert_equal( -2, -1.6.round )
- assert_equal( -1.4, -1.4.round(1) )
- assert_equal( -1.5, -1.45.round(1) )
- end
-
- def test_round_with_negative_precision
- assert_equal 123460.0, 123456.0.round(-1)
- assert_equal 123500.0, 123456.0.round(-2)
- end
-end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index fa800eada2..a0f261ebdb 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -6,7 +6,7 @@ require 'active_support/ordered_hash'
require 'active_support/core_ext/object/conversions'
require 'active_support/inflections'
-class HashExtTest < Test::Unit::TestCase
+class HashExtTest < ActiveSupport::TestCase
class IndifferentHash < HashWithIndifferentAccess
end
@@ -27,11 +27,7 @@ class HashExtTest < Test::Unit::TestCase
@symbols = { :a => 1, :b => 2 }
@mixed = { :a => 1, 'b' => 2 }
@fixnums = { 0 => 1, 1 => 2 }
- if RUBY_VERSION < '1.9.0'
- @illegal_symbols = { "\0" => 1, "" => 2, [] => 3 }
- else
- @illegal_symbols = { [] => 3 }
- end
+ @illegal_symbols = { [] => 3 }
end
def test_methods
@@ -121,6 +117,9 @@ class HashExtTest < Test::Unit::TestCase
foo = { "foo" => NonIndifferentHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access
assert_kind_of NonIndifferentHash, foo["foo"]
+
+ foo = { "foo" => IndifferentHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access
+ assert_kind_of IndifferentHash, foo["foo"]
end
def test_indifferent_assorted
@@ -524,7 +523,7 @@ class IWriteMyOwnXML
end
end
-class HashExtToParamTests < Test::Unit::TestCase
+class HashExtToParamTests < ActiveSupport::TestCase
class ToParam < String
def to_param
"#{self}-1"
@@ -555,7 +554,7 @@ class HashExtToParamTests < Test::Unit::TestCase
end
end
-class HashToXmlTest < Test::Unit::TestCase
+class HashToXmlTest < ActiveSupport::TestCase
def setup
@xml_options = { :root => :person, :skip_instruct => true, :indent => 0 }
end
diff --git a/activesupport/test/core_ext/integer_ext_test.rb b/activesupport/test/core_ext/integer_ext_test.rb
index fe8c7eb224..41736fb672 100644
--- a/activesupport/test/core_ext/integer_ext_test.rb
+++ b/activesupport/test/core_ext/integer_ext_test.rb
@@ -1,7 +1,9 @@
require 'abstract_unit'
require 'active_support/core_ext/integer'
-class IntegerExtTest < Test::Unit::TestCase
+class IntegerExtTest < ActiveSupport::TestCase
+ PRIME = 22953686867719691230002707821868552601124472329079
+
def test_multiple_of
[ -7, 0, 7, 14 ].each { |i| assert i.multiple_of?(7) }
[ -7, 7, 14 ].each { |i| assert ! i.multiple_of?(6) }
@@ -11,10 +13,7 @@ class IntegerExtTest < Test::Unit::TestCase
assert !5.multiple_of?(0)
# test with a prime
- assert !22953686867719691230002707821868552601124472329079.multiple_of?(2)
- assert !22953686867719691230002707821868552601124472329079.multiple_of?(3)
- assert !22953686867719691230002707821868552601124472329079.multiple_of?(5)
- assert !22953686867719691230002707821868552601124472329079.multiple_of?(7)
+ [2, 3, 5, 7].each { |i| assert !PRIME.multiple_of?(i) }
end
def test_ordinalize
@@ -22,6 +21,10 @@ class IntegerExtTest < Test::Unit::TestCase
# Its results are tested comprehensively in the inflector test cases.
assert_equal '1st', 1.ordinalize
assert_equal '8th', 8.ordinalize
- 1000000000000000000000000000000000000000000000000000000000000000000000.ordinalize
+ end
+
+ def test_ordinal
+ assert_equal 'st', 1.ordinal
+ assert_equal 'th', 8.ordinal
end
end
diff --git a/activesupport/test/core_ext/io_test.rb b/activesupport/test/core_ext/io_test.rb
deleted file mode 100644
index b9abf685da..0000000000
--- a/activesupport/test/core_ext/io_test.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'abstract_unit'
-
-require 'active_support/core_ext/io'
-
-class IOTest < Test::Unit::TestCase
- def test_binread_one_arg
- assert_equal File.read(__FILE__), IO.binread(__FILE__)
- end
-
- def test_binread_two_args
- assert_equal File.read(__FILE__).bytes.first(10).pack('C*'),
- IO.binread(__FILE__, 10)
- end
-
- def test_binread_three_args
- actual = IO.binread(__FILE__, 5, 10)
- expected = File.open(__FILE__, 'rb') { |f|
- f.seek 10
- f.read 5
- }
- assert_equal expected, actual
- end
-end
diff --git a/activesupport/test/core_ext/kernel_test.rb b/activesupport/test/core_ext/kernel_test.rb
index 995bc0751a..e90b9d454f 100644
--- a/activesupport/test/core_ext/kernel_test.rb
+++ b/activesupport/test/core_ext/kernel_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/kernel'
-class KernelTest < Test::Unit::TestCase
+class KernelTest < ActiveSupport::TestCase
def test_silence_warnings
silence_warnings { assert_nil $VERBOSE }
assert_equal 1234, silence_warnings { 1234 }
@@ -42,11 +42,6 @@ class KernelTest < Test::Unit::TestCase
assert_equal 1, silence_stderr { 1 }
end
- def test_singleton_class
- o = Object.new
- assert_equal class << o; self end, o.singleton_class
- end
-
def test_class_eval
o = Object.new
class << o; @x = 1; end
@@ -59,7 +54,7 @@ class KernelTest < Test::Unit::TestCase
end
end
-class KernelSuppressTest < Test::Unit::TestCase
+class KernelSuppressTest < ActiveSupport::TestCase
def test_reraise
assert_raise(LoadError) do
suppress(ArgumentError) { raise LoadError }
@@ -90,7 +85,7 @@ class MockStdErr
end
end
-class KernelDebuggerTest < Test::Unit::TestCase
+class KernelDebuggerTest < ActiveSupport::TestCase
def test_debugger_not_available_message_to_stderr
old_stderr = $stderr
$stderr = MockStdErr.new
@@ -112,4 +107,4 @@ class KernelDebuggerTest < Test::Unit::TestCase
ensure
Object.send(:remove_const, "Rails")
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/core_ext/load_error_test.rb b/activesupport/test/core_ext/load_error_test.rb
index d7b8f602ca..31863d0aca 100644
--- a/activesupport/test/core_ext/load_error_test.rb
+++ b/activesupport/test/core_ext/load_error_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/load_error'
-class TestMissingSourceFile < Test::Unit::TestCase
+class TestMissingSourceFile < ActiveSupport::TestCase
def test_with_require
assert_raise(MissingSourceFile) { require 'no_this_file_don\'t_exist' }
end
@@ -16,7 +16,7 @@ class TestMissingSourceFile < Test::Unit::TestCase
end
end
-class TestLoadError < Test::Unit::TestCase
+class TestLoadError < ActiveSupport::TestCase
def test_with_require
assert_raise(LoadError) { require 'no_this_file_don\'t_exist' }
end
diff --git a/activesupport/test/core_ext/module/attr_internal_test.rb b/activesupport/test/core_ext/module/attr_internal_test.rb
index 93578c9610..2aea14cf2b 100644
--- a/activesupport/test/core_ext/module/attr_internal_test.rb
+++ b/activesupport/test/core_ext/module/attr_internal_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/module/attr_internal'
-class AttrInternalTest < Test::Unit::TestCase
+class AttrInternalTest < ActiveSupport::TestCase
def setup
@target = Class.new
@instance = @target.new
diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb
index 29889b51e0..6a2ad2f241 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/module/attribute_accessors'
-class ModuleAttributeAccessorTest < Test::Unit::TestCase
+class ModuleAttributeAccessorTest < ActiveSupport::TestCase
def setup
m = @module = Module.new do
mattr_accessor :foo
diff --git a/activesupport/test/core_ext/module/attribute_aliasing_test.rb b/activesupport/test/core_ext/module/attribute_aliasing_test.rb
index 065c3531e0..29c3053b47 100644
--- a/activesupport/test/core_ext/module/attribute_aliasing_test.rb
+++ b/activesupport/test/core_ext/module/attribute_aliasing_test.rb
@@ -24,7 +24,7 @@ module AttributeAliasing
end
end
-class AttributeAliasingTest < Test::Unit::TestCase
+class AttributeAliasingTest < ActiveSupport::TestCase
def test_attribute_alias
e = AttributeAliasing::Email.new
diff --git a/activesupport/test/core_ext/module/remove_method_test.rb b/activesupport/test/core_ext/module/remove_method_test.rb
new file mode 100644
index 0000000000..4657f0c175
--- /dev/null
+++ b/activesupport/test/core_ext/module/remove_method_test.rb
@@ -0,0 +1,29 @@
+require 'abstract_unit'
+require 'active_support/core_ext/module/remove_method'
+
+module RemoveMethodTests
+ class A
+ def do_something
+ return 1
+ end
+
+ end
+end
+
+class RemoveMethodTest < ActiveSupport::TestCase
+
+ def test_remove_method_from_an_object
+ RemoveMethodTests::A.class_eval{
+ self.remove_possible_method(:do_something)
+ }
+ assert !RemoveMethodTests::A.new.respond_to?(:do_something)
+ end
+
+ def test_redefine_method_in_an_object
+ RemoveMethodTests::A.class_eval{
+ self.redefine_method(:do_something) { return 100 }
+ }
+ assert_equal 100, RemoveMethodTests::A.new.do_something
+ end
+
+end \ No newline at end of file
diff --git a/activesupport/test/core_ext/module/synchronization_test.rb b/activesupport/test/core_ext/module/synchronization_test.rb
deleted file mode 100644
index 6c407e2260..0000000000
--- a/activesupport/test/core_ext/module/synchronization_test.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'thread'
-require 'abstract_unit'
-
-require 'active_support/core_ext/class/attribute_accessors'
-require 'active_support/core_ext/module/synchronization'
-
-class SynchronizationTest < Test::Unit::TestCase
- def setup
- @target = Class.new
- @target.cattr_accessor :mutex, :instance_writer => false
- @target.mutex = Mutex.new
- @instance = @target.new
- end
-
- def test_synchronize_aliases_method_chain_with_synchronize
- @target.module_eval do
- attr_accessor :value
- synchronize :value, :with => :mutex
- end
- assert_respond_to @instance, :value_with_synchronization
- assert_respond_to @instance, :value_without_synchronization
- end
-
- def test_synchronize_does_not_change_behavior
- @target.module_eval do
- attr_accessor :value
- synchronize :value, :with => :mutex
- end
- expected = "some state"
- @instance.value = expected
- assert_equal expected, @instance.value
- end
-
- def test_synchronize_with_no_mutex_raises_an_argument_error
- assert_raise(ArgumentError) do
- @target.synchronize :to_s
- end
- end
-
- def test_double_synchronize_raises_an_argument_error
- @target.synchronize :to_s, :with => :mutex
- assert_raise(ArgumentError) do
- @target.synchronize :to_s, :with => :mutex
- end
- end
-
- def dummy_sync
- dummy = Object.new
- def dummy.synchronize
- @sync_count ||= 0
- @sync_count += 1
- yield
- end
- def dummy.sync_count; @sync_count; end
- dummy
- end
-
- def test_mutex_is_entered_during_method_call
- @target.mutex = dummy_sync
- @target.synchronize :to_s, :with => :mutex
- @instance.to_s
- @instance.to_s
- assert_equal 2, @target.mutex.sync_count
- end
-
- def test_can_synchronize_method_with_punctuation
- @target.module_eval do
- def dangerous?
- @dangerous
- end
- def dangerous!
- @dangerous = true
- end
- end
- @target.synchronize :dangerous?, :dangerous!, :with => :mutex
- @instance.dangerous!
- assert @instance.dangerous?
- end
-
- def test_can_synchronize_singleton_methods
- @target.mutex = dummy_sync
- class << @target
- synchronize :to_s, :with => :mutex
- end
- assert_respond_to @target, :to_s_without_synchronization
- assert_nothing_raised { @target.to_s; @target.to_s }
- assert_equal 2, @target.mutex.sync_count
- end
-end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index f11bf3dc69..09ca4e7296 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -68,7 +68,7 @@ class Name
end
end
-class ModuleTest < Test::Unit::TestCase
+class ModuleTest < ActiveSupport::TestCase
def setup
@david = Someone.new("David", Somewhere.new("Paulina", "Chicago"))
end
@@ -212,6 +212,12 @@ class ModuleTest < Test::Unit::TestCase
def test_local_constants
assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
end
+
+ def test_local_constant_names
+ ActiveSupport::Deprecation.silence do
+ assert_equal %w(Constant1 Constant3), Ab.local_constant_names
+ end
+ end
end
module BarMethodAliaser
@@ -245,7 +251,7 @@ module BarMethods
end
end
-class MethodAliasingTest < Test::Unit::TestCase
+class MethodAliasingTest < ActiveSupport::TestCase
def setup
Object.const_set :FooClassWithBarMethod, Class.new { def bar() 'bar' end }
@instance = FooClassWithBarMethod.new
diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb
index 6352484d04..03ce09f22a 100644
--- a/activesupport/test/core_ext/name_error_test.rb
+++ b/activesupport/test/core_ext/name_error_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/name_error'
-class NameErrorTest < Test::Unit::TestCase
+class NameErrorTest < ActiveSupport::TestCase
def test_name_error_should_set_missing_name
SomeNameThatNobodyWillUse____Really ? 1 : 0
flunk "?!?!"
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index 3a2452b4b0..1cb1e25d4c 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -3,7 +3,7 @@ require 'active_support/time'
require 'active_support/core_ext/numeric'
require 'active_support/core_ext/integer'
-class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase
+class NumericExtTimeAndDateTimeTest < ActiveSupport::TestCase
def setup
@now = Time.local(2005,2,10,15,30,45)
@dtnow = DateTime.civil(2005,2,10,15,30,45)
@@ -128,7 +128,7 @@ class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase
end
end
-class NumericExtDateTest < Test::Unit::TestCase
+class NumericExtDateTest < ActiveSupport::TestCase
def setup
@today = Date.today
end
@@ -151,7 +151,7 @@ class NumericExtDateTest < Test::Unit::TestCase
end
end
-class NumericExtSizeTest < Test::Unit::TestCase
+class NumericExtSizeTest < ActiveSupport::TestCase
def test_unit_in_terms_of_another
relationships = {
1024.bytes => 1.kilobyte,
diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb
index 1de857d678..22888333f5 100644
--- a/activesupport/test/core_ext/object/inclusion_test.rb
+++ b/activesupport/test/core_ext/object/inclusion_test.rb
@@ -1,7 +1,17 @@
require 'abstract_unit'
require 'active_support/core_ext/object/inclusion'
-class InTest < Test::Unit::TestCase
+class InTest < ActiveSupport::TestCase
+ def test_in_multiple_args
+ assert :b.in?(:a,:b)
+ assert !:c.in?(:a,:b)
+ end
+
+ def test_in_multiple_arrays
+ assert [1,2].in?([1,2],[2,3])
+ assert ![1,2].in?([1,3],[2,1])
+ end
+
def test_in_array
assert 1.in?([1,2])
assert !3.in?([1,2])
diff --git a/activesupport/test/core_ext/object/to_param_test.rb b/activesupport/test/core_ext/object/to_param_test.rb
index c3efefddb5..bd7c6c422a 100644
--- a/activesupport/test/core_ext/object/to_param_test.rb
+++ b/activesupport/test/core_ext/object/to_param_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/object/to_param'
-class ToParamTest < Test::Unit::TestCase
+class ToParamTest < ActiveSupport::TestCase
def test_object
foo = Object.new
def foo.to_s; 'foo' end
diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb
index c146f6cc9b..6a26e1fa4f 100644
--- a/activesupport/test/core_ext/object/to_query_test.rb
+++ b/activesupport/test/core_ext/object/to_query_test.rb
@@ -3,7 +3,7 @@ require 'active_support/ordered_hash'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/string/output_safety.rb'
-class ToQueryTest < Test::Unit::TestCase
+class ToQueryTest < ActiveSupport::TestCase
def test_simple_conversion
assert_query_equal 'a=10', :a => 10
end
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 782a01213d..b027fccab3 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -59,7 +59,7 @@ class ObjectTests < ActiveSupport::TestCase
end
end
-class ObjectInstanceVariableTest < Test::Unit::TestCase
+class ObjectInstanceVariableTest < ActiveSupport::TestCase
def setup
@source, @dest = Object.new, Object.new
@source.instance_variable_set(:@bar, 'bar')
@@ -91,7 +91,7 @@ class ObjectInstanceVariableTest < Test::Unit::TestCase
end
end
-class ObjectTryTest < Test::Unit::TestCase
+class ObjectTryTest < ActiveSupport::TestCase
def setup
@string = "Hello"
end
diff --git a/activesupport/test/core_ext/proc_test.rb b/activesupport/test/core_ext/proc_test.rb
index dc7b2c957d..690bfd3bf8 100644
--- a/activesupport/test/core_ext/proc_test.rb
+++ b/activesupport/test/core_ext/proc_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/proc'
-class ProcTests < Test::Unit::TestCase
+class ProcTests < ActiveSupport::TestCase
def test_bind_returns_method_with_changed_self
block = Proc.new { self }
assert_equal self, block.call
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 1424fa4aca..cf1ec448c2 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'active_support/time'
require 'active_support/core_ext/range'
-class RangeTest < Test::Unit::TestCase
+class RangeTest < ActiveSupport::TestCase
def test_to_s_from_dates
date_range = Date.new(2005, 12, 10)..Date.new(2005, 12, 12)
assert_equal "BETWEEN '2005-12-10' AND '2005-12-12'", date_range.to_s(:db)
@@ -53,6 +53,10 @@ class RangeTest < Test::Unit::TestCase
assert !(2..8).include?(5..9)
end
+ def test_should_include_identical_exclusive_with_floats
+ assert((1.0...10.0).include?(1.0...10.0))
+ end
+
def test_blockless_step
assert_equal [1,3,5,7,9], (1..10).step(2)
end
@@ -63,15 +67,20 @@ class RangeTest < Test::Unit::TestCase
assert_equal [1,3,5,7,9], array
end
- if RUBY_VERSION < '1.9'
- def test_cover
- assert((1..3).cover?(2))
- assert !(1..3).cover?(4)
- end
- else
- def test_cover_is_not_override
- range = (1..3)
- assert range.method(:include?) != range.method(:cover?)
- end
+ def test_cover_is_not_override
+ range = (1..3)
+ assert range.method(:include?) != range.method(:cover?)
+ end
+
+ def test_overlaps_on_time
+ time_range_1 = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
+ time_range_2 = Time.utc(2005, 12, 10, 17, 00)..Time.utc(2005, 12, 10, 18, 00)
+ assert time_range_1.overlaps?(time_range_2)
+ end
+
+ def test_no_overlaps_on_time
+ time_range_1 = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
+ time_range_2 = Time.utc(2005, 12, 10, 17, 31)..Time.utc(2005, 12, 10, 18, 00)
+ assert !time_range_1.overlaps?(time_range_2)
end
end
diff --git a/activesupport/test/core_ext/regexp_ext_test.rb b/activesupport/test/core_ext/regexp_ext_test.rb
index 68b089d5b4..c2398d31bd 100644
--- a/activesupport/test/core_ext/regexp_ext_test.rb
+++ b/activesupport/test/core_ext/regexp_ext_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/regexp'
-class RegexpExtAccessTests < Test::Unit::TestCase
+class RegexpExtAccessTests < ActiveSupport::TestCase
def test_multiline
assert_equal true, //m.multiline?
assert_equal false, //.multiline?
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index ade09efc56..6c2828b74e 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -17,16 +17,10 @@ module Ace
end
end
-class StringInflectionsTest < Test::Unit::TestCase
+class StringInflectionsTest < ActiveSupport::TestCase
include InflectorTestCases
include ConstantizeTestCases
- def test_erb_escape
- string = [192, 60].pack('CC')
- expected = 192.chr + "&lt;"
- assert_equal expected, ERB::Util.html_escape(string)
- end
-
def test_strip_heredoc_on_an_empty_string
assert_equal '', ''.strip_heredoc
end
@@ -166,14 +160,6 @@ class StringInflectionsTest < Test::Unit::TestCase
assert_equal 97, 'abc'.ord
end
- if RUBY_VERSION < '1.9'
- def test_getbyte
- assert_equal 97, 'a'.getbyte(0)
- assert_equal 99, 'abc'.getbyte(2)
- assert_nil 'abc'.getbyte(3)
- end
- end
-
def test_string_to_time
assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time
assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:local)
@@ -293,21 +279,9 @@ class StringInflectionsTest < Test::Unit::TestCase
assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, :omission => "[...]", :separator => ' ')
end
- if RUBY_VERSION < '1.9.0'
- def test_truncate_multibyte
- with_kcode 'none' do
- assert_equal "\354\225\210\353\205\225\355...", "\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224".truncate(10)
- end
- with_kcode 'u' do
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...",
- "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".truncate(10)
- end
- end
- else
- def test_truncate_multibyte
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
- "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10)
- end
+ def test_truncate_multibyte
+ assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
+ "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10)
end
def test_constantize
@@ -323,7 +297,7 @@ class StringInflectionsTest < Test::Unit::TestCase
end
end
-class StringBehaviourTest < Test::Unit::TestCase
+class StringBehaviourTest < ActiveSupport::TestCase
def test_acts_like_string
assert 'Bambi'.acts_like_string?
end
@@ -344,22 +318,8 @@ class CoreExtStringMultibyteTest < ActiveSupport::TestCase
assert !BYTE_STRING.is_utf8?
end
- if RUBY_VERSION < '1.9'
- def test_mb_chars_returns_self_when_kcode_not_set
- with_kcode('none') do
- assert_kind_of String, UNICODE_STRING.mb_chars
- end
- end
-
- def test_mb_chars_returns_an_instance_of_the_chars_proxy_when_kcode_utf8
- with_kcode('UTF8') do
- assert_kind_of ActiveSupport::Multibyte.proxy_class, UNICODE_STRING.mb_chars
- end
- end
- else
- def test_mb_chars_returns_instance_of_proxy_class
- assert_kind_of ActiveSupport::Multibyte.proxy_class, UNICODE_STRING.mb_chars
- end
+ def test_mb_chars_returns_instance_of_proxy_class
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, UNICODE_STRING.mb_chars
end
end
@@ -485,10 +445,8 @@ class OutputSafetyTest < ActiveSupport::TestCase
end
test 'knows whether it is encoding aware' do
- if RUBY_VERSION >= "1.9"
+ assert_deprecated do
assert 'ruby'.encoding_aware?
- else
- assert !'ruby'.encoding_aware?
end
end
@@ -497,6 +455,23 @@ class OutputSafetyTest < ActiveSupport::TestCase
assert string.html_safe?
assert !string.to_param.html_safe?
end
+
+ test "ERB::Util.html_escape should escape unsafe characters" do
+ string = '<>&"'
+ expected = '&lt;&gt;&amp;&quot;'
+ assert_equal expected, ERB::Util.html_escape(string)
+ end
+
+ test "ERB::Util.html_escape should correctly handle invalid UTF-8 strings" do
+ string = [192, 60].pack('CC')
+ expected = 192.chr + "&lt;"
+ assert_equal expected, ERB::Util.html_escape(string)
+ end
+
+ test "ERB::Util.html_escape should not escape safe strings" do
+ string = "<b>hello</b>".html_safe
+ assert_equal string, ERB::Util.html_escape(string)
+ end
end
class StringExcludeTest < ActiveSupport::TestCase
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index ab9be4b18b..eda8066579 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -59,8 +59,28 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,11,28), Time.local(2005,12,02,0,0,0).beginning_of_week #friday
assert_equal Time.local(2005,11,28), Time.local(2005,12,03,0,0,0).beginning_of_week #saturday
assert_equal Time.local(2005,11,28), Time.local(2005,12,04,0,0,0).beginning_of_week #sunday
+
end
+ def test_days_to_week_start
+ assert_equal 0, Time.local(2011,11,01,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 1, Time.local(2011,11,02,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 2, Time.local(2011,11,03,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 4, Time.local(2011,11,05,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 5, Time.local(2011,11,06,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 6, Time.local(2011,11,07,0,0,0).days_to_week_start(:tuesday)
+
+ assert_equal 3, Time.local(2011,11,03,0,0,0).days_to_week_start(:monday)
+ assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday)
+ assert_equal 3, Time.local(2011,11,05,0,0,0).days_to_week_start(:wednesday)
+ assert_equal 3, Time.local(2011,11,06,0,0,0).days_to_week_start(:thursday)
+ assert_equal 3, Time.local(2011,11,07,0,0,0).days_to_week_start(:friday)
+ assert_equal 3, Time.local(2011,11,8,0,0,0).days_to_week_start(:saturday)
+ assert_equal 3, Time.local(2011,11,9,0,0,0).days_to_week_start(:sunday)
+ end
+
+
def test_beginning_of_day
assert_equal Time.local(2005,2,4,0,0,0), Time.local(2005,2,4,10,10,10).beginning_of_day
with_env_tz 'US/Eastern' do
@@ -135,7 +155,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,5,1,10), Time.local(2005,6,5,10,0,0).weeks_ago(5)
assert_equal Time.local(2005,4,24,10), Time.local(2005,6,5,10,0,0).weeks_ago(6)
assert_equal Time.local(2005,2,27,10), Time.local(2005,6,5,10,0,0).weeks_ago(14)
- assert_equal Time.local(2004,12,25,10), Time.local(2005,1,1,10,0,0).weeks_ago(1)
+ assert_equal Time.local(2004,12,25,10), Time.local(2005,1,1,10,0,0).weeks_ago(1)
end
def test_months_ago
@@ -471,6 +491,11 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.utc(2013,10,17,20,22,19), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9)
end
+ def test_advance_with_nsec
+ t = Time.at(0, Rational(108635108, 1000))
+ assert_equal t, t.advance(:months => 0)
+ end
+
def test_prev_week
with_env_tz 'US/Eastern' do
assert_equal Time.local(2005,2,21), Time.local(2005,3,1,15,15,10).prev_week
@@ -479,7 +504,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2006,10,30), Time.local(2006,11,6,0,0,0).prev_week
assert_equal Time.local(2006,11,15), Time.local(2006,11,23,0,0,0).prev_week(:wednesday)
end
- end
+ end
def test_next_week
with_env_tz 'US/Eastern' do
@@ -538,12 +563,12 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_to_datetime
- assert_equal Time.utc(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal Time.utc(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, 0)
with_env_tz 'US/Eastern' do
- assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0)
+ assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400))
end
with_env_tz 'NZ' do
- assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0)
+ assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400))
end
assert_equal ::Date::ITALY, Time.utc(2005, 2, 21, 17, 44, 30).to_datetime.start # use Ruby's default start value
end
@@ -592,15 +617,15 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
def test_time_with_datetime_fallback
assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30)
assert_equal Time.time_with_datetime_fallback(:local, 2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30)
- assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0)
- assert_equal Time.time_with_datetime_fallback(:local, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, DateTime.local_offset, 0)
- assert_equal Time.time_with_datetime_fallback(:utc, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0)
+ assert_equal Time.time_with_datetime_fallback(:local, 2039, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 2039, 2, 21, 17, 44, 30)
+ assert_equal Time.time_with_datetime_fallback(:utc, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, 0)
assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005)
- assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0, 0)
+ assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0)
assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec
# This won't overflow on 64bit linux
unless time_is_64bits?
- assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, DateTime.local_offset, 0)
+ assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 1900, 2, 21, 17, 44, 30)
assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1),
DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0)
assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value
@@ -616,16 +641,16 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
def test_utc_time
assert_equal Time.utc_time(2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30)
- assert_equal Time.utc_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0)
- assert_equal Time.utc_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal Time.utc_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0)
+ assert_equal Time.utc_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, 0)
end
def test_local_time
assert_equal Time.local_time(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30)
- assert_equal Time.local_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, DateTime.local_offset, 0)
+ assert_equal Time.local_time(2039, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 2039, 2, 21, 17, 44, 30)
unless time_is_64bits?
- assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, DateTime.local_offset, 0)
+ assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil_from_format(:local, 1901, 2, 21, 17, 44, 30)
end
end
@@ -744,6 +769,12 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] ))
end
+ def test_eql?
+ assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) )
+ assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
+ assert_equal false,Time.utc(2000, 1, 1, 0, 0, 1).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) )
+ end
+
def test_minus_with_time_with_zone
assert_equal 86_400.0, Time.utc(2000, 1, 2) - ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['UTC'] )
end
@@ -776,6 +807,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
def test_all_week
assert_equal Time.local(2011,6,6,0,0,0)..Time.local(2011,6,12,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_week
+ assert_equal Time.local(2011,6,5,0,0,0)..Time.local(2011,6,11,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_week(:sunday)
end
def test_all_month
@@ -803,7 +835,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
end
-class TimeExtMarshalingTest < Test::Unit::TestCase
+class TimeExtMarshalingTest < ActiveSupport::TestCase
def test_marshaling_with_utc_instance
t = Time.utc(2000)
unmarshaled = Marshal.load(Marshal.dump(t))
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index b2309ae806..7cf3842a16 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'active_support/time'
require 'active_support/json'
-class TimeWithZoneTest < Test::Unit::TestCase
+class TimeWithZoneTest < ActiveSupport::TestCase
def setup
@utc = Time.utc(2000, 1, 1, 0)
@@ -200,8 +200,15 @@ class TimeWithZoneTest < Test::Unit::TestCase
end
def test_eql?
- assert @twz.eql?(Time.utc(2000))
- assert @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
+ assert_equal true, @twz.eql?(Time.utc(2000))
+ assert_equal true, @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
+ assert_equal false, @twz.eql?( Time.utc(2000, 1, 1, 0, 0, 1) )
+ assert_equal false, @twz.eql?( DateTime.civil(1999, 12, 31, 23, 59, 59) )
+ end
+
+ def test_hash
+ assert_equal Time.utc(2000).hash, @twz.hash
+ assert_equal Time.utc(2000).hash, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]).hash
end
def test_plus_with_integer
@@ -441,9 +448,8 @@ class TimeWithZoneTest < Test::Unit::TestCase
end
def test_ruby_19_weekday_name_query_methods
- ruby_19_or_greater = RUBY_VERSION >= '1.9'
%w(sunday? monday? tuesday? wednesday? thursday? friday? saturday?).each do |name|
- assert_equal ruby_19_or_greater, @twz.respond_to?(name)
+ assert_respond_to @twz, name
end
end
@@ -730,7 +736,7 @@ class TimeWithZoneTest < Test::Unit::TestCase
end
end
-class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase
+class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
def setup
@t, @dt = Time.utc(2000), DateTime.civil(2000)
end
diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb
index d988837603..03e388dd7a 100644
--- a/activesupport/test/core_ext/uri_ext_test.rb
+++ b/activesupport/test/core_ext/uri_ext_test.rb
@@ -3,15 +3,11 @@ require 'abstract_unit'
require 'uri'
require 'active_support/core_ext/uri'
-class URIExtTest < Test::Unit::TestCase
+class URIExtTest < ActiveSupport::TestCase
def test_uri_decode_handle_multibyte
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
- if URI.const_defined?(:Parser)
- parser = URI::Parser.new
- assert_equal str, parser.unescape(parser.escape(str))
- else
- assert_equal str, URI.unescape(URI.escape(str))
- end
+ parser = URI::Parser.new
+ assert_equal str, parser.unescape(parser.escape(str))
end
end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index fe8f51e11a..081e6a16fd 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -14,7 +14,7 @@ module ModuleWithConstant
InheritedConstant = "Hello"
end
-class DependenciesTest < Test::Unit::TestCase
+class DependenciesTest < ActiveSupport::TestCase
def teardown
ActiveSupport::Dependencies.clear
end
@@ -258,6 +258,85 @@ class DependenciesTest < Test::Unit::TestCase
$:.replace(original_path)
end
+ def test_require_returns_true_when_file_not_yet_required
+ path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
+ original_path = $:.dup
+ original_features = $".dup
+ $:.push(path)
+
+ with_loading do
+ assert_equal true, require('loaded_constant')
+ end
+ ensure
+ remove_constants(:LoadedConstant)
+ $".replace(original_features)
+ $:.replace(original_path)
+ end
+
+ def test_require_returns_true_when_file_not_yet_required_even_when_no_new_constants_added
+ path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
+ original_path = $:.dup
+ original_features = $".dup
+ $:.push(path)
+
+ with_loading do
+ Object.module_eval "module LoadedConstant; end"
+ assert_equal true, require('loaded_constant')
+ end
+ ensure
+ remove_constants(:LoadedConstant)
+ $".replace(original_features)
+ $:.replace(original_path)
+ end
+
+ def test_require_returns_false_when_file_already_required
+ path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
+ original_path = $:.dup
+ original_features = $".dup
+ $:.push(path)
+
+ with_loading do
+ require 'loaded_constant'
+ assert_equal false, require('loaded_constant')
+ end
+ ensure
+ remove_constants(:LoadedConstant)
+ $".replace(original_features)
+ $:.replace(original_path)
+ end
+
+ def test_require_raises_load_error_when_file_not_found
+ with_loading do
+ assert_raise(LoadError) { require 'this_file_dont_exist_dude' }
+ end
+ ensure
+ remove_constants(:LoadedConstant)
+ end
+
+ def test_load_returns_true_when_file_found
+ path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
+ original_path = $:.dup
+ original_features = $".dup
+ $:.push(path)
+
+ with_loading do
+ assert_equal true, load('loaded_constant.rb')
+ assert_equal true, load('loaded_constant.rb')
+ end
+ ensure
+ remove_constants(:LoadedConstant)
+ $".replace(original_features)
+ $:.replace(original_path)
+ end
+
+ def test_load_raises_load_error_when_file_not_found
+ with_loading do
+ assert_raise(LoadError) { load 'this_file_dont_exist_dude.rb' }
+ end
+ ensure
+ remove_constants(:LoadedConstant)
+ end
+
def failing_test_access_thru_and_upwards_fails
with_autoloading_fixtures do
assert ! defined?(ModuleFolder)
@@ -334,7 +413,7 @@ class DependenciesTest < Test::Unit::TestCase
assert ActiveSupport::Dependencies.qualified_const_defined?("Object")
assert ActiveSupport::Dependencies.qualified_const_defined?("::Object")
assert ActiveSupport::Dependencies.qualified_const_defined?("::Object::Kernel")
- assert ActiveSupport::Dependencies.qualified_const_defined?("::Test::Unit::TestCase")
+ assert ActiveSupport::Dependencies.qualified_const_defined?("::ActiveSupport::TestCase")
end
def test_qualified_const_defined_should_not_call_const_missing
diff --git a/activesupport/test/deprecation/proxy_wrappers_test.rb b/activesupport/test/deprecation/proxy_wrappers_test.rb
index aa887f274d..e4f0f0f7c2 100644
--- a/activesupport/test/deprecation/proxy_wrappers_test.rb
+++ b/activesupport/test/deprecation/proxy_wrappers_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/deprecation'
-class ProxyWrappersTest < Test::Unit::TestCase
+class ProxyWrappersTest < ActiveSupport::TestCase
Waffles = false
NewWaffles = :hamburgers
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index d77a62f108..e821a285d7 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -120,7 +120,7 @@ class DeprecationTest < ActiveSupport::TestCase
ActiveSupport::Deprecation.warn 'abc'
ActiveSupport::Deprecation.warn 'def'
end
- rescue Test::Unit::AssertionFailedError
+ rescue MiniTest::Assertion
flunk 'assert_deprecated should match any warning in block, not just the last one'
end
@@ -166,22 +166,4 @@ class DeprecationTest < ActiveSupport::TestCase
def test_deprecation_with_explicit_message
assert_deprecated(/you now need to do something extra for this one/) { @dtc.d }
end
-
- unless defined?(::MiniTest)
- def test_assertion_failed_error_doesnt_spout_deprecation_warnings
- error_class = Class.new(StandardError) do
- def message
- ActiveSupport::Deprecation.warn 'warning in error message'
- super
- end
- end
-
- raise error_class.new('hmm')
-
- rescue => e
- error = Test::Unit::Error.new('testing ur doodz', e)
- assert_not_deprecated { error.message }
- assert_nil @last_message
- end
- end
end
diff --git a/activesupport/test/descendants_tracker_with_autoloading_test.rb b/activesupport/test/descendants_tracker_with_autoloading_test.rb
index ae18a56f44..9180f1f977 100644
--- a/activesupport/test/descendants_tracker_with_autoloading_test.rb
+++ b/activesupport/test/descendants_tracker_with_autoloading_test.rb
@@ -1,10 +1,9 @@
require 'abstract_unit'
-require 'test/unit'
require 'active_support/descendants_tracker'
require 'active_support/dependencies'
require 'descendants_tracker_test_cases'
-class DescendantsTrackerWithAutoloadingTest < Test::Unit::TestCase
+class DescendantsTrackerWithAutoloadingTest < ActiveSupport::TestCase
include DescendantsTrackerTestCases
def test_clear_with_autoloaded_parent_children_and_granchildren
@@ -32,4 +31,4 @@ class DescendantsTrackerWithAutoloadingTest < Test::Unit::TestCase
assert_equal [], Child2.descendants
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/descendants_tracker_without_autoloading_test.rb b/activesupport/test/descendants_tracker_without_autoloading_test.rb
index 1f0c32dc3f..74669aaca1 100644
--- a/activesupport/test/descendants_tracker_without_autoloading_test.rb
+++ b/activesupport/test/descendants_tracker_without_autoloading_test.rb
@@ -1,8 +1,7 @@
require 'abstract_unit'
-require 'test/unit'
require 'active_support/descendants_tracker'
require 'descendants_tracker_test_cases'
-class DescendantsTrackerWithoutAutoloadingTest < Test::Unit::TestCase
+class DescendantsTrackerWithoutAutoloadingTest < ActiveSupport::TestCase
include DescendantsTrackerTestCases
-end \ No newline at end of file
+end
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index b65bb1d024..dd2483287b 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -1,18 +1,19 @@
require 'abstract_unit'
-require 'test/unit'
require 'fileutils'
MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
-class FileUpdateCheckerTest < Test::Unit::TestCase
+class FileUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase
FILES = %w(1.txt 2.txt 3.txt)
def setup
+ FileUtils.mkdir_p("tmp_watcher")
FileUtils.touch(FILES)
end
def teardown
- FileUtils.rm(FILES)
+ FileUtils.rm_rf("tmp_watcher")
+ FileUtils.rm_rf(FILES)
end
def test_should_not_execute_the_block_if_no_paths_are_given
@@ -22,34 +23,60 @@ class FileUpdateCheckerTest < Test::Unit::TestCase
assert_equal 0, i
end
- def test_should_invoke_the_block_on_first_call_if_it_does_not_calculate_last_updated_at_on_load
+ def test_should_not_invoke_the_block_if_no_file_has_changed
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
- checker.execute_if_updated
- assert_equal 1, i
+ 5.times { assert !checker.execute_if_updated }
+ assert_equal 0, i
end
- def test_should_not_invoke_the_block_on_first_call_if_it_calculates_last_updated_at_on_load
+ def test_should_invoke_the_block_if_a_file_has_changed
i = 0
- checker = ActiveSupport::FileUpdateChecker.new(FILES, true){ i += 1 }
- checker.execute_if_updated
- assert_equal 0, i
+ checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
+ sleep(1)
+ FileUtils.touch(FILES)
+ assert checker.execute_if_updated
+ assert_equal 1, i
end
- def test_should_not_invoke_the_block_if_no_file_has_changed
+ def test_should_be_robust_enough_to_handle_deleted_files
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
- 5.times { checker.execute_if_updated }
- assert_equal 1, i
+ FileUtils.rm(FILES)
+ assert !checker.execute_if_updated
+ assert_equal 0, i
end
- def test_should_invoke_the_block_if_a_file_has_changed
+ def test_should_cache_updated_result_until_execute
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
- checker.execute_if_updated
+ assert !checker.updated?
+
sleep(1)
FileUtils.touch(FILES)
- checker.execute_if_updated
- assert_equal 2, i
+
+ assert checker.updated?
+ checker.execute
+ assert !checker.updated?
+ end
+
+ def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob
+ i = 0
+ checker = ActiveSupport::FileUpdateChecker.new([], "tmp_watcher" => [:txt]){ i += 1 }
+ FileUtils.cd "tmp_watcher" do
+ FileUtils.touch(FILES)
+ end
+ assert checker.execute_if_updated
+ assert_equal 1, i
+ end
+
+ def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob
+ i = 0
+ checker = ActiveSupport::FileUpdateChecker.new([], "tmp_watcher" => :rb){ i += 1 }
+ FileUtils.cd "tmp_watcher" do
+ FileUtils.touch(FILES)
+ end
+ assert !checker.execute_if_updated
+ assert_equal 0, i
end
end
diff --git a/activesupport/test/flush_cache_on_private_memoization_test.rb b/activesupport/test/flush_cache_on_private_memoization_test.rb
deleted file mode 100644
index 20768b777a..0000000000
--- a/activesupport/test/flush_cache_on_private_memoization_test.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'abstract_unit'
-require 'test/unit'
-
-class FlashCacheOnPrivateMemoizationTest < Test::Unit::TestCase
- ActiveSupport::Deprecation.silence do
- extend ActiveSupport::Memoizable
- end
-
- def test_public
- assert_method_unmemoizable :pub
- end
-
- def test_protected
- assert_method_unmemoizable :prot
- end
-
- def test_private
- assert_method_unmemoizable :priv
- end
-
- def pub; rand end
- memoize :pub
-
- protected
-
- def prot; rand end
- memoize :prot
-
- private
-
- def priv; rand end
- memoize :priv
-
- def assert_method_unmemoizable(meth, message=nil)
- full_message = build_message(message, "<?> not unmemoizable.\n", meth)
- assert_block(full_message) do
- a = send meth
- b = send meth
- unmemoize_all
- c = send meth
- a == b && a != c
- end
- end
-
-end
diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb
index f564e63f29..75a0505899 100644
--- a/activesupport/test/gzip_test.rb
+++ b/activesupport/test/gzip_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/object/blank'
-class GzipTest < Test::Unit::TestCase
+class GzipTest < ActiveSupport::TestCase
def test_compress_should_decompress_to_the_same_value
assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World"))
end
@@ -9,10 +9,7 @@ class GzipTest < Test::Unit::TestCase
def test_compress_should_return_a_binary_string
compressed = ActiveSupport::Gzip.compress('')
- if "".encoding_aware?
- assert_equal Encoding.find('binary'), compressed.encoding
- end
-
+ assert_equal Encoding.find('binary'), compressed.encoding
assert !compressed.blank?, "a compressed blank string should not be blank"
end
end
diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb
index 34825c9b8f..4f2027f4eb 100644
--- a/activesupport/test/i18n_test.rb
+++ b/activesupport/test/i18n_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
require 'active_support/time'
require 'active_support/core_ext/array/conversions'
-class I18nTest < Test::Unit::TestCase
+class I18nTest < ActiveSupport::TestCase
def setup
@date = Date.parse("2008-7-2")
@time = Time.utc(2008, 7, 2, 16, 47, 1)
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index 6b7e839e43..7b012f7caa 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -4,7 +4,7 @@ require 'active_support/inflector'
require 'inflector_test_cases'
require 'constantize_test_cases'
-class InflectorTest < Test::Unit::TestCase
+class InflectorTest < ActiveSupport::TestCase
include InflectorTestCases
include ConstantizeTestCases
@@ -65,6 +65,14 @@ class InflectorTest < Test::Unit::TestCase
assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(plural.capitalize))
end
end
+
+ SingularToPlural.each do |singular, plural|
+ define_method "test_singularize_singular_#{singular}" do
+ assert_equal(singular, ActiveSupport::Inflector.singularize(singular))
+ assert_equal(singular.capitalize, ActiveSupport::Inflector.singularize(singular.capitalize))
+ end
+ end
+
def test_overwrite_previous_inflectors
assert_equal("series", ActiveSupport::Inflector.singularize("series"))
@@ -304,6 +312,12 @@ class InflectorTest < Test::Unit::TestCase
def test_ordinal
OrdinalNumbers.each do |number, ordinalized|
+ assert_equal(ordinalized, number + ActiveSupport::Inflector.ordinal(number))
+ end
+ end
+
+ def test_ordinalize
+ OrdinalNumbers.each do |number, ordinalized|
assert_equal(ordinalized, ActiveSupport::Inflector.ordinalize(number))
end
end
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index 0cb1f70657..809b8b46c9 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -14,6 +14,7 @@ module InflectorTestCases
"fish" => "fish",
"jeans" => "jeans",
"funky jeans" => "funky jeans",
+ "my money" => "my money",
"category" => "categories",
"query" => "queries",
@@ -92,6 +93,7 @@ module InflectorTestCases
"matrix_fu" => "matrix_fus",
"axis" => "axes",
+ "taxi" => "taxis", # prevents regression
"testis" => "testes",
"crisis" => "crises",
@@ -103,7 +105,11 @@ module InflectorTestCases
"edge" => "edges",
"cow" => "kine",
- "database" => "databases"
+ "database" => "databases",
+
+ # regression tests against improper inflection regexes
+ "|ice" => "|ices",
+ "|ouse" => "|ouses"
}
CamelToUnderscore = {
diff --git a/activesupport/test/isolation_test.rb b/activesupport/test/isolation_test.rb
deleted file mode 100644
index 2c2986ea28..0000000000
--- a/activesupport/test/isolation_test.rb
+++ /dev/null
@@ -1,182 +0,0 @@
-require 'abstract_unit'
-require 'rbconfig'
-
-if defined?(MiniTest) || defined?(Test::Unit::TestResultFailureSupport)
- $stderr.puts "Isolation tests can test test-unit 1 only"
-
-elsif ENV['CHILD']
- class ChildIsolationTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
-
- def self.setup
- File.open(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"), "a") do |f|
- f.puts "hello"
- end
- end
-
- def setup
- @instance = "HELLO"
- end
-
- def teardown
- raise if @boom
- end
-
- test "runs the test" do
- assert true
- end
-
- test "captures errors" do
- raise
- end
-
- test "captures failures" do
- assert false
- end
-
- test "first runs in isolation" do
- assert_nil $x
- $x = 1
- end
-
- test "second runs in isolation" do
- assert_nil $x
- $x = 2
- end
-
- test "runs with slow tests" do
- sleep 0.3
- assert true
- sleep 0.2
- end
-
- test "runs setup" do
- assert "HELLO", @instance
- end
-
- test "runs teardown" do
- @boom = true
- end
-
- test "resets requires one" do
- assert !defined?(Custom)
- assert_equal 0, $LOADED_FEATURES.grep(/fixtures\/custom/).size
- require File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "custom"))
- end
-
- test "resets requires two" do
- assert !defined?(Custom)
- assert_equal 0, $LOADED_FEATURES.grep(/fixtures\/custom/).size
- require File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "custom"))
- end
- end
-else
- class ParentIsolationTest < ActiveSupport::TestCase
-
- File.open(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"), "w") {}
-
- ENV["CHILD"] = "1"
- OUTPUT = `#{RbConfig::CONFIG["bindir"]}/#{RbConfig::CONFIG["ruby_install_name"]} -I#{File.dirname(__FILE__)} "#{File.expand_path(__FILE__)}" -v`
- ENV.delete("CHILD")
-
- def setup
- defined?(::MiniTest) ? parse_minitest : parse_testunit
- end
-
- def parse_testunit
- @results = {}
- OUTPUT[/Started\n\s*(.*)\s*\nFinished/mi, 1].to_s.split(/\s*\n\s*/).each do |result|
- result =~ %r'^(\w+)\(\w+\):\s*(\.|E|F)$'
- @results[$1] = { 'E' => :error, '.' => :success, 'F' => :failure }[$2]
- end
-
- # Extract the backtraces
- @backtraces = {}
- OUTPUT.scan(/^\s*\d+\).*?\n\n/m).each do |backtrace|
- # \n 1) Error:\ntest_captures_errors(ChildIsolationTest):
- backtrace =~ %r'\s*\d+\)\s*(Error|Failure):\n(\w+)'i
- @backtraces[$2] = { :type => $1, :output => backtrace }
- end
- end
-
- def parse_minitest
- @results = {}
- OUTPUT[/Started\n\s*(.*)\s*\nFinished/mi, 1].to_s.split(/\s*\n\s*/).each do |result|
- result =~ %r'^\w+#(\w+):.*:\s*(.*Assertion.*|.*RuntimeError.*|\.\s*)$'
- val = :success
- val = :error if $2.include?('RuntimeError')
- val = :failure if $2.include?('Assertion')
-
- @results[$1] = val
- end
-
- # Extract the backtraces
- @backtraces = {}
- OUTPUT.scan(/^\s*\d+\).*?\n\n/m).each do |backtrace|
- # \n 1) Error:\ntest_captures_errors(ChildIsolationTest):
- backtrace =~ %r'\s*\d+\)\s*(Error|Failure):\n(\w+)'i
- @backtraces[$2] = { :type => $1, :output => backtrace }
- end
- end
-
- def assert_failing(name)
- assert_equal :failure, @results[name.to_s], "Test #{name} failed"
- end
-
- def assert_passing(name)
- assert_equal :success, @results[name.to_s], "Test #{name} passed"
- end
-
- def assert_erroring(name)
- assert_equal :error, @results[name.to_s], "Test #{name} errored"
- end
-
- test "has all tests" do
- assert_equal 10, @results.length
- end
-
- test "passing tests are still reported" do
- assert_passing :test_runs_the_test
- assert_passing :test_runs_with_slow_tests
- end
-
- test "resets global variables" do
- assert_passing :test_first_runs_in_isolation
- assert_passing :test_second_runs_in_isolation
- end
-
- test "resets requires" do
- assert_passing :test_resets_requires_one
- assert_passing :test_resets_requires_two
- end
-
- test "erroring tests are still reported" do
- assert_erroring :test_captures_errors
- end
-
- test "runs setup and teardown methods" do
- assert_passing :test_runs_setup
- assert_erroring :test_runs_teardown
- end
-
- test "correct tests fail" do
- assert_failing :test_captures_failures
- end
-
- test "backtrace is printed for errors" do
- assert_equal 'Error', @backtraces["test_captures_errors"][:type]
- assert_match %r{isolation_test.rb:\d+}, @backtraces["test_captures_errors"][:output]
- end
-
- test "backtrace is printed for failures" do
- assert_equal 'Failure', @backtraces["test_captures_failures"][:type]
- assert_match %r{isolation_test.rb:\d+}, @backtraces["test_captures_failures"][:output]
- end
-
- test "self.setup is run only once" do
- text = File.read(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"))
- assert_equal "hello\n", text
- end
-
- end
-end
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 8cf1a54a99..a2e61d88d5 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
require 'active_support/core_ext/string/inflections'
require 'active_support/json'
-class TestJSONEncoding < Test::Unit::TestCase
+class TestJSONEncoding < ActiveSupport::TestCase
class Foo
def initialize(a, b)
@a, @b = a, b
@@ -38,6 +38,10 @@ class TestJSONEncoding < Test::Unit::TestCase
ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
[ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
+ RangeTests = [[ 1..2, %("1..2")],
+ [ 1...2, %("1...2")],
+ [ 1.5..2.5, %("1.5..2.5")]]
+
SymbolTests = [[ :a, %("a") ],
[ :this, %("this") ],
[ :"a b", %("a b") ]]
@@ -88,25 +92,21 @@ class TestJSONEncoding < Test::Unit::TestCase
assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d))
end
- def test_utf8_string_encoded_properly_when_kcode_is_utf8
- with_kcode 'UTF8' do
- result = ActiveSupport::JSON.encode('€2.99')
- assert_equal '"\\u20ac2.99"', result
- assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding)
+ def test_utf8_string_encoded_properly
+ result = ActiveSupport::JSON.encode('€2.99')
+ assert_equal '"\\u20ac2.99"', result
+ assert_equal(Encoding::UTF_8, result.encoding)
- result = ActiveSupport::JSON.encode('✎☺')
- assert_equal '"\\u270e\\u263a"', result
- assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding)
- end
+ result = ActiveSupport::JSON.encode('✎☺')
+ assert_equal '"\\u270e\\u263a"', result
+ assert_equal(Encoding::UTF_8, result.encoding)
end
- if '1.9'.respond_to?(:force_encoding)
- def test_non_utf8_string_transcodes
- s = '二'.encode('Shift_JIS')
- result = ActiveSupport::JSON.encode(s)
- assert_equal '"\\u4e8c"', result
- assert_equal Encoding::UTF_8, result.encoding
- end
+ def test_non_utf8_string_transcodes
+ s = '二'.encode('Shift_JIS')
+ result = ActiveSupport::JSON.encode(s)
+ assert_equal '"\\u4e8c"', result
+ assert_equal Encoding::UTF_8, result.encoding
end
def test_exception_raised_when_encoding_circular_reference_in_array
diff --git a/activesupport/test/load_paths_test.rb b/activesupport/test/load_paths_test.rb
index a2d8da726a..979e25bdf3 100644
--- a/activesupport/test/load_paths_test.rb
+++ b/activesupport/test/load_paths_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class LoadPathsTest < Test::Unit::TestCase
+class LoadPathsTest < ActiveSupport::TestCase
def test_uniq_load_paths
load_paths_count = $LOAD_PATH.inject({}) { |paths, path|
expanded_path = File.expand_path(path)
diff --git a/activesupport/test/log_subscriber_test.rb b/activesupport/test/log_subscriber_test.rb
index 0c1f3c51ed..8e160714b1 100644
--- a/activesupport/test/log_subscriber_test.rb
+++ b/activesupport/test/log_subscriber_test.rb
@@ -118,6 +118,6 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase
assert_equal 'some_event.my_log_subscriber', @logger.logged(:info).last
assert_equal 1, @logger.logged(:error).size
- assert_equal 'Could not log "puke.my_log_subscriber" event. RuntimeError: puke', @logger.logged(:error).last
+ assert_match 'Could not log "puke.my_log_subscriber" event. RuntimeError: puke', @logger.logged(:error).last
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb
deleted file mode 100644
index e333b9a78c..0000000000
--- a/activesupport/test/memoizable_test.rb
+++ /dev/null
@@ -1,290 +0,0 @@
-require 'abstract_unit'
-
-class MemoizableTest < ActiveSupport::TestCase
- class Person
- ActiveSupport::Deprecation.silence do
- extend ActiveSupport::Memoizable
- end
-
- attr_reader :name_calls, :age_calls, :is_developer_calls, :name_query_calls
-
- def initialize
- @name_calls = 0
- @age_calls = 0
- @is_developer_calls = 0
- @name_query_calls = 0
- end
-
- def name
- @name_calls += 1
- "Josh"
- end
-
- def name?
- @name_query_calls += 1
- true
- end
- memoize :name?
-
- def update(name)
- "Joshua"
- end
- memoize :update
-
- def age
- @age_calls += 1
- nil
- end
-
- memoize :name, :age
-
- protected
-
- def memoize_protected_test
- 'protected'
- end
- memoize :memoize_protected_test
-
- private
-
- def is_developer?
- @is_developer_calls += 1
- "Yes"
- end
- memoize :is_developer?
- end
-
- class Company
- attr_reader :name_calls
- def initialize
- @name_calls = 0
- end
-
- def name
- @name_calls += 1
- "37signals"
- end
- end
-
- module Rates
- ActiveSupport::Deprecation.silence do
- extend ActiveSupport::Memoizable
- end
-
- attr_reader :sales_tax_calls
- def sales_tax(price)
- @sales_tax_calls ||= 0
- @sales_tax_calls += 1
- price * 0.1025
- end
- memoize :sales_tax
- end
-
- class Calculator
- ActiveSupport::Deprecation.silence do
- extend ActiveSupport::Memoizable
- end
- include Rates
-
- attr_reader :fib_calls
- def initialize
- @fib_calls = 0
- end
-
- def fib(n)
- @fib_calls += 1
-
- if n == 0 || n == 1
- n
- else
- fib(n - 1) + fib(n - 2)
- end
- end
- memoize :fib
-
- def add_or_subtract(i, j, add)
- if add
- i + j
- else
- i - j
- end
- end
- memoize :add_or_subtract
-
- def counter
- @count ||= 0
- @count += 1
- end
- memoize :counter
- end
-
- def setup
- @person = Person.new
- @calculator = Calculator.new
- end
-
- def test_memoization
- assert_equal "Josh", @person.name
- assert_equal 1, @person.name_calls
-
- 3.times { assert_equal "Josh", @person.name }
- assert_equal 1, @person.name_calls
- end
-
- def test_memoization_with_punctuation
- assert_equal true, @person.name?
-
- assert_nothing_raised(NameError) do
- @person.memoize_all
- @person.unmemoize_all
- end
- end
-
- def test_memoization_flush_with_punctuation
- assert_equal true, @person.name?
- @person.flush_cache(:name?)
- 3.times { assert_equal true, @person.name? }
- assert_equal 2, @person.name_query_calls
- end
-
- def test_memoization_with_nil_value
- assert_equal nil, @person.age
- assert_equal 1, @person.age_calls
-
- 3.times { assert_equal nil, @person.age }
- assert_equal 1, @person.age_calls
- end
-
- def test_reloadable
- assert_equal 1, @calculator.counter
- assert_equal 2, @calculator.counter(:reload)
- assert_equal 2, @calculator.counter
- assert_equal 3, @calculator.counter(true)
- assert_equal 3, @calculator.counter
- end
-
- def test_flush_cache
- assert_equal 1, @calculator.counter
-
- assert @calculator.instance_variable_get(:@_memoized_counter).any?
- @calculator.flush_cache(:counter)
- assert @calculator.instance_variable_get(:@_memoized_counter).empty?
-
- assert_equal 2, @calculator.counter
- end
-
- def test_unmemoize_all
- assert_equal 1, @calculator.counter
-
- assert @calculator.instance_variable_get(:@_memoized_counter).any?
- @calculator.unmemoize_all
- assert @calculator.instance_variable_get(:@_memoized_counter).empty?
-
- assert_equal 2, @calculator.counter
- end
-
- def test_memoize_all
- @calculator.memoize_all
- assert @calculator.instance_variable_defined?(:@_memoized_counter)
- end
-
- def test_memoization_cache_is_different_for_each_instance
- assert_equal 1, @calculator.counter
- assert_equal 2, @calculator.counter(:reload)
- assert_equal 1, Calculator.new.counter
- end
-
- def test_memoized_is_not_affected_by_freeze
- @person.freeze
- assert_equal "Josh", @person.name
- assert_equal "Joshua", @person.update("Joshua")
- end
-
- def test_memoization_with_args
- assert_equal 55, @calculator.fib(10)
- assert_equal 11, @calculator.fib_calls
- end
-
- def test_reloadable_with_args
- assert_equal 55, @calculator.fib(10)
- assert_equal 11, @calculator.fib_calls
- assert_equal 55, @calculator.fib(10, :reload)
- assert_equal 12, @calculator.fib_calls
- assert_equal 55, @calculator.fib(10, true)
- assert_equal 13, @calculator.fib_calls
- end
-
- def test_memoization_with_boolean_arg
- assert_equal 4, @calculator.add_or_subtract(2, 2, true)
- assert_equal 2, @calculator.add_or_subtract(4, 2, false)
- end
-
- def test_object_memoization
- [Company.new, Company.new, Company.new].each do |company|
- ActiveSupport::Deprecation.silence do
- company.extend ActiveSupport::Memoizable
- end
- company.memoize :name
-
- assert_equal "37signals", company.name
- assert_equal 1, company.name_calls
- assert_equal "37signals", company.name
- assert_equal 1, company.name_calls
- end
- end
-
- def test_memoized_module_methods
- assert_equal 1.025, @calculator.sales_tax(10)
- assert_equal 1, @calculator.sales_tax_calls
- assert_equal 1.025, @calculator.sales_tax(10)
- assert_equal 1, @calculator.sales_tax_calls
- assert_equal 2.5625, @calculator.sales_tax(25)
- assert_equal 2, @calculator.sales_tax_calls
- end
-
- def test_object_memoized_module_methods
- company = Company.new
- company.extend(Rates)
-
- assert_equal 1.025, company.sales_tax(10)
- assert_equal 1, company.sales_tax_calls
- assert_equal 1.025, company.sales_tax(10)
- assert_equal 1, company.sales_tax_calls
- assert_equal 2.5625, company.sales_tax(25)
- assert_equal 2, company.sales_tax_calls
- end
-
- def test_double_memoization
- assert_raise(RuntimeError) { Person.memoize :name }
- person = Person.new
- ActiveSupport::Deprecation.silence do
- person.extend ActiveSupport::Memoizable
- end
- assert_raise(RuntimeError) { person.memoize :name }
-
- company = Company.new
- ActiveSupport::Deprecation.silence do
- company.extend ActiveSupport::Memoizable
- end
- company.memoize :name
- assert_raise(RuntimeError) { company.memoize :name }
- end
-
- def test_protected_method_memoization
- person = Person.new
-
- assert_raise(NoMethodError) { person.memoize_protected_test }
- assert_equal "protected", person.send(:memoize_protected_test)
- end
-
- def test_private_method_memoization
- person = Person.new
-
- assert_raise(NoMethodError) { person.is_developer? }
- assert_equal "Yes", person.send(:is_developer?)
- assert_equal 1, person.is_developer_calls
- assert_equal "Yes", person.send(:is_developer?)
- assert_equal 1, person.is_developer_calls
- end
-
-end
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index 83a19f8106..b544742300 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -11,70 +11,75 @@ require 'active_support/time'
require 'active_support/json'
class MessageEncryptorTest < ActiveSupport::TestCase
-
class JSONSerializer
def dump(value)
ActiveSupport::JSON.encode(value)
end
-
+
def load(value)
ActiveSupport::JSON.decode(value)
end
end
-
+
def setup
- @encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64))
+ @secret = SecureRandom.hex(64)
+ @verifier = ActiveSupport::MessageVerifier.new(@secret, :serializer => ActiveSupport::MessageEncryptor::NullSerializer)
+ @encryptor = ActiveSupport::MessageEncryptor.new(@secret)
@data = { :some => "data", :now => Time.local(2010) }
end
- def test_simple_round_tripping
- message = @encryptor.encrypt(@data)
- assert_equal @data, @encryptor.decrypt(message)
- end
-
def test_encrypting_twice_yields_differing_cipher_text
- first_messqage = @encryptor.encrypt(@data)
- second_message = @encryptor.encrypt(@data)
+ first_messqage = @encryptor.encrypt_and_sign(@data).split("--").first
+ second_message = @encryptor.encrypt_and_sign(@data).split("--").first
assert_not_equal first_messqage, second_message
end
- def test_messing_with_either_value_causes_failure
- text, iv = @encryptor.encrypt(@data).split("--")
+ def test_messing_with_either_encrypted_values_causes_failure
+ text, iv = @verifier.verify(@encryptor.encrypt_and_sign(@data)).split("--")
assert_not_decrypted([iv, text] * "--")
assert_not_decrypted([text, munge(iv)] * "--")
assert_not_decrypted([munge(text), iv] * "--")
assert_not_decrypted([munge(text), munge(iv)] * "--")
end
+ def test_messing_with_verified_values_causes_failures
+ text, iv = @encryptor.encrypt_and_sign(@data).split("--")
+ assert_not_verified([iv, text] * "--")
+ assert_not_verified([text, munge(iv)] * "--")
+ assert_not_verified([munge(text), iv] * "--")
+ assert_not_verified([munge(text), munge(iv)] * "--")
+ end
+
def test_signed_round_tripping
message = @encryptor.encrypt_and_sign(@data)
assert_equal @data, @encryptor.decrypt_and_verify(message)
end
-
+
def test_alternative_serialization_method
encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), :serializer => JSONSerializer.new)
message = encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.utc(2010) })
assert_equal encryptor.decrypt_and_verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" }
end
- def test_digest_algorithm_as_second_parameter_deprecation
- assert_deprecated(/options hash/) do
- ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), 'aes-256-cbc')
- end
- end
-
private
- def assert_not_decrypted(value)
- assert_raise(ActiveSupport::MessageEncryptor::InvalidMessage) do
- @encryptor.decrypt(value)
- end
+
+ def assert_not_decrypted(value)
+ assert_raise(ActiveSupport::MessageEncryptor::InvalidMessage) do
+ @encryptor.decrypt_and_verify(@verifier.generate(value))
end
+ end
- def munge(base64_string)
- bits = ActiveSupport::Base64.decode64(base64_string)
- bits.reverse!
- ActiveSupport::Base64.encode64s(bits)
+ def assert_not_verified(value)
+ assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
+ @encryptor.decrypt_and_verify(value)
end
+ end
+
+ def munge(base64_string)
+ bits = ::Base64.decode64(base64_string)
+ bits.reverse!
+ ::Base64.strict_encode64(bits)
+ end
end
end
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index 35747abe5b..5adff41653 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -50,12 +50,6 @@ class MessageVerifierTest < ActiveSupport::TestCase
assert_equal verifier.verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" }
end
- def test_digest_algorithm_as_second_parameter_deprecation
- assert_deprecated(/options hash/) do
- ActiveSupport::MessageVerifier.new("secret", "SHA1")
- end
- end
-
def assert_not_verified(message)
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
@verifier.verify(message)
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index bfff10fff2..90aa13b3e6 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -7,9 +7,10 @@ class String
def __method_for_multibyte_testing_with_integer_result; 1; end
def __method_for_multibyte_testing; 'result'; end
def __method_for_multibyte_testing!; 'result'; end
+ def __method_for_multibyte_testing_that_returns_nil!; end
end
-class MultibyteCharsTest < Test::Unit::TestCase
+class MultibyteCharsTest < ActiveSupport::TestCase
include MultibyteTestHelpers
def setup
@@ -36,11 +37,15 @@ class MultibyteCharsTest < Test::Unit::TestCase
assert_not_equal @chars.object_id, @chars.__method_for_multibyte_testing.object_id
end
- def test_forwarded_bang_method_calls_should_return_the_original_chars_instance
+ def test_forwarded_bang_method_calls_should_return_the_original_chars_instance_when_result_is_not_nil
assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing!
assert_equal @chars.object_id, @chars.__method_for_multibyte_testing!.object_id
end
+ def test_forwarded_bang_method_calls_should_return_nil_when_result_is_nil
+ assert_nil @chars.__method_for_multibyte_testing_that_returns_nil!
+ end
+
def test_methods_are_forwarded_to_wrapped_string_for_byte_strings
assert_equal BYTE_STRING.class, BYTE_STRING.mb_chars.class
end
@@ -67,17 +72,6 @@ class MultibyteCharsTest < Test::Unit::TestCase
assert !@proxy_class.consumes?(BYTE_STRING)
end
- def test_unpack_utf8_strings
- assert_equal 4, ActiveSupport::Multibyte::Unicode.u_unpack(UNICODE_STRING).length
- assert_equal 5, ActiveSupport::Multibyte::Unicode.u_unpack(ASCII_STRING).length
- end
-
- def test_unpack_raises_encoding_error_on_broken_strings
- assert_raise(ActiveSupport::Multibyte::EncodingError) do
- ActiveSupport::Multibyte::Unicode.u_unpack(BYTE_STRING)
- end
- end
-
def test_concatenation_should_return_a_proxy_class_instance
assert_equal ActiveSupport::Multibyte.proxy_class, ('a'.mb_chars + 'b').class
assert_equal ActiveSupport::Multibyte.proxy_class, ('a'.mb_chars << 'b').class
@@ -94,22 +88,18 @@ class MultibyteCharsTest < Test::Unit::TestCase
assert(('a'.mb_chars << 'b'.mb_chars).kind_of?(@proxy_class))
end
+ def test_should_return_string_as_json
+ assert_equal UNICODE_STRING, @chars.as_json
+ end
end
-class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
+class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
include MultibyteTestHelpers
def setup
@chars = UNICODE_STRING.dup.mb_chars
-
- if RUBY_VERSION < '1.9'
- # Multibyte support all kinds of whitespace (ie. NEWLINE, SPACE, EM SPACE)
- @whitespace = "\n\t#{[32, 8195].pack('U*')}"
- else
- # Ruby 1.9 only supports basic whitespace
- @whitespace = "\n\t "
- end
-
+ # Ruby 1.9 only supports basic whitespace
+ @whitespace = "\n\t "
@byte_order_mark = [65279].pack('U')
end
@@ -119,15 +109,11 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
end
end
- def test_indexed_insert_accepts_fixnums
- @chars[2] = 32
- assert_equal 'こに わ', @chars
- end
-
- %w{capitalize downcase lstrip reverse rstrip strip upcase}.each do |method|
+ %w{capitalize downcase lstrip reverse rstrip swapcase upcase}.each do |method|
class_eval(<<-EOTESTS)
- def test_#{method}_bang_should_return_self
- assert_equal @chars.object_id, @chars.send("#{method}!").object_id
+ def test_#{method}_bang_should_return_self_when_modifying_wrapped_string
+ chars = ' él piDió Un bUen café '
+ assert_equal chars.object_id, chars.send("#{method}!").object_id
end
def test_#{method}_bang_should_change_wrapped_string
@@ -150,10 +136,8 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
assert_not_equal original, proxy.to_s
end
- if RUBY_VERSION >= '1.9'
- def test_unicode_string_should_have_utf8_encoding
- assert_equal Encoding::UTF_8, UNICODE_STRING.encoding
- end
+ def test_unicode_string_should_have_utf8_encoding
+ assert_equal Encoding::UTF_8, UNICODE_STRING.encoding
end
def test_identity
@@ -180,6 +164,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
assert chars('').decompose.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars('').compose.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars('').tidy_bytes.kind_of?(ActiveSupport::Multibyte.proxy_class)
+ assert chars('').swapcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
end
def test_should_be_equal_to_the_wrapped_string
@@ -428,7 +413,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
def test_slice_bang_removes_the_slice_from_the_receiver
chars = 'úüù'.mb_chars
chars.slice!(0,2)
- assert_equal 'úü', chars
+ assert_equal 'ù', chars
end
def test_slice_should_throw_exceptions_on_invalid_arguments
@@ -451,6 +436,11 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
assert_equal 'abc', 'aBc'.mb_chars.downcase
end
+ def test_swapcase_should_swap_ascii_characters
+ assert_equal '', ''.mb_chars.swapcase
+ assert_equal 'AbC', 'aBc'.mb_chars.swapcase
+ end
+
def test_capitalize_should_work_on_ascii_characters
assert_equal '', ''.mb_chars.capitalize
assert_equal 'Abc', 'abc'.mb_chars.capitalize
@@ -476,7 +466,7 @@ end
# The default Multibyte Chars proxy has more features than the normal string implementation. Tests
# for the implementation of these features should run on all Ruby versions and shouldn't be tested
# through the proxy methods.
-class MultibyteCharsExtrasTest < Test::Unit::TestCase
+class MultibyteCharsExtrasTest < ActiveSupport::TestCase
include MultibyteTestHelpers
def test_upcase_should_be_unicode_aware
@@ -485,10 +475,15 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
end
def test_downcase_should_be_unicode_aware
- assert_equal "абвгд\0f", chars("аБвгд\0f").downcase
+ assert_equal "абвгд\0f", chars("аБвгд\0F").downcase
assert_equal 'こにちわ', chars('こにちわ').downcase
end
+ def test_swapcase_should_be_unicode_aware
+ assert_equal "аaéÜ\0f", chars("АAÉü\0F").swapcase
+ assert_equal 'こにちわ', chars('こにちわ').swapcase
+ end
+
def test_capitalize_should_be_unicode_aware
{ 'аБвг аБвг' => 'Абвг абвг',
'аБвг АБВГ' => 'Абвг абвг',
@@ -515,7 +510,7 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
def test_limit_should_work_on_a_multibyte_string
example = chars(UNICODE_STRING)
- bytesize = UNICODE_STRING.respond_to?(:bytesize) ? UNICODE_STRING.bytesize : UNICODE_STRING.size
+ bytesize = UNICODE_STRING.bytesize
assert_equal UNICODE_STRING, example.limit(bytesize)
assert_equal '', example.limit(0)
@@ -614,7 +609,7 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
else
str = input
end
- assert_equal expected_length, chars(str).g_length
+ assert_equal expected_length, chars(str).grapheme_length
end
end
diff --git a/activesupport/test/multibyte_conformance.rb b/activesupport/test/multibyte_conformance.rb
index b3b477bb75..2baf724da4 100644
--- a/activesupport/test/multibyte_conformance.rb
+++ b/activesupport/test/multibyte_conformance.rb
@@ -25,7 +25,7 @@ class Downloader
end
end
-class MultibyteConformanceTest < Test::Unit::TestCase
+class MultibyteConformanceTest < ActiveSupport::TestCase
include MultibyteTestHelpers
UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb
index 8839b75601..fdbe2f4350 100644
--- a/activesupport/test/multibyte_test_helpers.rb
+++ b/activesupport/test/multibyte_test_helpers.rb
@@ -3,10 +3,7 @@
module MultibyteTestHelpers
UNICODE_STRING = 'こにちわ'
ASCII_STRING = 'ohayo'
- BYTE_STRING = "\270\236\010\210\245"
- if BYTE_STRING.respond_to?(:force_encoding)
- BYTE_STRING.force_encoding("ASCII-8BIT")
- end
+ BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT")
def chars(str)
ActiveSupport::Multibyte::Chars.new(str)
diff --git a/activesupport/test/multibyte_unicode_database_test.rb b/activesupport/test/multibyte_unicode_database_test.rb
index 26a41579c2..bec65daf50 100644
--- a/activesupport/test/multibyte_unicode_database_test.rb
+++ b/activesupport/test/multibyte_unicode_database_test.rb
@@ -2,7 +2,7 @@
require 'abstract_unit'
-class MultibyteUnicodeDatabaseTest < Test::Unit::TestCase
+class MultibyteUnicodeDatabaseTest < ActiveSupport::TestCase
include ActiveSupport::Multibyte::Unicode
diff --git a/activesupport/test/multibyte_utils_test.rb b/activesupport/test/multibyte_utils_test.rb
deleted file mode 100644
index 0a2f20d282..0000000000
--- a/activesupport/test/multibyte_utils_test.rb
+++ /dev/null
@@ -1,137 +0,0 @@
-# encoding: utf-8
-
-require 'abstract_unit'
-require 'multibyte_test_helpers'
-
-class MultibyteUtilsTest < ActiveSupport::TestCase
- include MultibyteTestHelpers
-
- test "valid_character returns an expression for the current encoding" do
- with_encoding('None') do
- assert_nil ActiveSupport::Multibyte.valid_character
- end
- with_encoding('UTF8') do
- assert_equal ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'], ActiveSupport::Multibyte.valid_character
- end
- with_encoding('SJIS') do
- assert_equal ActiveSupport::Multibyte::VALID_CHARACTER['Shift_JIS'], ActiveSupport::Multibyte.valid_character
- end
- end
-
- test "verify verifies ASCII strings are properly encoded" do
- with_encoding('None') do
- examples.each do |example|
- assert ActiveSupport::Multibyte.verify(example)
- end
- end
- end
-
- test "verify verifies UTF-8 strings are properly encoded" do
- with_encoding('UTF8') do
- assert ActiveSupport::Multibyte.verify(example('valid UTF-8'))
- assert !ActiveSupport::Multibyte.verify(example('invalid UTF-8'))
- end
- end
-
- test "verify verifies Shift-JIS strings are properly encoded" do
- with_encoding('SJIS') do
- assert ActiveSupport::Multibyte.verify(example('valid Shift-JIS'))
- assert !ActiveSupport::Multibyte.verify(example('invalid Shift-JIS'))
- end
- end
-
- test "verify! raises an exception when it finds an invalid character" do
- with_encoding('UTF8') do
- assert_raises(ActiveSupport::Multibyte::EncodingError) do
- ActiveSupport::Multibyte.verify!(example('invalid UTF-8'))
- end
- end
- end
-
- test "verify! doesn't raise an exception when the encoding is valid" do
- with_encoding('UTF8') do
- assert_nothing_raised do
- ActiveSupport::Multibyte.verify!(example('valid UTF-8'))
- end
- end
- end
-
- if RUBY_VERSION < '1.9'
- test "clean leaves ASCII strings intact" do
- with_encoding('None') do
- [
- 'word', "\270\236\010\210\245"
- ].each do |string|
- assert_equal string, ActiveSupport::Multibyte.clean(string)
- end
- end
- end
-
- test "clean cleans invalid characters from UTF-8 encoded strings" do
- with_encoding('UTF8') do
- cleaned_utf8 = [8].pack('C*')
- assert_equal example('valid UTF-8'), ActiveSupport::Multibyte.clean(example('valid UTF-8'))
- assert_equal cleaned_utf8, ActiveSupport::Multibyte.clean(example('invalid UTF-8'))
- end
- end
-
- test "clean cleans invalid characters from Shift-JIS encoded strings" do
- with_encoding('SJIS') do
- cleaned_sjis = [184, 0, 136, 165].pack('C*')
- assert_equal example('valid Shift-JIS'), ActiveSupport::Multibyte.clean(example('valid Shift-JIS'))
- assert_equal cleaned_sjis, ActiveSupport::Multibyte.clean(example('invalid Shift-JIS'))
- end
- end
- else
- test "clean is a no-op" do
- with_encoding('UTF8') do
- assert_equal example('invalid Shift-JIS'), ActiveSupport::Multibyte.clean(example('invalid Shift-JIS'))
- end
- end
- end
-
- private
-
- STRINGS = {
- 'valid ASCII' => [65, 83, 67, 73, 73].pack('C*'),
- 'invalid ASCII' => [128].pack('C*'),
- 'valid UTF-8' => [227, 129, 147, 227, 129, 171, 227, 129, 161, 227, 130, 143].pack('C*'),
- 'invalid UTF-8' => [184, 158, 8, 136, 165].pack('C*'),
- 'valid Shift-JIS' => [131, 122, 129, 91, 131, 128].pack('C*'),
- 'invalid Shift-JIS' => [184, 158, 8, 0, 255, 136, 165].pack('C*')
- }
-
- if Kernel.const_defined?(:Encoding)
- def example(key)
- STRINGS[key].force_encoding(Encoding.default_external)
- end
-
- def examples
- STRINGS.values.map { |s| s.force_encoding(Encoding.default_external) }
- end
- else
- def example(key)
- STRINGS[key]
- end
-
- def examples
- STRINGS.values
- end
- end
-
- if 'string'.respond_to?(:encoding)
- KCODE_TO_ENCODING = Hash.new(Encoding::BINARY).
- update('UTF8' => Encoding::UTF_8, 'SJIS' => Encoding::Shift_JIS)
-
- def with_encoding(enc)
- before = Encoding.default_external
- silence_warnings { Encoding.default_external = KCODE_TO_ENCODING[enc] }
-
- yield
-
- silence_warnings { Encoding.default_external = before }
- end
- else
- alias with_encoding with_kcode
- end
-end
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 884ee61547..fc9fa90d07 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -24,6 +24,26 @@ module Notifications
end
end
+ class SubscribedTest < TestCase
+ def test_subscribed
+ name = "foo"
+ name2 = name * 2
+ expected = [name, name]
+
+ events = []
+ callback = lambda {|*_| events << _.first}
+ ActiveSupport::Notifications.subscribed(callback, name) do
+ ActiveSupport::Notifications.instrument(name)
+ ActiveSupport::Notifications.instrument(name2)
+ ActiveSupport::Notifications.instrument(name)
+ end
+ assert_equal expected, events
+
+ ActiveSupport::Notifications.instrument(name)
+ assert_equal expected, events
+ end
+ end
+
class UnsubscribeTest < TestCase
def test_unsubscribing_removes_a_subscription
@notifier.publish :foo
diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb
index 2bdd3034e5..9d139b61b8 100644
--- a/activesupport/test/option_merger_test.rb
+++ b/activesupport/test/option_merger_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/object/with_options'
-class OptionMergerTest < Test::Unit::TestCase
+class OptionMergerTest < ActiveSupport::TestCase
def setup
@options = {:hello => 'world'}
end
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 0b5f912dc4..e8defd396b 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -3,7 +3,7 @@ require 'active_support/json'
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/hash/indifferent_access'
-class OrderedHashTest < Test::Unit::TestCase
+class OrderedHashTest < ActiveSupport::TestCase
def setup
@keys = %w( blue green red pink orange )
@values = %w( 000099 009900 aa0000 cc0066 cc6633 )
@@ -81,24 +81,21 @@ class OrderedHashTest < Test::Unit::TestCase
keys = []
assert_equal @ordered_hash, @ordered_hash.each_key { |k| keys << k }
assert_equal @keys, keys
- expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
- assert_kind_of expected_class, @ordered_hash.each_key
+ assert_kind_of Enumerator, @ordered_hash.each_key
end
def test_each_value
values = []
assert_equal @ordered_hash, @ordered_hash.each_value { |v| values << v }
assert_equal @values, values
- expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
- assert_kind_of expected_class, @ordered_hash.each_value
+ assert_kind_of Enumerator, @ordered_hash.each_value
end
def test_each
values = []
assert_equal @ordered_hash, @ordered_hash.each {|key, value| values << value}
assert_equal @values, values
- expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
- assert_kind_of expected_class, @ordered_hash.each
+ assert_kind_of Enumerator, @ordered_hash.each
end
def test_each_with_index
@@ -114,9 +111,7 @@ class OrderedHashTest < Test::Unit::TestCase
end
assert_equal @values, values
assert_equal @keys, keys
-
- expected_class = RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator
- assert_kind_of expected_class, @ordered_hash.each_pair
+ assert_kind_of Enumerator, @ordered_hash.each_pair
end
def test_find_all
@@ -296,21 +291,16 @@ class OrderedHashTest < Test::Unit::TestCase
assert_equal @ordered_hash.values, @deserialized_ordered_hash.values
end
- begin
- require 'psych'
-
- def test_psych_serialize
- @deserialized_ordered_hash = Psych.load(Psych.dump(@ordered_hash))
+ def test_psych_serialize
+ @deserialized_ordered_hash = Psych.load(Psych.dump(@ordered_hash))
- values = @deserialized_ordered_hash.map { |_, value| value }
- assert_equal @values, values
- end
+ values = @deserialized_ordered_hash.map { |_, value| value }
+ assert_equal @values, values
+ end
- def test_psych_serialize_tag
- yaml = Psych.dump(@ordered_hash)
- assert_match '!omap', yaml
- end
- rescue LoadError
+ def test_psych_serialize_tag
+ yaml = Psych.dump(@ordered_hash)
+ assert_match '!omap', yaml
end
def test_has_yaml_tag
diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb
index b215b60df3..0eee991e20 100644
--- a/activesupport/test/ordered_options_test.rb
+++ b/activesupport/test/ordered_options_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class OrderedOptionsTest < Test::Unit::TestCase
+class OrderedOptionsTest < ActiveSupport::TestCase
def test_usage
a = ActiveSupport::OrderedOptions.new
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index c28ffa50f2..3f8d09c18e 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -70,7 +70,7 @@ class CoolStargate < Stargate
end
-class RescuableTest < Test::Unit::TestCase
+class RescuableTest < ActiveSupport::TestCase
def setup
@stargate = Stargate.new
@cool_stargate = CoolStargate.new
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 8f77999d25..bdde5141a9 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -1,9 +1,4 @@
require 'abstract_unit'
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'active_support/core_ext/string/inflections'
require 'yaml'
@@ -12,6 +7,10 @@ class SafeBufferTest < ActiveSupport::TestCase
@buffer = ActiveSupport::SafeBuffer.new
end
+ def test_titleize
+ assert_equal 'Foo', "foo".html_safe.titleize
+ end
+
test "Should look like a string" do
assert @buffer.is_a?(String)
assert_equal "", @buffer
@@ -96,13 +95,20 @@ class SafeBufferTest < ActiveSupport::TestCase
assert !@buffer.dup.html_safe?
end
+ test "Should return safe buffer when added with another safe buffer" do
+ clean = "<script>".html_safe
+ result_buffer = @buffer + clean
+ assert result_buffer.html_safe?
+ assert_equal "<script>", result_buffer
+ end
+
test "Should raise an error when safe_concat is called on dirty buffers" do
@buffer.gsub!('', '<>')
assert_raise ActiveSupport::SafeBuffer::SafeConcatError do
@buffer.safe_concat "BUSTED"
end
end
-
+
test "should not fail if the returned object is not a string" do
assert_kind_of NilClass, @buffer.slice("chipchop")
end
diff --git a/activesupport/test/string_inquirer_test.rb b/activesupport/test/string_inquirer_test.rb
index 7f11f667df..bb15916e9e 100644
--- a/activesupport/test/string_inquirer_test.rb
+++ b/activesupport/test/string_inquirer_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class StringInquirerTest < Test::Unit::TestCase
+class StringInquirerTest < ActiveSupport::TestCase
def test_match
assert ActiveSupport::StringInquirer.new("production").production?
end
diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb
index 17c4214dfc..dd4ae319e5 100644
--- a/activesupport/test/tagged_logging_test.rb
+++ b/activesupport/test/tagged_logging_test.rb
@@ -1,9 +1,9 @@
require 'abstract_unit'
-require 'active_support/core_ext/logger'
+require 'active_support/logger'
require 'active_support/tagged_logging'
class TaggedLoggingTest < ActiveSupport::TestCase
- class MyLogger < ::Logger
+ class MyLogger < ::ActiveSupport::Logger
def flush(*)
info "[FLUSHED]"
end
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 756d21b3e4..e5b5547478 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -18,44 +18,50 @@ module ActiveSupport
end
end
- if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
- def test_callback_with_exception
- tc = Class.new(TestCase) do
- setup :bad_callback
- def bad_callback; raise 'oh noes' end
- def test_true; assert true end
+ def test_callback_with_exception
+ tc = Class.new(TestCase) do
+ def self.name
+ nil
end
- test_name = 'test_true'
- fr = FakeRunner.new
+ setup :bad_callback
+ def bad_callback; raise 'oh noes' end
+ def test_true; assert true end
+ end
- test = tc.new test_name
- test.run fr
- klass, name, exception = *fr.puked.first
+ test_name = 'test_true'
+ fr = FakeRunner.new
- assert_equal tc, klass
- assert_equal test_name, name
- assert_equal 'oh noes', exception.message
- end
+ test = tc.new test_name
+ test.run fr
+ klass, name, exception = *fr.puked.first
+
+ assert_equal tc, klass
+ assert_equal test_name, name
+ assert_equal 'oh noes', exception.message
+ end
- def test_teardown_callback_with_exception
- tc = Class.new(TestCase) do
- teardown :bad_callback
- def bad_callback; raise 'oh noes' end
- def test_true; assert true end
+ def test_teardown_callback_with_exception
+ tc = Class.new(TestCase) do
+ def self.name
+ nil
end
- test_name = 'test_true'
- fr = FakeRunner.new
+ teardown :bad_callback
+ def bad_callback; raise 'oh noes' end
+ def test_true; assert true end
+ end
- test = tc.new test_name
- test.run fr
- klass, name, exception = *fr.puked.first
+ test_name = 'test_true'
+ fr = FakeRunner.new
- assert_equal tc, klass
- assert_equal test_name, name
- assert_equal 'oh noes', exception.message
- end
+ test = tc.new test_name
+ test.run fr
+ klass, name, exception = *fr.puked.first
+
+ assert_equal tc, klass
+ assert_equal test_name, name
+ assert_equal 'oh noes', exception.message
end
end
end
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index f880052786..11506554a9 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -61,24 +61,19 @@ class AssertDifferenceTest < ActiveSupport::TestCase
end
def test_array_of_expressions_identify_failure
- assert_difference ['@object.num', '1 + 1'] do
- @object.increment
+ assert_raises(MiniTest::Assertion) do
+ assert_difference ['@object.num', '1 + 1'] do
+ @object.increment
+ end
end
- fail 'should not get to here'
- rescue Exception => e
- assert_match(/didn't change by/, e.message)
- assert_match(/expected but was/, e.message)
end
def test_array_of_expressions_identify_failure_when_message_provided
- assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do
- @object.increment
+ assert_raises(MiniTest::Assertion) do
+ assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do
+ @object.increment
+ end
end
- fail 'should not get to here'
- rescue Exception => e
- assert_match(/something went wrong/, e.message)
- assert_match(/didn't change by/, e.message)
- assert_match(/expected but was/, e.message)
end
else
def default_test; end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 3575175517..e26256f9c6 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
-class TimeZoneTest < Test::Unit::TestCase
+class TimeZoneTest < ActiveSupport::TestCase
def test_utc_to_local
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
assert_equal Time.utc(1999, 12, 31, 19), zone.utc_to_local(Time.utc(2000, 1)) # standard offset -0500
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index 08e11d4f38..b7076e9e58 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
require 'active_support/inflector/transliterate'
require 'active_support/core_ext/object/inclusion'
-class TransliterateTest < Test::Unit::TestCase
+class TransliterateTest < ActiveSupport::TestCase
def test_transliterate_should_not_change_ascii_chars
(0..127).each do |byte|
diff --git a/activesupport/test/ts_isolated.rb b/activesupport/test/ts_isolated.rb
index 58710e0165..1d96c20bb6 100644
--- a/activesupport/test/ts_isolated.rb
+++ b/activesupport/test/ts_isolated.rb
@@ -1,10 +1,11 @@
$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
-require 'test/unit'
+require 'minitest/autorun'
+require 'active_support/test_case'
require 'rbconfig'
require 'active_support/core_ext/kernel/reporting'
-class TestIsolated < Test::Unit::TestCase
+class TestIsolated < ActiveSupport::TestCase
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
Dir["#{File.dirname(__FILE__)}/**/*_test.rb"].each do |file|
diff --git a/activesupport/test/whiny_nil_test.rb b/activesupport/test/whiny_nil_test.rb
deleted file mode 100644
index 1acaf7228f..0000000000
--- a/activesupport/test/whiny_nil_test.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# Stub to enable testing without Active Record
-module ActiveRecord
- class Base
- def save!
- end
- end
-end
-
-require 'abstract_unit'
-require 'active_support/whiny_nil'
-
-NilClass.add_whiner ::ActiveRecord::Base
-
-class WhinyNilTest < Test::Unit::TestCase
- def test_unchanged
- nil.method_thats_not_in_whiners
- rescue NoMethodError => nme
- assert_match(/nil:NilClass/, nme.message)
- end
-
- def test_active_record
- nil.save!
- rescue NoMethodError => nme
- assert_no_match(/nil:NilClass/, nme.message)
- assert_match(/nil\.save!/, nme.message)
- end
-
- def test_array
- nil.each
- rescue NoMethodError => nme
- assert_no_match(/nil:NilClass/, nme.message)
- assert_match(/nil\.each/, nme.message)
- end
-
- def test_id
- nil.id
- rescue RuntimeError => nme
- assert_no_match(/nil:NilClass/, nme.message)
- assert_match(Regexp.new(nil.object_id.to_s), nme.message)
- end
-
- def test_no_to_ary_coercion
- nil.to_ary
- rescue NoMethodError => nme
- assert_no_match(/nil:NilClass/, nme.message)
- assert_match(/nil\.to_ary/, nme.message)
- end
-
- def test_no_to_str_coercion
- nil.to_str
- rescue NoMethodError => nme
- assert_match(/nil:NilClass/, nme.message)
- end
-end
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index 7f809e7898..f77d78d42c 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -3,7 +3,7 @@ if RUBY_PLATFORM =~ /java/
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
- class JDOMEngineTest < Test::Unit::TestCase
+ class JDOMEngineTest < ActiveSupport::TestCase
include ActiveSupport
def setup
diff --git a/activesupport/test/xml_mini/libxml_engine_test.rb b/activesupport/test/xml_mini/libxml_engine_test.rb
index 83d03bccc6..5debb2fd59 100644
--- a/activesupport/test/xml_mini/libxml_engine_test.rb
+++ b/activesupport/test/xml_mini/libxml_engine_test.rb
@@ -8,7 +8,7 @@ rescue LoadError
# Skip libxml tests
else
-class LibxmlEngineTest < Test::Unit::TestCase
+class LibxmlEngineTest < ActiveSupport::TestCase
include ActiveSupport
def setup
diff --git a/activesupport/test/xml_mini/libxmlsax_engine_test.rb b/activesupport/test/xml_mini/libxmlsax_engine_test.rb
index 864810099e..94250d48ec 100644
--- a/activesupport/test/xml_mini/libxmlsax_engine_test.rb
+++ b/activesupport/test/xml_mini/libxmlsax_engine_test.rb
@@ -8,7 +8,7 @@ rescue LoadError
# Skip libxml tests
else
-class LibXMLSAXEngineTest < Test::Unit::TestCase
+class LibXMLSAXEngineTest < ActiveSupport::TestCase
include ActiveSupport
def setup
diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb
index db0d7c5b02..3f37c7cbb6 100644
--- a/activesupport/test/xml_mini/nokogiri_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb
@@ -8,7 +8,7 @@ rescue LoadError
# Skip nokogiri tests
else
-class NokogiriEngineTest < Test::Unit::TestCase
+class NokogiriEngineTest < ActiveSupport::TestCase
include ActiveSupport
def setup
diff --git a/activesupport/test/xml_mini/nokogirisax_engine_test.rb b/activesupport/test/xml_mini/nokogirisax_engine_test.rb
index 1149d0fecc..d6ae7f12ae 100644
--- a/activesupport/test/xml_mini/nokogirisax_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogirisax_engine_test.rb
@@ -8,7 +8,7 @@ rescue LoadError
# Skip nokogiri tests
else
-class NokogiriSAXEngineTest < Test::Unit::TestCase
+class NokogiriSAXEngineTest < ActiveSupport::TestCase
include ActiveSupport
def setup
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index 57bb35254a..c4770405f2 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/xml_mini'
-class REXMLEngineTest < Test::Unit::TestCase
+class REXMLEngineTest < ActiveSupport::TestCase
include ActiveSupport
def test_default_is_rexml
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index dde17ea403..504fc96493 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -3,7 +3,7 @@ require 'active_support/xml_mini'
require 'active_support/builder'
module XmlMiniTest
- class RenameKeyTest < Test::Unit::TestCase
+ class RenameKeyTest < ActiveSupport::TestCase
def test_rename_key_dasherizes_by_default
assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key")
end
diff --git a/ci/travis.rb b/ci/travis.rb
index 8087c72f90..52e146df70 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -132,7 +132,7 @@ failures = results.select { |key, value| value == false }
if failures.empty?
puts
- puts "Rails build finished sucessfully"
+ puts "Rails build finished successfully"
exit(true)
else
puts
diff --git a/install.rb b/install.rb
index 05bba27a14..abc02249c2 100644
--- a/install.rb
+++ b/install.rb
@@ -8,4 +8,4 @@ end
puts "Installing Rails..."
`gem build rails.gemspec`
`gem install rails-#{version}.gem --no-ri --no-rdoc `
-`rm rails-#{version}.gem` \ No newline at end of file
+`rm rails-#{version}.gem`
diff --git a/load_paths.rb b/load_paths.rb
index 17f5ce180d..6b224d4ad5 100644
--- a/load_paths.rb
+++ b/load_paths.rb
@@ -1,4 +1,4 @@
# bust gem prelude
require 'rubygems' unless defined? Gem
require 'bundler'
-Bundler.setup \ No newline at end of file
+Bundler.setup
diff --git a/rails.gemspec b/rails.gemspec
index 3377b4e175..cd7c5d1ee9 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -7,8 +7,8 @@ Gem::Specification.new do |s|
s.summary = 'Full-stack web application framework.'
s.description = 'Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.'
- s.required_ruby_version = '>= 1.8.7'
- s.required_rubygems_version = ">= 1.3.6"
+ s.required_ruby_version = '>= 1.9.3'
+ s.required_rubygems_version = ">= 1.8.11"
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
deleted file mode 100644
index 404bc73b0b..0000000000
--- a/railties/CHANGELOG
+++ /dev/null
@@ -1,2390 +0,0 @@
-*Rails 3.2.0 (unreleased)*
-
-* Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. [José Valim]
-
-* Updated Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications [DHH]
-
-* Default options to `rails new` can be set in ~/.railsrc [Guillermo Iguaran]
-
-* Added destroy alias to Rails engines. [Guillermo Iguaran]
-
-* Added destroy alias for Rails command line. This allows the following: `rails d model post`. [Andrey Ognevsky]
-
-* Attributes on scaffold and model generators default to string. This allows the following: "rails g scaffold Post title body:text author" [José Valim]
-
-* Removed old plugin generator (`rails generate plugin`) in favor of `rails plugin new` command. [Guillermo Iguaran]
-
-* Removed old 'config.paths.app.controller' API in favor of 'config.paths["app/controller"]' API. [Guillermo Iguaran]
-
-
-*Rails 3.1.1
-
-* Add jquery-rails to Gemfile of plugins, test/dummy app needs it. Closes #3091. [Santiago Pastorino]
-
-* Add config.assets.initialize_on_precompile which, when set to false, forces
- `rake assets:precompile` to load the application but does not initialize it.
-
- To the app developer, this means configuration add in
- config/initializers/* will not be executed.
-
- Plugins developers need to special case their initializers that are
- meant to be run in the assets group by adding :group => :assets.
-
-
-*Rails 3.1.0 (August 30, 2011)*
-
-* The default database schema file is written as UTF-8. [Aaron Patterson]
-
-* Generated apps with --dev or --edge flags depend on git versions of
-sass-rails and coffee-rails. [Santiago Pastorino]
-
-* Rack::Sendfile middleware is used only if x_sendfile_header is present. [Santiago Pastorino]
-
-* Add JavaScript Runtime name to the Rails Info properties. [DHH]
-
-* Make pp enabled by default in Rails console. [Akira Matsuda]
-
-* Add alias `r` for rails runner. [Jordi Romero]
-
-* Make sprockets/railtie require explicit and add --skip-sprockets to app generator [José Valim]
-
-* Added Rails.groups that automatically handles Rails.env and ENV["RAILS_GROUPS"] [José Valim]
-
-* The new rake task assets:clean removes precompiled assets. [fxn]
-
-* Application and plugin generation run bundle install unless --skip-gemfile or --skip-bundle. [fxn]
-
-* Fixed database tasks for jdbc* adapters #jruby [Rashmi Yadav]
-
-* Template generation for jdbcpostgresql #jruby [Vishnu Atrai]
-
-* Template generation for jdbcmysql and jdbcsqlite3 #jruby [Arun Agrawal]
-
-* The -j option of the application generator accepts an arbitrary string. If passed "foo",
-the gem "foo-rails" is added to the Gemfile, and the application JavaScript manifest
-requires "foo" and "foo_ujs". As of this writing "prototype-rails" and "jquery-rails"
-exist and provide those files via the asset pipeline. Default is "jquery". [fxn]
-
-* jQuery is no longer vendored, it is provided from now on by the jquery-rails gem. [fxn]
-
-* Prototype and Scriptaculous are no longer vendored, they are provided from now on
-by the prototype-rails gem. [fxn]
-
-* The scaffold controller will now produce SCSS file if Sass is available [Prem Sichanugrist]
-
-* The controller and resource generators will now automatically produce asset stubs (this can be turned off with --skip-assets). These stubs will use Coffee and Sass, if those libraries are available. [DHH]
-
-* jQuery is the new default JavaScript library. [fxn]
-
-* Changed scaffold, application, and mailer generator to create Ruby 1.9 style hash when running on Ruby 1.9 [Prem Sichanugrist]
-
- So instead of creating something like:
-
- redirect_to users_path, :notice => "User has been created"
-
- it will now be like this:
-
- redirect_to users_path, notice: "User has been created"
-
- You can also passing `--old-style-hash` to make Rails generate old style hash even you're on Ruby 1.9
-
-* Changed scaffold_controller generator to create format block for JSON instead of XML [Prem Sichanugrist]
-
-* Add using Turn with natural language test case names for test_help.rb when running with minitest (Ruby 1.9.2+) [DHH]
-
-* Direct logging of Active Record to STDOUT so it's shown inline with the results in the console [DHH]
-
-* Added `config.force_ssl` configuration which loads Rack::SSL middleware and force all requests to be under HTTPS protocol [DHH, Prem Sichanugrist, and Josh Peek]
-
-* Added `rails plugin new` command which generates rails plugin with gemspec, tests and dummy application for testing [Piotr Sarnacki]
-
-* Added -j parameter with jquery/prototype as options. Now you can create your apps with jQuery using `rails new myapp -j jquery`. The default is still Prototype. [siong1987]
-
-* Added Rack::Etag and Rack::ConditionalGet to the default middleware stack [José Valim]
-
-* Added Rack::Cache to the default middleware stack [Yehuda Katz and Carl Lerche]
-
-* Engine is now rack application [Piotr Sarnacki]
-
-* Added middleware stack to Engine [Piotr Sarnacki]
-
-* Engine can now load plugins [Piotr Sarnacki]
-
-* Engine can load its own environment file [Piotr Sarnacki]
-
-* Added helpers to call engines' route helpers from application and vice versa [Piotr Sarnacki]
-
-* Task for copying plugins' and engines' migrations to application's db/migrate directory [Piotr Sarnacki]
-
-* Changed ActionDispatch::Static to allow handling multiple directories [Piotr Sarnacki]
-
-* Added isolate_namespace() method to Engine, which sets Engine as isolated [Piotr Sarnacki]
-
-* Include all helpers from plugins and shared engines in application [Piotr Sarnacki]
-
-
-*Rails 3.0.7 (April 18, 2011)*
-
-*No changes.
-
-
-*Rails 3.0.6 (April 5, 2011)
-
-* No changes.
-
-
-*Rails 3.0.5 (February 26, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.4 (February 8, 2011)*
-
-* No changes.
-
-
-*Rails 3.0.3 (November 16, 2010)*
-
-* No changes.
-
-
-*Rails 3.0.2 (November 15, 2010)*
-
-* No changes.
-
-
-*Rails 3.0.1 (October 15, 2010)*
-
-* No Changes, just a version bump.
-
-
-*Rails 3.0.0 (August 29, 2010)*
-
-* Application generation: --skip-testunit and --skip-activerecord become --skip-test-unit and --skip-active-record respectively. [fxn]
-
-* Added console to Rails::Railtie as a hook called just after console starts. [José Valim]
-
-* Rails no longer autoload code in lib for application. You need to explicitly require it. [José Valim]
-
-* Rails::LogSubscriber was renamed to ActiveSupport::LogSubscriber [José Valim]
-
-* config.load_(once_)paths in config/application.rb got renamed to config.autoload_(once_)paths. [fxn]
-
-* Abort generation/booting on Ruby 1.9.1. [fxn]
-
-* Made the rails command work even when you're in a subdirectory [Chad Fowler]
-
-* Removed Rails Metal [Yehuda Katz, José Valim].
-
-* Renamed config.cookie_secret to config.secret_token and pass it as env key. [José Valim]
-
-* Session store configuration has changed [Yehuda Katz, Carl Lerche]
-
- config.session_store :cookie_store, {:key => "..."}
- config.cookie_secret = "fdsfhisdghfidugnfdlg"
-
-* railtie_name and engine_name are deprecated. You can now add any object to
- the configuration object: config.your_plugin = {} [José Valim]
-
-* Added config.generators.templates to provide alternative paths for the generators
- to look for templates [José Valim]
-
-* Added "rake about" as a replacement for script/about [DHH]
-
-* Removed all the default commands in script/* and replaced them with script/rails and a rails command that'll act the same when run from within the app [DHH]. Example:
-
- ./script/generate scaffold post title:string can now be called as rails g scaffold post title:string
-
- Run rails --help inside an app for more help.
-
-* Removed config/initializers/new_rails_defaults.rb as all frameworks now follow the settings from it [DHH]
-
-* Set config.time_zone to UTC by default [DHH]
-
-* Added default .gitignore (this is just recognizing Git market share, don't throw a hissy if you use another SCM) [DHH]
-
-* Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH]. Examples:
-
- cookies.permanent[:prefers_open_id] = true
- # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
- cookies.signed[:discount] = 45
- # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
-
- cookies.signed[:discount]
- # => 45 (if the cookie was changed, you'll get a InvalidSignature exception)
-
- cookies.permanent.signed[:remember_me] = current_user.id
- # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
- ...to use the signed cookies, you need to set a secret to ActionController::Base.cookie_verifier_secret (automatically done in config/initializers/cookie_verification_secret.rb for new Rails applications).
-
-* Added config/initializers/cookie_verification_secret.rb with an auto-generated secret for using ActionController::Base#cookies.signed [DHH]
-
-* Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs [DHH]
-
-* I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs]
-
-* 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]
-
-* Allow metal to live in plugins #2045 [Matthew Rudy]
-
-* Added metal [Josh Peek]
-
-* Remove script/performance/request in favour of the performance integration tests. [Pratik Naik]
-
- To continue using script/performance/request, install the request_profiler plugin :
-
- script/plugin install git://github.com/rails/request_profiler.git
-
-* Add a rake task to apply a template to an existing application : rake rails:template LOCATION=~/template.rb [Pratik Naik]
-
-* Add "-m/--template" option to Rails generator to apply a template to the generated application. [Jeremy McAnally]
-
- This has been extracted from rg - http://github.com/jeremymcanally/rg
-
- Example:
-
- # template.rb
-
- # Install plugins from git or svn
- plugin "will-paginate", :git => "git://github.com/mislav/will_paginate.git"
- plugin "old-restful-auth", :svn => "http://svn.techno-weenie.net/projects/plugins/restful_authentication/"
-
- # Add gems to environment.rb
- gem "jeremymcanally-context"
- gem "bluecloth"
-
- # Vendor file. Data in a string or...
- vendor("borrowed.rb", <<CODE
- def helpful_method
- do_something_helpful_here
- end
- CODE
-
- # ...file data from block return value.
- # #initializer creates a new initializer file
- initializer("crypto.rb") do
- salt = "--#{Time.now}--#{rand}--#{srand(Time.now.to_i)}"
-
- "SPECIAL_SALT = '#{salt}'"
- end
-
- Usage:
-
- To use a template, provide a file path or URL:
-
- 1. Using a local file :
-
- rails <application name> -m /path/to/my/template.rb
-
- 2. Or directly from a URL :
-
- rails <application name> --template=http://gist.github.com/31208.txt
-
-* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts [David Heinemeier Hansson]
-
-* Changed Rails.root to return a Pathname object (allows for Rails.root.join('app', 'controllers') => "#{RAILS_ROOT}/app/controllers") #1482 [Damian Janowski/?]
-
-* Added view path support for engines [David Heinemeier Hansson]
-
-* Added that config/routes.rb files in engine plugins are automatically loaded (and reloaded when they change in dev mode) [David Heinemeier Hansson]
-
-* Added app/[models|controllers|helpers] to the load path for plugins that has an app directory (go engines ;)) [David Heinemeier Hansson]
-
-* Add config.preload_frameworks to load all frameworks at startup. Default to false so Rails autoloads itself as it's used. Turn this on for Passenger and JRuby. Also turned on by config.threadsafe! [Jeremy Kemper]
-
-* Add a rake task to generate dispatchers : rake rails:generate_dispatchers [Pratik Naik]
-
-* "rails <app>" will not generate public/dispatch.cgi/fcgi/rb files by default now. Please use "--with-dispatchers" option if you need them. [Yaroslav Markin, Pratik Naik]
-
-* Added rake rails:update:application_controller to renamed application.rb to application_controller.rb -- included in rake rails:update so upgrading to 2.3 will automatically trigger it #1439 [kastner]
-
-* Added Rails.backtrace_cleaner as an accessor for the Rails::BacktraceCleaner instance used by the framework to cut down on backtrace noise and config/initializers/backtrace_silencers.rb to add your own (or turn them all off) [David Heinemeier Hansson]
-
-* Switch from Test::Unit::TestCase to ActiveSupport::TestCase. [Jeremy Kemper]
-
-* Added config.i18n settings gatherer to config/environment, auto-loading of all locales in config/locales/*.rb,yml, and config/locales/en.yml as a sample locale [David Heinemeier Hansson]
-
-* BACKWARDS INCOMPATIBLE: Renamed application.rb to application_controller.rb and removed all the special casing that was in place to support the former. You must do this rename in your own application when you upgrade to this version [David Heinemeier Hansson]
-
-
-*2.2.1 [RC2] (November 14th, 2008)*
-
-* Fixed plugin generator so that generated unit tests would subclass ActiveSupport::TestCase, also introduced a helper script to reduce the needed require statements #1137 [Mathias Meyer]
-
-* Update Prototype to 1.6.0.3 [sam]
-
-
-*2.2.0 [RC1] (October 24th, 2008)*
-
-* Fixed that sqlite would report "db/development.sqlite3 already exists" whether true or not on db:create #614 [Antonio Cangiano]
-
-* Added config.threadsafe! to toggle allow concurrency settings and disable the dependency loader [Josh Peek]
-
-* Turn cache_classes on by default [Josh Peek]
-
-* Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek]
-
-* Introduce simple internationalization support. [Ruby i18n team]
-
-* Make script/plugin install <plugin> -r <revision> option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example:
-
- script/plugin install git://github.com/mislav/will_paginate.git -r agnostic # Installs 'agnostic' branch
- script/plugin install git://github.com/dchelimsky/rspec.git -r 'tag 1.1.4'
-
-* Added Rails.initialized? flag [Josh Peek]
-
-* Make rake test:uncommitted work with Git. [Tim Pope]
-
-* Added Thin support to script/server. #488 [Bob Klosinski]
-
-* Fix script/about in production mode. #370 [Cheah Chu Yeow, Xavier Noria, David Krmpotic]
-
-* Add the gem load paths before the framework is loaded, so certain gems like RedCloth and BlueCloth can be frozen.
-
-* Fix discrepancies with loading rails/init.rb from gems.
-
-* Plugins check for the gem init path (rails/init.rb) before the standard plugin init path (init.rb) [Jacek Becela]
-
-* Changed all generated tests to use the test/do declaration style [David Heinemeier Hansson]
-
-* Wrapped Rails.env in StringInquirer so you can do Rails.env.development? [David Heinemeier Hansson]
-
-* Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) [#310 state:resolved]
-
-
-*2.1.0 (May 31st, 2008)*
-
-* script/dbconsole fires up the command-line database client. #102 [Steve Purcell]
-
-* Fix bug where plugin init.rb files from frozen gem specs weren't being run. (pjb3) [#122 state:resolved]
-
-* Made the location of the routes file configurable with config.routes_configuration_file (Scott Fleckenstein) [#88]
-
-* Rails Edge info returns the latest git commit hash [Francesc Esplugas]
-
-* Added Rails.public_path to control where HTML and assets are expected to be loaded from (defaults to Rails.root + "/public") #11581 [Nick Sieger]
-
-* rake time:zones:local finds correct base utc offset for zones in the Southern Hemisphere [Geoff Buesing]
-
-* Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. [Rick Olson]
-
-* script/performance/profiler compatibility with the ruby-prof >= 0.5.0. Closes #9176. [Jonathan del Strother]
-
-* Flesh out rake gems:unpack to unpack all gems, and add rake gems:build for native extensions. #11513 [ddollar]
-
- rake gems:unpack # unpacks all gems
- rake gems:unpack GEM=mygem # unpacks only the gem 'mygem'
-
- rake gems:build # builds all unpacked gems
- rake gems:build GEM=mygem # builds only the gem 'mygem'
-
-* Add config.active_support for future configuration options. Also, add more new Rails 3 config settings to new_rails_defaults.rb [Rick Olson]
-
-* Add Rails.logger, Rails.root, Rails.env and Rails.cache shortcuts for RAILS_* constants [Pratik Naik]
-
-* Allow files in plugins to be reloaded like the rest of the application. [Rick Olson]
-
- Enables or disables plugin reloading.
-
- config.reload_plugins = true
-
- You can get around this setting per plugin.
- If #reload_plugins? == false (DEFAULT), add this to your plugin's init.rb to make it reloadable:
-
- Dependencies.load_once_paths.delete lib_path
-
- If #reload_plugins? == true, add this to your plugin's init.rb to only load it once:
-
- Dependencies.load_once_paths << lib_path
-
-* Small tweak to allow plugins to specify gem dependencies. [Rick Olson]
-
- # OLD open_id_authentication plugin init.rb
- require 'yadis'
- require 'openid'
- ActionController::Base.send :include, OpenIdAuthentication
-
- # NEW
- config.gem "ruby-openid", :lib => "openid", :version => "1.1.4"
- config.gem "ruby-yadis", :lib => "yadis", :version => "0.3.4"
-
- config.after_initialize do
- ActionController::Base.send :include, OpenIdAuthentication
- end
-
-* Added config.gem for specifying which gems are required by the application, as well as rake tasks for installing and freezing gems. [Rick Olson]
-
- Rails::Initializer.run do |config|
- config.gem "bj"
- config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
- config.gem "aws-s3", :lib => "aws/s3"
- end
-
- # List required gems.
- rake gems
-
- # Install all required gems:
- rake gems:install
-
- # Unpack specified gem to vendor/gems/gem_name-x.x.x
- rake gems:unpack GEM=bj
-
-* Removed the default .htaccess configuration as there are so many good deployment options now (kept it as an example in README) [David Heinemeier Hansson]
-
-* config.time_zone accepts TZInfo::Timezone identifiers as well as Rails TimeZone identifiers [Geoff Buesing]
-
-* Rails::Initializer#initialize_time_zone raises an error if value assigned to config.time_zone is not recognized. Rake time zone tasks only require ActiveSupport instead of entire environment [Geoff Buesing]
-
-* Stop adding the antiquated test/mocks/* directories and only add them to the path if they're still there for legacy reasons [David Heinemeier Hansson]
-
-* Added that gems can now be plugins if they include rails/init.rb #11444 [John Barnette]
-
-* Added Plugin#about method to programmatically access the about.yml in a plugin #10979 [James Adam]
-
- plugin = Rails::Plugin.new(path_to_my_plugin)
- plugin.about["author"] # => "James Adam"
- plugin.about["url"] # => "http://interblah.net"
-
-* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-
-* Added config.time_zone = 'UTC' in the default environment.rb [Geoff Buesing]
-
-* Added rake tasks time:zones:all, time:zones:us and time:zones:local for finding time zone names for config.time_zone option [Geoff Buesing]
-
-* Add config.time_zone for configuring the default Time.zone value. #10982 [Geoff Buesing]
-
-* Added support for installing plugins hosted at git repositories #11294 [Jack Danger Canty]
-
-* Fixed that script/generate would not look for plugin generators in plugin_paths #11000 [glv]
-
-* Fixed database rake tasks to work with charset/collation and show proper error messages on failure. Closes #11301 [matt]
-
-* Added a -e/--export to script/plugin install, uses svn export. #10847 [jon@blankpad.net)]
-
-* Reshuffle load order so that routes and observers are initialized after plugins and app initializers. Closes #10980 [Rick Olson]
-
-* Git support for script/generate. #10690 [ssoroka]
-
-* Update scaffold to use labels instead of bold tags. Closes #10757 [zach-inglis-lt3]
-
-* Resurrect WordNet synonym lookups. #10710 [tom./, matt]
-
-* Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) [David Heinemeier Hansson]
-
-* Added that rails:update is run when you do rails:freeze:edge to ensure you also get the latest JS and config files #10565 [jeff]
-
-* SQLite: db:drop:all doesn't fail silently if the database is already open. #10577 [Cheah Chu Yeow, mrichman]
-
-* Introduce native mongrel handler and push mutex into dispatcher. [Jeremy Kemper]
-
-* Ruby 1.9 compatibility. #1689, #10546 [Cheah Chu Yeow, frederico]
-
-
-*2.0.2* (December 16th, 2007)
-
-* Changed the default database from mysql to sqlite3, so now running "rails myapp" will have a config/database.yml that's setup for SQLite3 (which in OS X Leopard is installed by default, so is the gem, so everything Just Works with no database configuration at all). To get a Rails application preconfigured for MySQL, just run "rails -d mysql myapp" [David Heinemeier Hansson]
-
-* Turned on ActionView::Base.cache_template_loading by default in config/environments/production.rb to prevent file system stat calls for every template loading to see if it changed (this means that you have to restart the application to see template changes in production mode) [David Heinemeier Hansson]
-
-* Introduce `rake secret` to output a crytographically secure secret key for use with cookie sessions #10363 [revans]
-
-* Fixed that local database creation should consider 127.0.0.1 local #9026 [parcelbrat]
-
-* Fixed that functional tests generated for scaffolds should use fixture calls instead of hard-coded IDs #10435 [boone]
-
-* Added db:migrate:redo and db:migrate:reset for rerunning existing migrations #10431, #10432 [matt]
-
-* RAILS_GEM_VERSION may be double-quoted also. #10443 [James Cox]
-
-* Update rails:freeze:gems to work with RubyGems 0.9.5. [Jeremy Kemper]
-
-
-*2.0.1* (December 7th, 2007)
-
-* Fixed Active Record bug
-
-
-*2.0.0* (December 6th, 2007)
-
-* The test task stops with a warning if you have pending migrations. #10377 [Josh Knowles]
-
-* Add warning to documentation about using transactional fixtures when the code under test uses transactions itself. Closes #7548 [Thijs van der Vossen]
-
-* Update Prototype to 1.6.0.1. [sam]
-
-* Update script.aculo.us to 1.8.0.1. [madrobby]
-
-* Added db:fixtures:identity as a way of locating what ID a foxy fixture was assigned #10332 [John Barnette]
-
-* Generated fixtures should not specify ids since theyre expected to be foxy fixtures #10330 [John Barnette]
-
-* Update to Prototype -r8232. [sam]
-
-* Introduce SecretKeyGenerator for more secure session secrets than CGI::Session's pseudo-random id generator. Consider extracting to Active Support later. #10286 [Hongli Lai (Phusion)]
-
-* RAILS_GEM_VERSION may be set to any valid gem version specifier. #10057 [Chad Woolley, Cheah Chu Yeow]
-
-* Load config/preinitializer.rb, if present, before loading the environment. #9943 [Chad Woolley]
-
-* FastCGI handler ignores unsupported signals like USR2 on Windows. [Grzegorz Derebecki]
-
-* Only load ActionMailer::TestCase if ActionMailer is loaded. Closes #10137 [defunkt]
-
-* Fixed that db:reset would use migrations instead of loading db/schema.rb [David Heinemeier Hansson]
-
-* Ensure the plugin loader only loads plugins once. Closes #10102 [haruki_zaemon]
-
-* Refactor Plugin Loader. Add plugin lib paths early, and add lots of tests. Closes #9795 [James Adam]
-
-* Added --skip-timestamps to generators that produce models #10036 [Tim Pope]
-
-* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. [sam, madrobby]
-
-* Added db:rollback to rollback the schema one version (or multiple as specified by STEP) [Jeffrey Allan Hardy]
-
-* Fix typo in test_helper. Closes #9925 [viktor tron]
-
-* Request profiler. [Jeremy Kemper]
-
-* config/boot.rb correctly detects RAILS_GEM_VERSION. #9834 [alexch, thewoolleyman]
-
-* Fixed incorrect migration number if script/generate executed outside of Rails root #7080 [Jeremy McAnally]
-
-* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0. [sam, madrobby]
-
-* Generated fixtures use the actual primary key instead of id. #4343 [Frederick Ros, Tarmo Tänav]
-
-* Extend the console +helper+ method to allow you to include custom helpers. e.g:
- >> helper :posts
- >> helper.some_method_from_posts_helper(Post.find(1))
-
-* db:create works with remote databases whereas db:create:all only creates
-databases on localhost. #9753 [Trevor Wennblom]
-
-* Removed calls to fixtures in generated tests as fixtures :all is now present by default in test_helper.rb [David Heinemeier Hansson]
-
-* Add --prefix option to script/server when using mongrel. [dacat]
-
-
-*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.1.4 - 1.2.3]
-
-* Fixed that installing plugins from SVN repositories that use trunk/ will work #8188 [evan]
-
-* Moved the SourceAnnotationExtractor to a separate file in case libraries try to load the rails rake tasks twice. [Rick Olson]
-
-* Moved Dispatcher to ActionController::Dispatcher. [Jeremy Kemper]
-
-* Changed the default logger from Ruby's own Logger with the clean_logger extensions to ActiveSupport::BufferedLogger for performance reasons [David Heinemeier Hansson]. (You can change it back with config.logger = Logger.new("/path/to/log", level).)
-
-* Added a default 422.html page to be rendered when ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, or ActionController::InvalidAuthenticityToken is raised [David Heinemeier Hansson]
-
-* Added --skip-fixture option to script/generate model #6862 [sandofsky]
-
-* Print Rails version when starting console #7440 [eyematz]
-
-* Fixed the placement of fixture files for nested models when generating through script/generate model #7547 [jkit]
-
-* Added TEMPLATE option to rake doc:app to set a custom output template #7737 [Jakob Skjerning]
-
-* Added VERBOSE option to rake db:migrate to turn off output #8204 [John Barnette]
-
-* Fixed that rake doc:app should use UTF-8 #8906 [farzy]
-
-* Fixes rake annotations to search erb and builder files as well #9150 [m.langenberg]
-
-* Removed web_service generator [Michael Koziarski]
-
-* Added the :all option to config.plugins that'll include the rest of the plugins not already explicitly named #9613 [Frederick Cheung]. Example:
-
- # Loads :classic_pagination before all the other plugins
- config.plugins = [ :classic_pagination, :all ]
-
-* Added symbols as a legal way of specifying plugins in config.plugins #9629 [tom]
-
-* Removed deprecated task names, like clear_logs, in favor of the new namespaced style [David Heinemeier Hansson]
-
-* Support multiple config.after_initialize blocks so plugins and apps can more easily cooperate. #9582 [Zach Dennis]
-
-* Added db:drop:all to drop all databases declared in config/database.yml [David Heinemeier Hansson]
-
-* Use attribute pairs instead of the migration name to create add and remove column migrations. Closes #9166 [Pratik Naik]
-
- For example:
-
- ruby script/generation migration AddSomeStuffToCustomers first_name:string last_name:string
-
- or
-
- ruby script/generation migration RemoveSomeStuffFromCustomers first_name:string last_name:string
-
-* Add ActiveResource to Rails::Info. Closes #8741 [Chris Kampmeier]
-
-* use Gem.find_name instead of search when freezing gems. Prevent false positives for other gems with rails in the name. Closes #8729 [wselman]
-
-* Automatically generate add/remove column commands in specially named migrations like AddLocationToEvent. Closes #9006 [Ryan Davis]
-
-* Default to plural table name in Rails Generator if ActiveRecord is not present. Closes #8963 [evan]
-
-* Added rake routes for listing all the defined routes in the system. #8795 [Josh Peek]
-
-* db:create creates the database for the current environment if it's on localhost. db:create:all creates local databases for all environments. #8783 [matt]
-
-* Generators: look for generators in all gems, not just those suffixed with _generator, in the gem's generators or rails_generators directory. Allow use of the rails_generators directory instead of the standard generators directory in plugins also. #8730 [Dr Nic, topfunky]
-
-* MySQL, PostgreSQL: database.yml defaults to utf-8. #8701 [matt]
-
-* Added db:version to get the current schema number [via Err The Blog]
-
-* Added --skip-migration option to scaffold and resource generators #8656 [Michael Glaesemann]
-
-* Fix that FCGIs would leave log files open when asked to shut down by USR2. #3028 [Sebastian Kanthak, Josh Peek]
-
-* Fixed that dispatcher preparation callbacks only run once in production mode. Mock Routes.reload so that dispatcher preparation callback tests run. [Rick Olson]
-
-* Fix syntax error in dispatcher than wrecked failsafe responses. #8625 [mtitorenko]
-
-* Scaffolded validation errors set the appropriate HTTP status for XML responses. #6946, #8622 [Manfred Stienstra, mmmultiworks]
-
-* Sexy migrations for the session_migration generator. #8561 [Vladislav]
-
-* Console reload! runs to_prepare callbacks also. #8393 [f.svehla]
-
-* Generated migrations include timestamps by default. #8501 [Shane Vitarana]
-
-* Drop Action Web Service from rails:freeze:edge. [Jeremy Kemper]
-
-* Add db:create, drop, reset, charset, and collation tasks. #8448 [matt]
-
-* Scaffold generator depends on model generator instead of duplicating it. #7222 [bscofield]
-
-* Scaffold generator tests. #8443 [pelle]
-
-* Generated scaffold functional tests use assert_difference. #8421 [Norbert Crombach]
-
-* Update to Prototype 1.5.1. [Sam Stephenson]
-
-* Update to script.aculo.us 1.7.1_beta3. [Thomas Fuchs]
-
-* Generators use *.html.erb view template naming. #8278 [Tim Pope]
-
-* Updated resource_scaffold and model generators to use short-hand style migrations [David Heinemeier Hansson]
-
-* Updated initializer to only load #{RAILS_ENV}.rb once. Added deprecation warning for config.breakpoint_server. [Nicholas Seckar]
-
-* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [David Heinemeier Hansson]
-
- To use the new debugger, start your server with script/server --debugger and insert a call to 'debugger'
- (instead of 'breakpoint') where you want to jump into the debugger.
-
- BACKWARDS INCOMPATIBILITY NOTE: You must remove the default line 12 from config/environments/development.rb:
-
- config.breakpoint_server = true
-
- This configuration option is no longer available. Rails will fail to start in development mode as long as
- that's still present.
-
-* Resource scaffolding returns the created entity.to_xml. [Jeremy Kemper]
-
-* Resource scaffolding responds to new.xml. #8185 [Eric Mill]
-
-* Include Active Resource in rails:freeze:edge rake task. [Thomas Fuchs]
-
-* Include Active Resource instead of Action Web Service [David Heinemeier Hansson] You can add AWS back with this in config/environment.rb:
-
- config.load_paths += %W( #{RAILS_ROOT}/vendor/rails/actionwebservice/lib )
-
- ...or just gem 'actionwebservice'
-
-* Give generate scaffold a more descriptive database message. Closes #7316 [Jeremy McAnally]
-
-* Canonicalize RAILS_ROOT by using File.expand_path on Windows, which doesn't have to worry about symlinks, and Pathname#realpath elsewhere, which respects symlinks in relative paths but is incompatible with Windows. #6755 [Jeremy Kemper, trevor]
-
-* Deprecation: remove components from controller paths. [Jeremy Kemper]
-
-* Add environment variable RAILS_DEFAULT_DATABASE, which allows the builtin default of 'mysql' to be overridden. [Nicholas Seckar]
-
-* Windows: include MinGW in RUBY_PLATFORM check. #2982 [okkez000@gmail.com, Kaspar Schiess]
-
-* Split out the basic plugin locator functionality into an abstract super class. Add a FileSystemLocator to do the job of checking the plugin_paths for plugins. Add plugin_locators configuration option which will iterate over the set of plugin locators and load each of the plugin loaders they return. Rename locater everywhere to locator. [Marcel Molina Jr.]
-
-* Split plugin location and loading out of the initializer and into a new Plugin namespace, which includes Plugin::Locater and Plugin::Loader. The loader class that is used can be customized using the config.plugin_loader option. Those monkey patching the plugin loading subsystem take note, the internals changing here will likely break your modifications. The good news is that it should be substantially easier to hook into the plugin locating and loading process now. [Marcel Molina Jr.]
-
-* Added assumption that all plugin creators desire to be sharing individuals and release their work under the MIT license [David Heinemeier Hansson]
-
-* Added source-annotations extractor tasks to rake [Jamis Buck]. This allows you to add FIXME, OPTIMIZE, and TODO comments to your source code that can then be extracted in concert with rake notes (shows all), rake notes:fixme, rake notes:optimize and rake notes:todo.
-
-* Added fixtures :all to test_helper.rb to assume that most people just want all their fixtures loaded all the time [David Heinemeier Hansson]
-
-* Added config/initializers where all ruby files within it are automatically loaded after the Rails configuration is done, so you don't have to litter the environment.rb file with a ton of mixed stuff [David Heinemeier Hansson]
-
-* For new apps, generate a random secret for the cookie-based session store. [Jeremy Kemper]
-
-* Stop swallowing errors during rake test [Michael Koziarski]
-
-* Update Rails Initializer to use ActionController::Base#view_paths [Rick Olson]
-
-* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler]
-
-* Plugins may be symlinked in vendor/plugins. #4245 [brandon, progrium@gmail.com]
-
-* Resource generator depends on the model generator rather than duplicating it. #7269 [bscofield]
-
-* Add/Update usage documentation for script/destroy, resource generator and scaffold_resource generator. Closes #7092, #7271, #7267. [bscofield]
-
-* Update to script.aculo.us 1.7.0. [Thomas Fuchs]
-
-* Update to Prototype 1.5.0. [Sam Stephenson]
-
-* Generator: use destination path for diff tempfiles. #7015 [alfeld]
-
-* Fixed that webrick would strip leading newlines and hang connection #4156 [psross]
-
-* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick Olson]
- If you really want your plugins to reload, add this to the very top of init.rb:
-
- Dependencies.load_once_paths.delete(lib_path)
-
-* Allow config.to_prepare to work, make the dispatcher safe to 're require'. [Michael Koziarski, Nicholas Seckar]
-
-* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 [fearoffish]
-
-* Fix Webrick Daemon dispatching bug regarding a bad current working directory. Closes #4899 [Rick Olson]
-
-* Make config.plugins affect the load path and the dependencies system. Allows you to control plugin loading order, and keep disabled plugins off the load path. [James Adam]
-
-* Don't generate a components directory in new Rails apps. [Jeremy Kemper]
-
-* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [David Heinemeier Hansson]
-
-* Added one-letter aliases for the three default environments to script/console, so script/console p will load the production environment (t for test, d for development) [David Heinemeier Hansson]
-
-* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [David Heinemeier Hansson]
-
-* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar]
-
-* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [David Heinemeier Hansson]
-
-* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [David Heinemeier Hansson]
-
-* Update to Prototype and script.aculo.us [5579]. [Thomas Fuchs]
-
-* Made script/server work with -e and -d when using Mongrel [David Heinemeier Hansson]
-
-* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again [Thomas Fuchs]
-
-* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Lütke]
-
-* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 [fearoffish]
-
-* Update to Prototype 1.5.0_rc2. [Sam Stephenson]
-
-* Add grep-based fallback to reaper, to work in pidless setups [Jamis Buck]
-
-* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished. [Jeremy Kemper]
-
-* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. [Nicholas Seckar]
-
-* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT. #6584 [jchris]
-
-* Remove temporary crutch to help ApplicationController be unloaded. Closes #6496. [Nicholas Seckar]
-
-* scaffold_resource generator uses _path named routes and head instead of render :nothing => true. #6545 [Josh Susser]
-
-* Generator can show diff on file collision to help you decide whether to skip or overwrite. #6364 [jeffw, Jeremy Kemper]
-
-* Generated directories are recursively svn added, like mkdir -p. #6416 [NeilW]
-
-* resource and scaffold_resource generators add a restful route to config/routes.rb [Jeremy Kemper]
-
-* Revert environment changes for autoload_paths. [Michael Koziarski]
-
-* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs]
-
-* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Michael Koziarski]
-
-* Added option to script/process/spawner of specifying the binding address #5133 [Dee Zsombor]
-
-* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio]
-
-* Update scaffold to use new form_tag block functionality. Closes #6480. [Bob Silva]
-
-* Plugin generator: check for class collisions. #4833 [vinbarnes@gmail.com]
-
-* Mailer generator: handle mailers in modules, set mime_version in unit test. [Jeremy Kemper]
-
-* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Michael Koziarski]
-
-* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
-
- config.plugins = %w[ routing_navigator simply_helpful ]
-
-* Clean up html on included error pages. [Tim Lucas]
-
-* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [David Heinemeier Hansson]
-
-* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
-
-* PostgreSQL: db:test:purge closes open database connections first. #6236 [alex]
-
-* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul@paulbutcher.com]
-
-* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [Bob Silva]
-
-* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon@opensoul.org]
-
-* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [David Heinemeier Hansson]
-
-* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples:
-
- ./script/generate model post title:string created_on:date body:text published:boolean
-
-* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [David Heinemeier Hansson]
-
-* script/runner can run files, pass on arguments, and be used as a shebang. #6286 [Tuxie, dlpond]
- #!/usr/bin/env /path/to/my/app/script/runner
- # Example: just start using your models as if you are in script/console
- Product.find(:all).each { |product| product.check_inventory }
-
-* Look for rake tasks in plugin subdirs. #6259 [obrie]
-
-* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [David Heinemeier Hansson]
-
-* Updated prototype.js to 1.5.0_rc1 with latest fixes. [Rick Olson]
-
- - XPATH support
- - Make Form.getElements() return elements in the correct order
- - fix broken Form.serialize return
-
-* session_migration generator adds an index on updated_at. #6207 [grg]
-
-* script/server creates the tmp/pids directory. #6204 [jonathan]
-
-* Fix script/console --sandbox for internal transactions changes. #5738 [Chris McGrath, charles.gerungan@gmail.com]
-
-* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
-documented in initializer.rb, the workaround is:
-
- config.autoload_paths += Dir[RAILS_ROOT + '/app/models/*/']
-
-References #6031. [Nicholas Seckar]
-
-* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
-
-* Update to Prototype 1.5.0_rc1 [sam]
-
-* Formally Deprecate the old rake tasks. [Michael Koziarski]
-
-* Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark]
-
-* Remove Dir.chdir in the Webrick DispatchServlet#initialize method. Fix bad path errors when trying to load config/routes.rb. [Rick Olson]
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Cleaning up tests. [Kevin Clark, Jeremy Kemper]
-
-* Add Dependencies.load_once_paths. [Nicholas Seckar]
-
-* Updated to script.aculo.us 1.6.2 [Thomas Fuchs]
-
-* Assign Routing.controller_paths; fix script/about and rails info controller. [Nicholas Seckar]
-
-* Don't warn dispatcher of Reloadable deprecations. [Nicholas Seckar]
-
-* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base [Rick Olson]
-
- ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed.
- ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback.
-
-* Add missing mock directories from the autoload_paths configuration. [Rick Olson]
-
-* Nested controller scaffolding also nests the generated layout. [iain d broadfoot]
-
-* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. [Nicholas Seckar]
-
-* Add autoload_paths support to Initializer. [Nicholas Seckar]
-
-* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. [Rick Olson]
-
-* Clarify usage of script/plugin source. Closes #5344. [James Adam]
-
-* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar]
-
-* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279. [Rick Olson]
-
-* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats@imediatec.co.uk]
-
-* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [Shugo Maeda]
-
-* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. [Rick Olson]
-
-* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. [Jamis Buck]
-
-* Fix script/plugin about so it uses about.yml and not meta.yml. [James Adam]
-
-* Dispatcher processes rescued actions with the same controller that processed the request. #4625 [sd@notso.net]
-
-* rails -d frontbase to create a new project with a frontbase database.yml. #4945 [mlaster@metavillage.com]
-
-* Ensure the logger is initialized. #5629 [mike@clarkware.com]
-
-* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples:
-
- spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available
- spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI
- spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel
- spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available
- spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
-
- Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster.
-
-* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 [evan@protest.net]
-
-* db:test:clone should remove existing tables before reloading the schema. #5607 [sveit@tradeharbor.com]
-
-* Fixed migration generation for class names like ACLController #5197 [brad@madriska.com]
-
-* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi@drecom.co.jp]. Examples:
-
- irb(#<TopController:0x40822a68>):002:0> show_source_list
- 0001 class TopController < ApplicationController
- 0002 def show
- 0003-> breakpoint
- 0004 end
- 0005
- 0006 def index
- 0007 end
- 0008
- => "/path/to/rails/root/app/controllers/top_controller.rb"
-
- irb(#<TopController:0x40822a68>):004:0> show_call_stack 3
- vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
- vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
- app/controllers/top_controller.rb:3:in `show'
- => "/path/to/rails/root/app/controllers/top_controller.rb:3"
-
-* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 [nils@alumni.rice.edu]
-
-* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 [mike@clarkware.com]
-
-* Don't assume Active Record is available. #5497 [bob@sporkmonger.com]
-
-* Mongrel: script/server works on Win32. #5499 [jeremydurham@gmail.com]
-
-* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [Nick Sieger]
-
-* Mongrel support for script/server. #5475 [jeremydurham@gmail.com]
-
-* Fix script/plugin so it doesn't barf on invalid URLs [Rick Olson]
-
-* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA]
-
-* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick Olson]
-
-* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi@drecom.co.jp]
-
-* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 [elliot@townx.org]
-
-* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. [Jamis Buck]
-
-* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson]
-
-* Added lib/ to the directories that will get application docs generated [David Heinemeier Hansson]
-
-* Add observer generator. Closes #5167. [François Beausoleil]
-
-* Session migration generator obeys pluralize_table_names. #5145 [James Adam]
-
-* rake test:recent understands subdirectories. #2925 [jerrett@bravenet.com]
-
-* The app generator detects the XAMPP package's MySQL socket location. #3832 [elliot@townx.org]
-
-* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug@owl.me.uk]
-
-* Distinguish the spawners for different processes [David Heinemeier Hansson]
-
-* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [David Heinemeier Hansson]
-
-* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov]
-
-* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-
-* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [David Heinemeier Hansson]
-
-* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [David Heinemeier Hansson]
-
-
-*1.2.3* (March 12th, 2007)
-
-* Ruby 1.8.6 compatibility
-
-* Windows: include MinGW in RUBY_PLATFORM check. #2982 [okkez000@gmail.com, Kaspar Schiess]
-
-* Stop swallowing errors during rake test [Michael Koziarski]
-
-
-*1.2.2* (February 5th, 2007)
-
-* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler]
-
-* Require the dispatcher for Rails::Configuration#to_prepare. [Rick Olson]
-
-
-*1.2.1* (January 16th, 2007)
-
-* Updated to Active Record 1.15.1, Action Pack 1.13.1, Action Mailer 1.3.1, Action Web Service 1.2.1
-
-
-*1.2.0* (January 16th, 2007)
-
-* Update to Prototype 1.5.0. [Sam Stephenson]
-
-* Generator: use destination path for diff tempfiles. #7015 [alfeld]
-
-* Fixed that webrick would strip leading newlines and hang connection #4156 [psross]
-
-* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick Olson]
- If you really want your plugins to reload, add this to the very top of init.rb:
-
- Dependencies.load_once_paths.delete(lib_path)
-
-* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 [fearoffish]
-
-* Fix Webrick Daemon dispatching bug regarding a bad current working directory. Closes #4899 [Rick Olson]
-
-* Make config.plugins affect the load path and the dependencies system. Allows you to control plugin loading order, and keep disabled plugins off the load path. [James Adam]
-
-* Don't generate a components directory in new Rails apps. [Jeremy Kemper]
-
-* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [David Heinemeier Hansson]
-
-* Deprecated the name route "root" as it'll be used as a shortcut for map.connect '' in Rails 2.0 [David Heinemeier Hansson]
-
-* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [David Heinemeier Hansson]
-
-* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar]
-
-* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [David Heinemeier Hansson]
-
-* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [David Heinemeier Hansson]
-
-* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs]
-
-* Made script/server work with -e and -d when using Mongrel [David Heinemeier Hansson]
-
-* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Lütke]
-
-* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 [fearoffish]
-
-* Add grep-based fallback to reaper, to work in pidless setups [Jamis Buck]
-
-* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished. [Jeremy Kemper]
-
-* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. [Nicholas Seckar]
-
-* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT. #6584 [jchris]
-
-* scaffold_resource generator uses _path named routes and head instead of render :nothing => true. #6545 [Josh Susser]
-
-* Generator can show diff on file collision to help you decide whether to skip or overwrite. #6364 [jeffw, Jeremy Kemper]
-
-* Generated directories are recursively svn added, like mkdir -p. #6416 [NeilW]
-
-* resource and scaffold_resource generators add a restful route to config/routes.rb [Jeremy Kemper]
-
-* Revert environment changes for autoload_paths. [Michael Koziarski]
-
-* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Michael Koziarski]
-
-* Added option to script/process/spawner of specifying the binding address #5133 [Dee Zsombor]
-
-* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio]
-
-* Update scaffold to use new form_tag block functionality. Closes #6480. [Bob Silva]
-
-* Plugin generator: check for class collisions. #4833 [vinbarnes@gmail.com]
-
-* Mailer generator: handle mailers in modules, set mime_version in unit test. [Jeremy Kemper]
-
-* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Michael Koziarski]
-
-* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
-
- config.plugins = %w[ routing_navigator simply_helpful ]
-
-* Clean up html on included error pages. [Tim Lucas]
-
-* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [David Heinemeier Hansson]
-
-* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
-
-* PostgreSQL: db:test:purge closes open database connections first. #6236 [alex]
-
-* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul@paulbutcher.com]
-
-* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [Bob Silva]
-
-* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon@opensoul.org]
-
-* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [David Heinemeier Hansson]
-
-* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples:
-
- ./script/generate model post title:string created_on:date body:text published:boolean
-
-* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [David Heinemeier Hansson]
-
-* script/runner can run files, pass on arguments, and be used as a shebang. #6286 [Tuxie, dlpond]
- #!/usr/bin/env /path/to/my/app/script/runner
- # Example: just start using your models as if you are in script/console
- Product.find(:all).each { |product| product.check_inventory }
-
-* Look for rake tasks in plugin subdirs. #6259 [obrie]
-
-* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [David Heinemeier Hansson]
-
-* session_migration generator adds an index on updated_at. #6207 [grg]
-
-* script/server creates the tmp/pids directory. #6204 [jonathan]
-
-* Fix script/console --sandbox for internal transactions changes. #5738 [Chris McGrath, charles.gerungan@gmail.com]
-
-* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
-documented in initializer.rb, the workaround is:
-
- config.autoload_paths += Dir[RAILS_ROOT + '/app/models/*/']
-
-References #6031. [Nicholas Seckar]
-
-* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
-
-* Formally Deprecate the old rake tasks. [Michael Koziarski]
-
-* Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark]
-
-* Remove Dir.chdir in the Webrick DispatchServlet#initialize method. Fix bad path errors when trying to load config/routes.rb. [Rick Olson]
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Cleaning up tests. [Kevin Clark, Jeremy Kemper]
-
-* Add Dependencies.load_once_paths. [Nicholas Seckar]
-
-* Assign Routing.controller_paths; fix script/about and rails info controller. [Nicholas Seckar]
-
-* Don't warn dispatcher of Reloadable deprecations. [Nicholas Seckar]
-
-* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base [Rick Olson]
-
- ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed.
- ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback.
-
-* Add missing mock directories from the autoload_paths configuration. [Rick Olson]
-
-* Nested controller scaffolding also nests the generated layout. [iain d broadfoot]
-
-* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. [Nicholas Seckar]
-
-* Add autoload_paths support to Initializer. [Nicholas Seckar]
-
-* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. [Rick Olson]
-
-* Clarify usage of script/plugin source. Closes #5344. [James Adam]
-
-* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar]
-
-* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279. [Rick Olson]
-
-* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats@imediatec.co.uk]
-
-* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [Shugo Maeda]
-
-* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. [Rick Olson]
-
-* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. [Jamis Buck]
-
-* Fix script/plugin about so it uses about.yml and not meta.yml. [James Adam]
-
-* Dispatcher processes rescued actions with the same controller that processed the request. #4625 [sd@notso.net]
-
-* rails -d frontbase to create a new project with a frontbase database.yml. #4945 [mlaster@metavillage.com]
-
-* Ensure the logger is initialized. #5629 [mike@clarkware.com]
-
-* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples:
-
- spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available
- spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI
- spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel
- spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available
- spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
-
- Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster.
-
-* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 [evan@protest.net]
-
-* db:test:clone should remove existing tables before reloading the schema. #5607 [sveit@tradeharbor.com]
-
-* Fixed migration generation for class names like ACLController #5197 [brad@madriska.com]
-
-* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi@drecom.co.jp]. Examples:
-
- irb(#<TopController:0x40822a68>):002:0> show_source_list
- 0001 class TopController < ApplicationController
- 0002 def show
- 0003-> breakpoint
- 0004 end
- 0005
- 0006 def index
- 0007 end
- 0008
- => "/path/to/rails/root/app/controllers/top_controller.rb"
-
- irb(#<TopController:0x40822a68>):004:0> show_call_stack 3
- vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
- vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
- app/controllers/top_controller.rb:3:in `show'
- => "/path/to/rails/root/app/controllers/top_controller.rb:3"
-
-* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 [nils@alumni.rice.edu]
-
-* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 [mike@clarkware.com]
-
-* Don't assume Active Record is available. #5497 [bob@sporkmonger.com]
-
-* Mongrel: script/server works on Win32. #5499 [jeremydurham@gmail.com]
-
-* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [Nick Sieger]
-
-* Mongrel support for script/server. #5475 [jeremydurham@gmail.com]
-
-* Fix script/plugin so it doesn't barf on invalid URLs [Rick Olson]
-
-* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA]
-
-* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick Olson]
-
-* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi@drecom.co.jp]
-
-* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 [elliot@townx.org]
-
-* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. [Jamis Buck]
-
-* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson]
-
-* Added lib/ to the directories that will get application docs generated [David Heinemeier Hansson]
-
-* Add observer generator. Closes #5167. [François Beausoleil]
-
-* Session migration generator obeys pluralize_table_names. #5145 [James Adam]
-
-* rake test:recent understands subdirectories. #2925 [jerrett@bravenet.com]
-
-* The app generator detects the XAMPP package's MySQL socket location. #3832 [elliot@townx.org]
-
-* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug@owl.me.uk]
-
-* Distinguish the spawners for different processes [David Heinemeier Hansson]
-
-* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [David Heinemeier Hansson]
-
-* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov]
-
-* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-
-* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [David Heinemeier Hansson]
-
-* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [David Heinemeier Hansson]
-
-
-*1.1.6* (August 10th, 2006)
-
-* Additional security patch
-
-
-*1.1.5* (August 8th, 2006)
-
-* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. #4857 [Alisdair McDiarmid]
-
-* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]
-
-* Includes critical security patch
-
-
-*1.1.4* (June 29th, 2006)
-
-* Remove use of opts.on { |options[:name] } style hash assignment. References #4440. [headius@headius.com]
-
-* Updated to Action Pack 1.12.3, ActionWebService 1.1.4, ActionMailer 1.2.3
-
-
-*1.1.3* (June 27th, 2006)
-
-* Updated to Active Record 1.14.3, Action Pack 1.12.2, ActionWebService 1.1.3, ActionMailer 1.2.2
-
-
-*1.1.2* (April 9th, 2006)
-
-* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. Closes #4857. [Alisdair McDiarmid]
-
-* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]
-
-* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [David Heinemeier Hansson]
-
-* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [David Heinemeier Hansson]
-
-
-*1.1.1* (April 6th, 2006)
-
-* Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) [ruben.nine@gmail.com]
-
-* Update to Prototype 1.5.0_rc0 [Sam Stephenson]
-
-* Fixed that the -r/--ruby path option of the rails command was not being respected #4549 [ryan.raaum@gmail.com]
-
-* Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html [David Heinemeier Hansson]
-
-* Fixed that rake clone_structure_to_test should quit on pgsql if the dump is unsuccesful #4585 [augustz@augustz.com]
-
-* Fixed that rails --version should have the return code of 0 (success) #4560 [blair@orcaware.com]
-
-* Install alias so Rails::InfoController is accessible at /rails_info. Closes #4546. [Nicholas Seckar]
-
-* Fixed that spawner should daemonize if running in repeat mode [David Heinemeier Hansson]
-
-* Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release [David Heinemeier Hansson]
-
-* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
-
-* Use --simple-prompt instead of --prompt-mode simple for console compatibility with Windows/Ruby 1.8.2 #4532 [starr@starrnhorne.com]
-
-* Make Rails::VERSION implicitly loadable #4491. [Nicholas Seckar]
-
-* Fixed rake rails:freeze:gems #4518 [benji@silverinsanity.com]
-
-* Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with [David Heinemeier Hansson]
-
-* Added gem binding of apps generated through the rails command to the gems of they were generated with [Nicholas Seckar]
-
-* Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf [David Heinemeier Hansson]
-
-* Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf [David Heinemeier Hansson]
-
-* Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar]
-
-
-*1.1.0* (March 27th, 2006)
-
-* Allow db:fixtures:load to load a subset of the applications fixtures. [Chad Fowler]
-
- ex.
-
- rake db:fixtures:load FIXTURES=customers,plans
-
-* Update to Prototype 1.5.0_pre1 [Sam Stephenson]
-
-* Update to script.aculo.us 1.6 [Thomas Fuchs]
-
-* Add an integration_test generator [Jamis Buck]
-
-* Make all ActionView helpers available in the console from the helper method for debugging purposes. n.b.: Only an 80% solution. Some stuff won't work, most will. [Marcel Molina Jr.]
-
- ex.
-
- >> puts helper.options_for_select([%w(a 1), %w(b 2), %w(c 3)])
- <option value="1">a</option>
- <option value="2">b</option>
- <option value="3">c</option>
- => nil
-
-* Replaced old session rake tasks with db:sessions:create to generate a migration, and db:sessions:clear to remove sessions. [Rick Olson]
-
-* Reject Ruby 1.8.3 when loading Rails; extract version checking code. [Chad Fowler]
-
-* Remove explicit loading of RailsInfo and RailsInfoController. [Nicholas Seckar]
-
-* Move RailsInfo and RailsInfoController to Rails::Info and Rails::InfoController. [Nicholas Seckar]
-
-* Extend load path with Railties' builtin directory to make adding support code easy. [Nicholas Seckar]
-
-* Fix the rails_info controller by explicitly loading it, and marking it as not reloadable. [Nicholas Seckar]
-
-* Fixed rails:freeze:gems for Windows #3274 [paul@paulbutcher.com]
-
-* Added 'port open?' check to the spawner when running in repeat mode so we don't needlessly boot the dispatcher if the port is already in use anyway #4089 [guy.naor@famundo.com]
-
-* Add verification to generated scaffolds, don't allow get for unsafe actions [Michael Koziarski]
-
-* Don't replace application.js in public/javascripts if it already exists [Cody Fauser]
-
-* Change test:uncommitted to delay execution of `svn status` by using internal Rake API's. [Nicholas Seckar]
-
-* Use require_library_or_gem to load rake in commands/server.rb. Closes #4205. [rob.rasmussen@gmail.com]
-
-* Use the Rake API instead of shelling out to create the tmp directory in commands/server.rb. [Chad Fowler]
-
-* Added a backtrace to the evil WSOD (White Screen of Death). Closes #4073. TODO: Clearer exceptions [Rick Olson]
-
-* Added tracking of database and framework versions in script/about #4088 [charles.gerungan@gmail.com/Rick Olson]
-
-* Added public/javascripts/application.js as a sample since it'll automatically be included in javascript_include_tag :defaults [David Heinemeier Hansson]
-
-* Added socket cleanup for lighttpd, both before and after [David Heinemeier Hansson]
-
-* Added automatic creation of tmp/ when running script/server [David Heinemeier Hansson]
-
-* Added silence_stream that'll work on both STDERR or STDOUT or any other stream and deprecated silence_stderr in the process [David Heinemeier Hansson]
-
-* Added reload! method to script/console to reload all models and others that include Reloadable without quitting the console #4056 [esad@esse.at]
-
-* Added that rake rails:freeze:edge will now just export all the contents of the frameworks instead of just lib, so stuff like rails:update:scripts, rails:update:javascripts, and script/server on lighttpd still just works #4047 [David Heinemeier Hansson]
-
-* Added fix for upload problems with lighttpd from Safari/IE to config/lighttpd.conf #3999 [Thijs van der Vossen]
-
-* Added test:uncommitted to test changes since last checkin to Subversion #4035 [technomancy@gmail.com]
-
-* Help script/about print the correct svn revision when in a non-English locale. #4026 [babie7a0@ybb.ne.jp]
-
-* Add 'app' accessor to script/console as an instance of Integration::Session [Jamis Buck]
-
-* Generator::Base#usage takes an optional message argument which defaults to Generator::Base#usage_message. [Jeremy Kemper]
-
-* Remove the extraneous AR::Base.threaded_connections setting from the webrick server. [Jeremy Kemper]
-
-* Add integration test support to app generation and testing [Jamis Buck]
-
-* Added namespaces to all tasks, so for example load_fixtures is now db:fixtures:load. All the old task names are still valid, they just point to the new namespaced names. "rake -T" will only show the namespaced ones, though [David Heinemeier Hansson]
-
-* CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by default instead of :sql. This means that we'll assume you want to live in the world of db/schema.rb where the grass is green and the girls are pretty. If your schema contains un-dumpable elements, such as constraints or database-specific column types, you just got an invitation to either 1) patch the dumper to include foreign key support, 2) stop being db specific, or 3) just change the default in config/environment.rb to config.active_record.schema_format = :sql -- we even include an example for that on new Rails skeletons now. Brought to you by the federation of opinionated framework builders! [David Heinemeier Hansson]
-
-* Added -r/--repeat option to script/process/spawner that offers the same loop protection as the spinner did. This deprecates the script/process/spinner, so it's no longer included in the default Rails skeleton, but still available for backwards compatibility #3461 [ror@andreas-s.net]
-
-* Added collision option to template generation in generators #3329 [anna@wota.jp]. Examples:
-
- m.template "stuff.config" , "config/stuff.config" , :collision => :skip
- m.template "auto-stamping", "config/generator.log", :collision => :force
-
-* Added more information to script/plugin's doings to ease debugging #3755 [Rick Olson]
-
-* Changed the default configuration for lighttpd to use tmp/sockets instead of log/ for the FastCGI sockets [David Heinemeier Hansson]
-
-* Added a default configuration of the FileStore for fragment caching if tmp/cache is available, which makes action/fragment caching ready to use out of the box with no additional configuration [David Heinemeier Hansson]
-
-* Changed the default session configuration to place sessions in tmp/sessions, if that directory is available, instead of /tmp (this essentially means a goodbye to 9/10 White Screen of Death errors and should have web hosting firms around the world cheering) [David Heinemeier Hansson]
-
-* Added tmp/sessions, tmp/cache, and tmp/sockets as default directories in the Rails skeleton [David Heinemeier Hansson]
-
-* Added that script/generate model will now automatically create a migration file for the model created. This can be turned off by calling the generator with --skip-migration [David Heinemeier Hansson]
-
-* Added -d/--database option to the rails command, so you can do "rails --database=sqlite2 myapp" to start a new application preconfigured to use SQLite2 as the database. Removed the configuration examples from SQLite and PostgreSQL from the default MySQL configuration [David Heinemeier Hansson]
-
-* Allow script/server -c /path/to/lighttpd.conf [Jeremy Kemper]
-
-* Remove hardcoded path to reaper script in script/server [Jeremy Kemper]
-
-* Update script.aculo.us to V1.5.3 [Thomas Fuchs]
-
-* Added SIGTRAP signal handler to RailsFCGIHandler that'll force the process into a breakpoint after the next request. This breakpoint can then be caught with script/breakpointer and give you access to the Ruby image inside that process. Useful for debugging memory leaks among other things [David Heinemeier Hansson]
-
-* Changed default lighttpd.conf to use CWD from lighttpd 1.4.10 that allows the same configuration to be used for both detach and not. Also ensured that auto-repeaping of FCGIs only happens when lighttpd is not detached. [David Heinemeier Hansson]
-
-* Added Configuration#after_initialize for registering a block which gets called after the framework is fully initialized. Useful for things like per-environment configuration of plugins. [Michael Koziarski]
-
-* Added check for RAILS_FRAMEWORK_ROOT constant that allows the Rails framework to be found in a different place than vendor/rails. Should be set in boot.rb. [David Heinemeier Hansson]
-
-* Fixed that static requests could unlock the mutex guarding dynamic requests in the WEBrick servlet #3433 [tom@craz8.com]
-
-* Fixed documentation tasks to work with Rake 0.7.0 #3563 [kazuhiko@fdiary.net]
-
-* Update to Prototype 1.5.0_pre0 [Sam Stephenson]
-
-* Sort the list of plugins so we load in a consistent order [Rick Olson]
-
-* Show usage when script/plugin is called without arguments [tom@craz8.com]
-
-* Corrected problems with plugin loader where plugins set 'name' incorrectly #3297 [anna@wota.jp]
-
-* Make migration generator only report on exact duplicate names, not partial dupliate names. #3442 [jeremy@planetargon.com Marcel Molina Jr.]
-
-* Fix typo in mailer generator USAGE. #3458 [chriztian.steinmeier@gmail.com]
-
-* Ignore version mismatch between pg_dump and the database server. #3457 [Simon Stapleton]
-
-* Reap FCGI processes after lighttpd exits. [Sam Stephenson]
-
-* Honor ActiveRecord::Base.pluralize_table_names when creating and destroying session store table. #3204. [rails@bencurtis.com, Marcel Molina Jr.]
-
-*1.0.0* (December 13th, 2005)
-
-* Update instructions on how to find and install generators. #3172. [Chad Fowler]
-
-* Generator looks in vendor/generators also. [Chad Fowler]
-
-* Generator copies files in binary mode. #3156 [minimudboy@gmail.com]
-
-* Add builtin/ to the gemspec. Closes #3047. [Nicholas Seckar, Sam Stephenson]
-
-* Add install.rb file to plugin generation which is loaded, if it exists, when you install a plugin. [Marcel Molina Jr.]
-
-* Run initialize_logger in script/lighttpd to ensure the log file exists before tailing it. [Sam Stephenson]
-
-* Make load_fixtures include csv fixtures. #3053. [me@mdaines.com]
-
-* Fix freeze_gems so that the latest rails version is dumped by default. [Nicholas Seckar]
-
-* script/plugin: handle root paths and plugin names which contain spaces. #2995 [justin@aspect.net]
-
-* Model generator: correct relative path to test_helper in unit test. [Jeremy Kemper]
-
-* Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. #2931. [Blair Zajac]
-
-* Have the lighttpd server script report the actual ip to which the server is bound. #2903. [Adam]
-
-* Add plugin library directories to the load path after the lib directory so that libraries in the lib directory get precedence. #2910. [James Adam]
-
-* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. [anonymous]
-
-* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 [Dreamer3]
-
-* Introducing the session_migration generator. Creates an add_session_table migration. Allows generator to specify migrations directory. #2958, #2960 [Rick Olson]
-
-* script/console uses RAILS_ENV environment variable if present. #2932 [Blair Zajac <blair@orcaware.com>
-
-* Windows: eliminate the socket option in database.yml. #2924 [Wayne Vucenic <waynev@gmail.com>]
-
-* Eliminate nil from newly generated logfiles. #2927 [Blair Zajac <blair@orcaware.com>]
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-* Eliminate Subversion dependencies in scripts/plugin. Correct install options. Introduce --force option to reinstall a plugin. Remove useless --long option for list. Use --quiet to quiet the download output and --revision to update to a specific svn revision. #2842 [Chad Fowler, Rick Olson]
-
-* SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment. #2846 [Rick Olson]
-
-* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko@fdiary.net]
-
-* Pass __FILE__ when evaluating plugins' init.rb. #2817 [James Adam]
-
-* Better svn status matching for generators. #2814 [François Beausoleil <francois.beausoleil@gmail.com>, Blair Zajac <blair@orcaware.com>]
-
-* Don't reload routes until plugins have been loaded so they have a chance to extend the routing capabilities [David Heinemeier Hansson]
-
-* Don't detach or fork for script/server tailing [Nicholas Seckar]
-
-* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path [David Heinemeier Hansson]
-
-* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [David Heinemeier Hansson] #2660
-
-* Correct versioning in :freeze_gems Rake task. #2778 [Jakob Skjerning, Jeremy Kemper]
-
-* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. [Sam Stephenson]
-
-
-*0.14.3 (RC4)* (November 7th, 2005)
-
-* Add 'add_new_scripts' rake task for adding new rails scripts to script/* [Jamis Buck]
-
-* Remove bogus hyphen from script/process/reaper calls to 'ps'. #2767 [anonymous]
-
-* Copy lighttpd.conf when it is first needed, instead of on app creation [Jamis Buck]
-
-* Use require_library_or_gem 'fcgi' in script/server [Sam Stephenson]
-
-* Added default lighttpd config in config/lighttpd.conf and added a default runner for lighttpd in script/server (works like script/server, but using lighttpd and FastCGI). It will use lighttpd if available, otherwise WEBrick. You can force either or using 'script/server lighttpd' or 'script/server webrick' [David Heinemeier Hansson]
-
-* New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins']. [Jeremy Kemper]
-
-* Plugins are discovered in nested paths, so you can organize your plugins directory as you like. [Jeremy Kemper]
-
-* Refactor load_plugin from load_plugins. #2757 [alex.r.moon@gmail.com]
-
-* Make use of silence_stderr in script/lighttpd, script/plugin, and Rails::Info [Sam Stephenson]
-
-* Enable HTTP installation of plugins when svn isn't avaialable. Closes #2661. [Chad Fowler]
-
-* Added script/about to display formatted Rails::Info output [Sam Stephenson]
-
-* Added Rails::Info to catalog assorted information about a Rails application's environment [Sam Stephenson]
-
-* Tail the logfile when running script/server lighttpd in the foreground [Sam Stephenson]
-
-* Try to guess the port number from config/lighttpd.conf in script/server lighttpd [Sam Stephenson]
-
-* Don't reap spawn-fcgi. #2727 [matthew@walker.wattle.id.au]
-
-* Reaper knows how to find processes even if the dispatch path is very long. #2711 [matthew@walker.wattle.id.au]
-
-* Make fcgi handler respond to TERM signals with an explicit exit [Jamis Buck]
-
-* Added demonstration of fixture use to the test case generated by the model generator [David Heinemeier Hansson]
-
-* If specified, pass PostgreSQL client character encoding to createdb. #2703 [Kazuhiko <kazuhiko@fdiary.net>]
-
-* Catch CGI multipart parse errors. Wrap dispatcher internals in a failsafe response handler. [Jeremy Kemper]
-
-* The freeze_gems Rake task accepts the VERSION environment variable to decide which version of Rails to pull into vendor/rails. [Chad Fowler, Jeremy Kemper]
-
-* Removed script.aculo.us.js, builder.js and slider.js (preperation for move of scriptaculous extensions to plugins, core scriptaculous will remain in Railties) [Thomas Fuchs]
-
-* The freeze_edge Rake task does smarter svn detection and can export a specific revision by passing the REVISION environment variable. For example: rake freeze_edge REVISION=1234. #2663 [Rick Olson]
-
-* Comment database.yml and include PostgreSQL and SQLite examples. [Jeremy Kemper]
-
-* Improve script/plugin on Windows. #2646 [Chad Fowler]
-
-* The *_plugindoc Rake tasks look deeper into the plugins' lib directories. #2652 [bellis@deepthought.org]
-
-* The PostgreSQL :db_structure_dump Rake task limits its dump to the schema search path in database.yml. [Anatol Pomozov <anatol.pomozov@gmail.com>]
-
-* Add task to generate rdoc for all installed plugins. [Marcel Molina Jr.]
-
-* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs]
-
-* Add default Mac + DarwinPorts MySQL socket locations to the app generator. [Jeremy Kemper]
-
-* Migrations may be destroyed: script/destroy migration foo. #2635 [Charles M. Gerungan <charles.gerungan@gmail.com>, Jamis Buck, Jeremy Kemper]
-
-* Added that plugins can carry generators and that generator stub files can be created along with new plugins using script/generate plugin <name> --with-generator [David Heinemeier Hansson]
-
-* Removed app/apis as a default empty dir since its automatically created when using script/generate web_service [David Heinemeier Hansson]
-
-* Added script/plugin to manage plugins (install, remove, list, etc) [Ryan Tomayko]
-
-* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) [David Heinemeier Hansson]
-
-* Added plugin generator to create a stub structure for a new plugin in vendor/plugins (see "script/generate plugin" for help) [David Heinemeier Hansson]
-
-* Fixed scaffold generator when started with only 1 parameter #2609 [self@mattmower.com]
-
-* rake should run functional tests even if the unit tests have failures [Jim Weirich]
-
-* Back off cleanpath to be symlink friendly. Closes #2533 [Nicholas Seckar]
-
-* Load rake task files in alphabetical order so you can build dependencies and count on them #2554 [Blair Zajac]
-
-
-*0.14.2 (RC3)* (October 26th, 2005)
-
-* Constants set in the development/test/production environment file are set in Object
-
-* Scaffold generator pays attention to the controller name. #2562 [self@mattmower.com]
-
-* Include tasks from vendor/plugins/*/tasks in the Rakefile #2545 [Rick Olson]
-
-
-*0.14.1 (RC2)* (October 19th, 2005)
-
-* Don't clean RAILS_ROOT on windows
-
-* Remove trailing '/' from RAILS_ROOT [Nicholas Seckar]
-
-* Upgraded to Active Record 1.12.1 and Action Pack 1.10.1
-
-
-*0.14.0 (RC1)* (October 16th, 2005)
-
-* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators [Tobias Lütke]
-
-* Fix rake dev and related commands [Nicholas Seckar]
-
-* The rails command tries to deduce your MySQL socket by running `mysql_config
---socket`. If it fails, default to /path/to/your/mysql.sock
-
-* Made the rails command use the application name for database names in the tailored database.yml file. Example: "rails ~/projects/blog" will use "blog_development" instead of "rails_development". [Florian Weber]
-
-* Added Rails framework freezing tasks: freeze_gems (freeze to current gems), freeze_edge (freeze to Rails SVN trunk), unfreeze_rails (float with newest gems on system)
-
-* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. [Tobias Lütke]
-
-* Added cleaning of RAILS_ROOT to useless elements such as '../non-dot-dot/'. Provides cleaner backtraces and error messages. [Nicholas Seckar]
-
-* Made the instantiated/transactional fixtures settings be controlled through Rails::Initializer. Transactional and non-instantiated fixtures are default from now on. [Florian Weber]
-
-* Support using different database adapters for development and test with ActiveRecord::Base.schema_format = :ruby [Sam Stephenson]
-
-* Make webrick work with session(:off)
-
-* Add --version, -v option to the Rails command. Closes #1840. [stancell]
-
-* Update Prototype to V1.4.0_pre11, script.aculo.us to V1.5_rc3 [2504] and fix the rails generator to include the new .js files [Thomas Fuchs]
-
-* Make the generator skip a file if it already exists and is identical to the new file.
-
-* Add experimental plugin support #2335
-
-* Made Rakefile aware of new .js files in script.aculo.us [Thomas Fuchs]
-
-* Make table_name and controller_name in generators honor AR::Base.pluralize_table_names. #1216 #2213 [kazuhiko@fdiary.net]
-
-* Clearly label functional and unit tests in rake stats output. #2297 [lasse.koskela@gmail.com]
-
-* Make the migration generator only check files ending in *.rb when calculating the next file name #2317 [Chad Fowler]
-
-* Added prevention of duplicate migrations from the generator #2240 [François Beausoleil]
-
-* Add db_schema_dump and db_schema_import rake tasks to work with the new ActiveRecord::SchemaDumper (for dumping a schema to and reading a schema from a ruby file).
-
-* Reformed all the config/environments/* files to conform to the new Rails::Configuration approach. Fully backwards compatible.
-
-* Added create_sessions_table, drop_sessions_table, and purge_sessions_table as rake tasks for databases that supports migrations (MySQL, PostgreSQL, SQLite) to get a table for use with CGI::Session::ActiveRecordStore
-
-* Added dump of schema version to the db_structure_dump task for databases that support migrations #1835 [Rick Olson]
-
-* Fixed script/profiler for Ruby 1.8.2 #1863 [Rick Olson]
-
-* Fixed clone_structure_to_test task for SQLite #1864 [jon@burningbush.us]
-
-* Added -m/--mime-types option to the WEBrick server, so you can specify a Apache-style mime.types file to load #2059 [ask@develooper.com]
-
-* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 [Kevin Clark]
-
-* Added -c/--charset option to WEBrick server, so you can specify a default charset (which without changes is UTF-8) #2084 [wejn@box.cz]
-
-* Make the default stats task extendable by modifying the STATS_DIRECTORIES constant
-
-* Allow the selected environment to define RAILS_DEFAULT_LOGGER, and have Rails::Initializer use it if it exists.
-
-* Moved all the shared tasks from Rakefile into Rails, so that the Rakefile is empty and doesn't require updating.
-
-* Added Rails::Initializer and Rails::Configuration to abstract all of the common setup out of config/environment.rb (uses config/boot.rb to bootstrap the initializer and paths)
-
-* Fixed the scaffold generator to fail right away if the database isn't accessible instead of in mid-air #1169 [Chad Fowler]
-
-* Corrected project-local generator location in scripts.rb #2010 [Michael Schuerig]
-
-* Don't require the environment just to clear the logs #2093 [Scott Barron]
-
-* Make the default rakefile read *.rake files from config/tasks (for easy extension of the rakefile by e.g. generators)
-
-* Only load breakpoint in development mode and when BREAKPOINT_SERVER_PORT is defined.
-
-* Allow the --toggle-spin switch on process/reaper to be negated
-
-* Replace render_partial with render :partial in scaffold generator [Nicholas Seckar]
-
-* Added -w flag to ps in process/reaper #1934 [Scott Barron]
-
-* Allow ERb in the database.yml file (just like with fixtures), so you can pull out the database configuration in environment variables #1822 [Duane Johnson]
-
-* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help.
-
-* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina Jr.]
-
-* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [Michael Schubert]
-
-* Dropped the 'immediate close-down' of FCGI processes since it didn't work consistently and produced bad responses when it didn't. So now a TERM ensures exit after the next request (just as if the process is handling a request when it receives the signal). This means that you'll have to 'nudge' all FCGI processes with a request in order to ensure that they have all reloaded. This can be done by something like ./script/process/repear --nudge 'http://www.myapp.com' --instances 10, which will load the myapp site 10 times (and thus hit all of the 10 FCGI processes once, enough to shut down).
-
-
-*0.13.1* (11 July, 2005)
-
-* Look for app-specific generators in RAILS_ROOT/generators rather than the clunky old RAILS_ROOT/script/generators. Nobody really uses this feature except for the unit tests, so it's a negligible-impact change. If you want to work with third-party generators, drop them in ~/.rails/generators or simply install gems.
-
-* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
-
-* Added support for SQL Server in the database rake tasks #1652 [Ken Barker] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
-
-* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
-
-* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay]
-
-* Improved documentation for environment config files #1625 [court3nay]
-
-
-*0.13.0* (6 July, 2005)
-
-* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
-
-* Added migration generator: ./script/generate migration add_system_settings
-
-* Added "migrate" as rake task to execute all the pending migrations from db/migrate
-
-* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina Jr.]
-
-* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski]
-
-* SIGTERM also gracefully exits dispatch.fcgi. Ignore SIGUSR1 on Windows.
-
-* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi [Stefan Kaes]
-
-* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
-
-* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
-
-* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [Michael Schubert]
-
-* ActionMailer::Base subclasses are reloaded with the other rails components #1262
-
-* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false)
-
-* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron]
-
-* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski]
-
-* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 [Nicholas Seckar]
-
-* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar]
-
-* Added -e/--environment option to script/runner #1408 [François Beausoleil]
-
-* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id]. #1367
-
-* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 [Caleb Tennis]
-
-* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck]
-
-* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck]
-
-* Added console --profile for profiling an IRB session #1154 [Jeremy Kemper]
-
-* Changed console_sandbox into console --sandbox #1154 [Jeremy Kemper]
-
-
-*0.12.1* (20th April, 2005)
-
-* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1
-
-
-*0.12.0* (19th April, 2005)
-
-* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 [rails@cogentdude.com]
-
-* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples:
-
- # runs the one statement 10 times
- script/benchmarker 10 'Person.expensive_method(10)'
-
- # pits the two statements against each other with 50 runs each
- script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)'
-
-* Added script/profiler to easily profile a single statement from within the environment. Examples:
-
- script/profiler 'Person.expensive_method(10)'
- script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times
-
-* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 [Lucas Carlson]
-
-* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k@v2studio.com]
-
-* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson]
-
-* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
-
-
-*0.11.1* (27th March, 2005)
-
-* Fixed the dispatch.fcgi use of a logger
-
-* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3
-
-
-*0.11.0* (22th March, 2005)
-
-* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 [Nicholas Seckar]
-
-* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 [dblack/Nicholas Seckar]
-
-* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
-
-* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Lütke]
-
-* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar]
-
-* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Lütke]
-
-* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
-
- ./script/runner 'ReminderService.deliver'
- ./script/runner 'Mailer.receive(STDIN.read)'
-
- This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code.
-
-* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 [dave@cherryville.org]
-
-* Fixed the Rakefile's interaction with postgresql to:
-
- 1. Use PGPASSWORD and PGHOST in the environment to fix prompting for
- passwords when connecting to a remote db and local socket connections.
- 2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik]
- 3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson]
- 4. Use the port if available #875 [madrobby]
-
-* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2
-
-
-*0.10.1* (7th March, 2005)
-
-* Fixed rake stats to ignore editor backup files like model.rb~ #791 [Sebastian Kanthak]
-
-* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Lütke]
-
-* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
-
-* Fixed that scaffold generator new template should use local variable instead of instance variable #778 [Dan Peterson]
-
-* Allow unit tests to run on a remote server for PostgreSQL #781 [adamm@galacticasoftware.com]
-
-* Added web_service generator (run ./script/generate web_service for help) #776 [Leon Bredt]
-
-* Added app/apis and components to code statistics report #729 [Scott Barron]
-
-* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 [Nicholas Seckar]
-
-* Fixed rails_generator to be usable without RubyGems #686 [Cristi BALAN]
-
-* Fixed -h/--help for generate and destroy generators #331
-
-* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log)
-
-* Fixed that association#count would produce invalid sql when called sequentialy #659 [kanis@comcard.de]
-
-* Fixed test/mocks/testing to the correct test/mocks/test #740
-
-* Added early failure if the Ruby version isn't 1.8.2 or above #735
-
-* Removed the obsolete -i/--index option from the WEBrick servlet #743
-
-* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1
-
-
-*0.10.0* (24th February, 2005)
-
-* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel Molina Jr.]
-
-* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar]
-
-* Upgraded to breakpoint 92 which fixes:
-
- * overload IRB.parse_opts(), fixes #443
- => breakpoints in tests work even when running them via rake
- * untaint handlers, might fix an issue discussed on the Rails ML
- * added verbose mode to breakpoint_client
- * less noise caused by breakpoint_client by default
- * ignored TerminateLineInput exception in signal handler
- => quiet exit on Ctrl-C
-
-* Added support for independent components residing in /components. Example:
-
- Controller: components/list/items_controller.rb
- (holds a List::ItemsController class with uses_component_template_root called)
-
- Model : components/list/item.rb
- (namespace is still shared, so an Item model in app/models will take precedence)
-
- Views : components/list/items/show.rhtml
-
-
-* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 [Jeremy Kemper]
-
-* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 [Jeremy Kemper]
-
-* Changed script/console to default to development environment and drop --no-inspect #650 [Jeremy Kemper]
-
-* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 [Jeremy Kemper]
-
-* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 [rasputnik]
-
-* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 [Jeremy Kemper]
-
-* Added Action Web Service as a new add-on framework for Action Pack [Leon Bredt]
-
-* Added Active Support as an independent utility and standard library extension bundle
-
-* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0
-
-
-*0.9.5* (January 25th, 2005)
-
-* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require_dependency to do it.
-
-* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
-
-* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik]
-
-* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt@kurowski.net]
-
-* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter
-
-* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload
-
-* Fixed that generate scaffold would produce bad functional tests
-
-* Fixed that FCGI can also display SyntaxErrors
-
-* Upgraded to Active Record 1.6.0, Action Pack 1.4.0
-
-
-*0.9.4.1* (January 18th, 2005)
-
-* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina Jr.]
-
-* Fixed binding of caller #496 [Alexey]
-
-* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1
-
-
-*0.9.4* (January 17th, 2005)
-
-* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Lütke]
-
-* Flipped code-to-test ratio around to be more readable #468 [Scott Baron]
-
-* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson]
-
-* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Lütke]
-
-* Added rewrite rules to deal with caching to public/.htaccess
-
-* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
-
-* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
- RAILS_ENV instead of just forcing development #424 [Tobias Lütke]
-
-* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [Chris McGrath]
-
-* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
-
-* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes
-
-* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6
-
-
-*0.9.3* (January 4th, 2005)
-
-* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416
-
-* Added automated rewriting of the shebang lines on installs through the gem rails command #379 [Manfred Stienstra]
-
-* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries
- for functional testing.
-
-* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date.
- It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) [Florian Gross]
-
-* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that
- the environment options needs to change from:
-
- Before in development.rb:
- ActionController::Base.reload_dependencies = true  
- ActiveRecord::Base.reload_associations     = true
-
- Now in development.rb:
- Dependencies.mechanism = :load
-
- Before in production.rb and test.rb:
- ActionController::Base.reload_dependencies = false
- ActiveRecord::Base.reload_associations     = false
-
- Now in production.rb and test.rb:
- Dependencies.mechanism = :require
-
-* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351
-
-* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 [Lucas Carlson]
-
-* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0
-
-
-*0.9.2*
-
-* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping [Kent Sibilev]
-
-* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration [Scott Baron]
-
-* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record.
-
-* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 [rainmkr]
-
-* Added support for a -h/--help parameter in the generator #331 [Ulysses]
-
-* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories [mjobin]
-
-* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0
-
-
-*0.9.1*
-
-* Upgraded to Action Pack 1.0.1 for important bug fix
-
-* Updated gem dependencies
-
-
-*0.9.0*
-
-* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb
-
-* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible
- for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb
-
-* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to
- see help.
-
-* Added breakpoint support through the script/breakpointer client. This means that you can break out of execution at any point in
- the code, investigate and change the model, AND then resume execution! Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find_all
- breakpoint "Breaking out from the list"
- end
- end
-
- So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window.
- Here you can do things like:
-
- Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
-
- >> @posts.inspect
- => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
- #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
- >> @posts.first.title = "hello from a breakpoint"
- => "hello from a breakpoint"
-
- ...and even better is that you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
- Finally, when you're ready to resume execution, you press CTRL-D
-
-* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you
- can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server):
-
- SetEnv RAILS_ENV production
-
- ...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this:
-
- ruby public/dispatcher.servlet -e production
-
-* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that.
-
-* Added a start_server in the root of the Rails application to make it even easier to get started
-
-* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods
-
-* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe)
-
-* Added support for controllers in directories. So you can have:
-
- app/controllers/account_controller.rb # URL: /account/
- app/controllers/admin/account_controller.rb # URL: /admin/account/
-
- NOTE: You need to update your public/.htaccess with the new rules to pick it up
-
-* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use
- those environments for development. This is turned on by default, but can be turned off with
- ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments.
-
-* Added support for sub-directories in app/models. So now you can have something like Basecamp with:
-
- app/models/accounting
- app/models/project
- app/models/participants
- app/models/settings
-
- It's poor man's namespacing, but only for file-system organization. You still require files just like before.
- Nothing changes inside the files themselves.
-
-
-* Fixed a few references in the tests generated by new_mailer [Jeremy Kemper]
-
-* Added support for mocks in testing with test/mocks
-
-* Cleaned up the environments a bit and added global constant RAILS_ROOT
-
-
-*0.8.5* (9)
-
-* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]:
-
- def test_complex_stuff
- @david.projects << @new_project
- breakpoint "Let's have a closer look at @david"
- end
-
- You need to install dev-utils yourself for this to work ("gem install dev-util").
-
-* Added shared generator behavior so future upgrades should be possible without manually copying over files [Jeremy Kemper]
-
-* Added the new helper style to both controller and helper templates [Jeremy Kemper]
-
-* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding [Jeremy Kemper]
-
-* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style
-
-* Fixed that new_model was generating singular table/fixture names
-
-* Upgraded to Action Mailer 0.4.0
-
-* Upgraded to Action Pack 0.9.5
-
-* Upgraded to Active Record 1.1.0
-
-
-*0.8.0 (15)*
-
-* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is
-
-* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task.
-
-* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully [alexey]
-
-* Added user option for all postgresql tool calls in the rakefile [elvstone]
-
-* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" [alexey]
-
-* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based
- on the Ruby runtime configuration. [Marcel Molina Jr.]
-
-* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb [Caio Chassot]
-
-* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector.
-
-* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target [Andreas Schwarz]
-
-* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant.
- Class compilation cache can be turned on with "-c" or "--cache-classes".
-
-* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) [Kevin Temp]
-
-* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason [Andreas Schwarz]
-
-* Added new_mailer generator to work with Action Mailer
-
-* Included new framework: Action Mailer 0.3
-
-* Upgraded to Action Pack 0.9.0
-
-* Upgraded to Active Record 1.0.0
-
-
-*0.7.0*
-
-* Added an optional second argument to the new_model script that allows the programmer to specify the table name,
- which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures.
- [Kevin Radloff]
-
-* script/new_model now turns AccountHolder into account_holder instead of accountholder [Kevin Radloff]
-
-* Fixed the faulty handleing of static files with WEBrick [Andreas Schwarz]
-
-* Unified function_test_helper and unit_test_helper into test_helper
-
-* Fixed bug with the automated production => test database dropping on PostgreSQL [dhawkins]
-
-* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation
- and can generate more than one fixture at a time. Which makes it possible for assignments like:
-
- @people, @projects, @project_access, @companies, @accounts =
- create_fixtures "people", "projects", "project_access", "companies", "accounts"
-
-* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file)
-
-* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection)
-
-
-*0.6.5*
-
-* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile)
-
-* The new_model generator will now use the same rules for plural wordings as Active Record
- (so Category will give categories, not categorys) [Kevin Radloff]
-
-* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded [danp]
-
-* Made the GEM work with Windows
-
-* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers
-
-* PostgreSQL are now supported for the automated production => test database dropping [Kevin Radloff]
-
-* Errors thrown by the dispatcher are now properly handled in FCGI.
-
-* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes)
-
-* Upgraded to Active Record 0.9.4 (a bunch of fixes)
-
-
-*0.6.0*
-
-* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used
- to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all
- controllers will also automatically have available.
-
-* Added environments that can be included from any script to get the full Active Record and Action Controller
- context running. This can be used by maintenance scripts or to interact with the model through IRB. Example:
-
- require 'config/environments/production'
-
- for account in Account.find_all
- account.recalculate_interests
- end
-
- A short migration script for an account model that had it's interest calculation strategy changed.
-
-* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick)
-
-* Simplified the default Apache config so even remote requests are served off CGI as a default.
- You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls).
- This should make it easier for new comers that start on an external server.
-
-* Added more of the necessary Apache options to .htaccess to make it easier to setup
-
-* Upgraded to Action Pack 0.7.9 (lots of fixes)
-
-* Upgraded to Active Record 0.9.3 (lots of fixes)
-
-
-*0.5.7*
-
-* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL
- (through GET requests or otherwise)
-
-* Added lib in root as a place to store app specific libraries
-
-* Added lib and vendor to load_path, so anything store within can be loaded directly.
- Hence lib/redcloth.rb can be loaded with require "redcloth"
-
-* Upgraded to Action Pack 0.7.8 (lots of fixes)
-
-* Upgraded to Active Record 0.9.2 (minor upgrade)
-
-
-*0.5.6*
-
-* Upgraded to Action Pack 0.7.7 (multipart form fix)
-
-* Updated the generated template stubs to valid XHTML files
-
-* Ensure that controllers generated are capitalized, so "new_controller TodoLists"
- gives the same as "new_controller Todolists" and "new_controller todolists".
-
-
-*0.5.5*
-
-* Works on Windows out of the box! (Dropped symlinks)
-
-* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" [Florian Gross]
-
-* Report errors about initialization to browser (instead of attempting to use uninitialized logger)
-
-* Upgraded to Action Pack 0.7.6
-
-* Upgraded to Active Record 0.9.1
-
-* Added distinct 500.html instead of reusing 404.html
-
-* Added MIT license
-
-
-*0.5.0*
-
-* First public release
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
new file mode 100644
index 0000000000..a5f32d1a2c
--- /dev/null
+++ b/railties/CHANGELOG.md
@@ -0,0 +1,2445 @@
+## Rails 4.0.0 (unreleased) ##
+
+* Allow to set class that will be used to run as a console, other than IRB, with `Rails.application.config.console=`. It's best to add it to `console` block. *Piotr Sarnacki*
+
+ Example:
+
+ # it can be added to config/application.rb
+ console do
+ # this block is called only when running console,
+ # so we can safely require pry here
+ require "pry"
+ config.console = Pry
+ end
+
+* Add convenience `hide!` method to Rails generators to hide current generator
+ namespace from showing when running `rails generate`. *Carlos Antonio da Silva*
+
+* Scaffold now uses `content_tag_for` in index.html.erb *José Valim*
+
+* Rails::Plugin has gone. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies. *Santiago Pastorino*
+
+## Rails 3.2.0 (January 20, 2012) ##
+
+* Turn gem has been removed from default Gemfile. We still looking for a best presentation for tests output. *Guillermo Iguaran*
+
+* Rails::Plugin is deprecated and will be removed in Rails 4.0. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies. *Santiago Pastorino*
+
+* Guides are available as a single .mobi for the Kindle and free Kindle readers apps. *Michael Pearson & Xavier Noria*
+
+* Allow scaffold/model/migration generators to accept a "index" and "uniq" modifiers, as in: "tracking_id:integer:uniq" in order to generate (unique) indexes. Some types also accept custom options, for instance, you can specify the precision and scale for decimals as "price:decimal{7,2}". *Dmitrii Samoilov*
+
+* Added `config.exceptions_app` to set the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to `ActionDispatch::PublicExceptions.new(Rails.public_path)`. *José Valim*
+
+* Speed up development by only reloading classes if dependencies files changed. This can be turned off by setting `config.reload_classes_only_on_change` to false. *José Valim*
+
+* New applications get a flag `config.active_record.auto_explain_threshold_in_seconds` in the environments configuration files. With a value of 0.5 in development.rb, and commented out in production.rb. No mention in test.rb. *fxn*
+
+* Add DebugExceptions middleware which contains features extracted from ShowExceptions middleware *José Valim*
+
+* Display mounted engine's routes in `rake routes` *Piotr Sarnacki*
+
+* Allow to change the loading order of railties with `config.railties_order=` *Piotr Sarnacki*
+
+ Example:
+ config.railties_order = [Blog::Engine, :main_app, :all]
+
+* Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box *José Valim*
+
+* Update Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications *DHH*
+
+* Default options to `rails new` can be set in ~/.railsrc. You can specify extra command-line arguments to be used every time
+ 'rails new' runs in the .railsrc configuration file in your home directory. *Guillermo Iguaran*
+
+* Add destroy alias to Rails engines *Guillermo Iguaran*
+
+* Add destroy alias for Rails command line. This allows the following: `rails d model post` *Andrey Ognevsky*
+
+* Attributes on scaffold and model generators default to string. This allows the following: "rails g scaffold Post title body:text author" *José Valim*
+
+* Remove old plugin generator (`rails generate plugin`) in favor of `rails plugin new` command *Guillermo Iguaran*
+
+* Remove old 'config.paths.app.controller' API in favor of 'config.paths["app/controller"]' API *Guillermo Iguaran*
+
+
+## Rails 3.1.2 (November 18, 2011) ##
+
+* Engines: don't blow up if db/seeds.rb is missing.
+
+ *Jeremy Kemper*
+
+* `rails new foo --skip-test-unit` should not add the `:test` task to the rake default task.
+ *GH 2564*
+
+ *José Valim*
+
+## Rails 3.1.1 (October 07, 2011) ##
+
+* Add jquery-rails to Gemfile of plugins, test/dummy app needs it. Closes #3091. *Santiago Pastorino*
+
+* Add config.assets.initialize_on_precompile which, when set to false, forces
+ `rake assets:precompile` to load the application but does not initialize it.
+
+ To the app developer, this means configuration add in
+ config/initializers/* will not be executed.
+
+ Plugins developers need to special case their initializers that are
+ meant to be run in the assets group by adding :group => :assets.
+
+## Rails 3.1.0 (August 30, 2011) ##
+
+* The default database schema file is written as UTF-8. *Aaron Patterson*
+
+* Generated apps with --dev or --edge flags depend on git versions of
+ sass-rails and coffee-rails. *Santiago Pastorino*
+
+* Rack::Sendfile middleware is used only if x_sendfile_header is present. *Santiago Pastorino*
+
+* Add JavaScript Runtime name to the Rails Info properties. *DHH*
+
+* Make pp enabled by default in Rails console. *Akira Matsuda*
+
+* Add alias `r` for rails runner. *Jordi Romero*
+
+* Make sprockets/railtie require explicit and add --skip-sprockets to app generator *José Valim*
+
+* Added Rails.groups that automatically handles Rails.env and ENV["RAILS_GROUPS"] *José Valim*
+
+* The new rake task assets:clean removes precompiled assets. *fxn*
+
+* Application and plugin generation run bundle install unless --skip-gemfile or --skip-bundle. *fxn*
+
+* Fixed database tasks for jdbc* adapters #jruby *Rashmi Yadav*
+
+* Template generation for jdbcpostgresql #jruby *Vishnu Atrai*
+
+* Template generation for jdbcmysql and jdbcsqlite3 #jruby *Arun Agrawal*
+
+* The -j option of the application generator accepts an arbitrary string. If passed "foo",
+ the gem "foo-rails" is added to the Gemfile, and the application JavaScript manifest
+ requires "foo" and "foo_ujs". As of this writing "prototype-rails" and "jquery-rails"
+ exist and provide those files via the asset pipeline. Default is "jquery". *fxn*
+
+* jQuery is no longer vendored, it is provided from now on by the jquery-rails gem. *fxn*
+
+* Prototype and Scriptaculous are no longer vendored, they are provided from now on
+ by the prototype-rails gem. *fxn*
+
+* The scaffold controller will now produce SCSS file if Sass is available *Prem Sichanugrist*
+
+* The controller and resource generators will now automatically produce asset stubs (this can be turned off with --skip-assets). These stubs will use Coffee and Sass, if those libraries are available. *DHH*
+
+* jQuery is the new default JavaScript library. *fxn*
+
+* Changed scaffold, application, and mailer generator to create Ruby 1.9 style hash when running on Ruby 1.9 *Prem Sichanugrist*
+
+ So instead of creating something like:
+
+ redirect_to users_path, :notice => "User has been created"
+
+ it will now be like this:
+
+ redirect_to users_path, notice: "User has been created"
+
+ You can also passing `--old-style-hash` to make Rails generate old style hash even you're on Ruby 1.9
+
+* Changed scaffold_controller generator to create format block for JSON instead of XML *Prem Sichanugrist*
+
+* Add using Turn with natural language test case names for test_help.rb when running with minitest (Ruby 1.9.2+) *DHH*
+
+* Direct logging of Active Record to STDOUT so it's shown inline with the results in the console *DHH*
+
+* Added `config.force_ssl` configuration which loads Rack::SSL middleware and force all requests to be under HTTPS protocol *DHH, Prem Sichanugrist, and Josh Peek*
+
+* Added `rails plugin new` command which generates rails plugin with gemspec, tests and dummy application for testing *Piotr Sarnacki*
+
+* Added -j parameter with jquery/prototype as options. Now you can create your apps with jQuery using `rails new myapp -j jquery`. The default is still Prototype. *siong1987*
+
+* Added Rack::Etag and Rack::ConditionalGet to the default middleware stack *José Valim*
+
+* Added Rack::Cache to the default middleware stack *Yehuda Katz and Carl Lerche*
+
+* Engine is now rack application *Piotr Sarnacki*
+
+* Added middleware stack to Engine *Piotr Sarnacki*
+
+* Engine can now load plugins *Piotr Sarnacki*
+
+* Engine can load its own environment file *Piotr Sarnacki*
+
+* Added helpers to call engines' route helpers from application and vice versa *Piotr Sarnacki*
+
+* Task for copying plugins' and engines' migrations to application's db/migrate directory *Piotr Sarnacki*
+
+* Changed ActionDispatch::Static to allow handling multiple directories *Piotr Sarnacki*
+
+* Added isolate_namespace() method to Engine, which sets Engine as isolated *Piotr Sarnacki*
+
+* Include all helpers from plugins and shared engines in application *Piotr Sarnacki*
+
+
+## Rails 3.0.7 (April 18, 2011) ##
+
+* No changes.
+
+
+* Rails 3.0.6 (April 5, 2011)
+
+* No changes.
+
+
+## Rails 3.0.5 (February 26, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.4 (February 8, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.3 (November 16, 2010) ##
+
+* No changes.
+
+
+## Rails 3.0.2 (November 15, 2010) ##
+
+* No changes.
+
+
+## Rails 3.0.1 (October 15, 2010) ##
+
+* No Changes, just a version bump.
+
+
+## Rails 3.0.0 (August 29, 2010) ##
+
+* Application generation: --skip-testunit and --skip-activerecord become --skip-test-unit and --skip-active-record respectively. *fxn*
+
+* Added console to Rails::Railtie as a hook called just after console starts. *José Valim*
+
+* Rails no longer autoload code in lib for application. You need to explicitly require it. *José Valim*
+
+* Rails::LogSubscriber was renamed to ActiveSupport::LogSubscriber *José Valim*
+
+* config.load_(once_)paths in config/application.rb got renamed to config.autoload_(once_)paths. *fxn*
+
+* Abort generation/booting on Ruby 1.9.1. *fxn*
+
+* Made the rails command work even when you're in a subdirectory *Chad Fowler*
+
+* Removed Rails Metal [Yehuda Katz, José Valim].
+
+* Renamed config.cookie_secret to config.secret_token and pass it as env key. *José Valim*
+
+* Session store configuration has changed *Yehuda Katz, Carl Lerche*
+
+ config.session_store :cookie_store, {:key => "..."}
+ config.cookie_secret = "fdsfhisdghfidugnfdlg"
+
+* railtie_name and engine_name are deprecated. You can now add any object to
+ the configuration object: config.your_plugin = {} *José Valim*
+
+* Added config.generators.templates to provide alternative paths for the generators
+ to look for templates *José Valim*
+
+* Added "rake about" as a replacement for script/about *DHH*
+
+* Removed all the default commands in script/* and replaced them with script/rails and a rails command that'll act the same when run from within the app [DHH]. Example:
+
+ ./script/generate scaffold post title:string can now be called as rails g scaffold post title:string
+
+ Run rails --help inside an app for more help.
+
+* Removed config/initializers/new_rails_defaults.rb as all frameworks now follow the settings from it *DHH*
+
+* Set config.time_zone to UTC by default *DHH*
+
+* Added default .gitignore (this is just recognizing Git market share, don't throw a hissy if you use another SCM) *DHH*
+
+* Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH]. Examples:
+
+ cookies.permanent[:prefers_open_id] = true
+ # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+
+ cookies.signed[:discount] = 45
+ # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
+
+ cookies.signed[:discount]
+ # => 45 (if the cookie was changed, you'll get a InvalidSignature exception)
+
+ cookies.permanent.signed[:remember_me] = current_user.id
+ # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+
+ ...to use the signed cookies, you need to set a secret to ActionController::Base.cookie_verifier_secret (automatically done in config/initializers/cookie_verification_secret.rb for new Rails applications).
+
+* Added config/initializers/cookie_verification_secret.rb with an auto-generated secret for using ActionController::Base#cookies.signed *DHH*
+
+* Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs *DHH*
+
+* I18n support for plugins. #2325 *Antonio Tapiador, Sven Fuchs*
+
+* 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*
+
+* Allow metal to live in plugins #2045 *Matthew Rudy*
+
+* Added metal *Josh Peek*
+
+* Remove script/performance/request in favour of the performance integration tests. *Pratik Naik*
+
+ To continue using script/performance/request, install the request_profiler plugin :
+
+ script/plugin install git://github.com/rails/request_profiler.git
+
+* Add a rake task to apply a template to an existing application : rake rails:template LOCATION=~/template.rb *Pratik Naik*
+
+* Add "-m/--template" option to Rails generator to apply a template to the generated application. *Jeremy McAnally*
+
+ This has been extracted from rg - http://github.com/jeremymcanally/rg
+
+ Example:
+
+ # template.rb
+
+ # Install plugins from git or svn
+ plugin "will-paginate", :git => "git://github.com/mislav/will_paginate.git"
+ plugin "old-restful-auth", :svn => "http://svn.techno-weenie.net/projects/plugins/restful_authentication/"
+
+ # Add gems to environment.rb
+ gem "jeremymcanally-context"
+ gem "bluecloth"
+
+ # Vendor file. Data in a string or...
+ vendor("borrowed.rb", <<CODE
+ def helpful_method
+ do_something_helpful_here
+ end
+ CODE
+
+ # ...file data from block return value.
+ # #initializer creates a new initializer file
+ initializer("crypto.rb") do
+ salt = "--#{Time.now}--#{rand}--#{srand(Time.now.to_i)}"
+
+ "SPECIAL_SALT = '#{salt}'"
+ end
+
+ Usage:
+
+ To use a template, provide a file path or URL:
+
+ 1. Using a local file :
+
+ rails <application name> -m /path/to/my/template.rb
+
+ 2. Or directly from a URL :
+
+ rails <application name> --template=http://gist.github.com/31208.txt
+
+* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts *David Heinemeier Hansson*
+
+* Changed Rails.root to return a Pathname object (allows for Rails.root.join('app', 'controllers') => "#{RAILS_ROOT}/app/controllers") #1482 *Damian Janowski/?*
+
+* Added view path support for engines *David Heinemeier Hansson*
+
+* Added that config/routes.rb files in engine plugins are automatically loaded (and reloaded when they change in dev mode) *David Heinemeier Hansson*
+
+* Added app/[models|controllers|helpers] to the load path for plugins that has an app directory (go engines ;)) *David Heinemeier Hansson*
+
+* Add config.preload_frameworks to load all frameworks at startup. Default to false so Rails autoloads itself as it's used. Turn this on for Passenger and JRuby. Also turned on by config.threadsafe! *Jeremy Kemper*
+
+* Add a rake task to generate dispatchers : rake rails:generate_dispatchers *Pratik Naik*
+
+* "rails <app>" will not generate public/dispatch.cgi/fcgi/rb files by default now. Please use "--with-dispatchers" option if you need them. *Yaroslav Markin, Pratik Naik*
+
+* Added rake rails:update:application_controller to renamed application.rb to application_controller.rb -- included in rake rails:update so upgrading to 2.3 will automatically trigger it #1439 *kastner*
+
+* Added Rails.backtrace_cleaner as an accessor for the Rails::BacktraceCleaner instance used by the framework to cut down on backtrace noise and config/initializers/backtrace_silencers.rb to add your own (or turn them all off) *David Heinemeier Hansson*
+
+* Switch from Test::Unit::TestCase to ActiveSupport::TestCase. *Jeremy Kemper*
+
+* Added config.i18n settings gatherer to config/environment, auto-loading of all locales in config/locales/*.rb,yml, and config/locales/en.yml as a sample locale *David Heinemeier Hansson*
+
+* BACKWARDS INCOMPATIBLE: Renamed application.rb to application_controller.rb and removed all the special casing that was in place to support the former. You must do this rename in your own application when you upgrade to this version *David Heinemeier Hansson*
+
+
+## 2.2.1 RC2 (November 14th, 2008) ##
+
+* Fixed plugin generator so that generated unit tests would subclass ActiveSupport::TestCase, also introduced a helper script to reduce the needed require statements #1137 *Mathias Meyer*
+
+* Update Prototype to 1.6.0.3 *sam*
+
+
+## 2.2.0 RC1 (October 24th, 2008) ##
+
+* Fixed that sqlite would report "db/development.sqlite3 already exists" whether true or not on db:create #614 *Antonio Cangiano*
+
+* Added config.threadsafe! to toggle allow concurrency settings and disable the dependency loader *Josh Peek*
+
+* Turn cache_classes on by default *Josh Peek*
+
+* Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers *Josh Peek*
+
+* Introduce simple internationalization support. *Ruby i18n team*
+
+* Make script/plugin install <plugin> -r <revision> option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example:
+
+ script/plugin install git://github.com/mislav/will_paginate.git -r agnostic # Installs 'agnostic' branch
+ script/plugin install git://github.com/dchelimsky/rspec.git -r 'tag 1.1.4'
+
+* Added Rails.initialized? flag *Josh Peek*
+
+* Make rake test:uncommitted work with Git. *Tim Pope*
+
+* Added Thin support to script/server. #488 *Bob Klosinski*
+
+* Fix script/about in production mode. #370 *Cheah Chu Yeow, Xavier Noria, David Krmpotic*
+
+* Add the gem load paths before the framework is loaded, so certain gems like RedCloth and BlueCloth can be frozen.
+
+* Fix discrepancies with loading rails/init.rb from gems.
+
+* Plugins check for the gem init path (rails/init.rb) before the standard plugin init path (init.rb) *Jacek Becela*
+
+* Changed all generated tests to use the test/do declaration style *David Heinemeier Hansson*
+
+* Wrapped Rails.env in StringInquirer so you can do Rails.env.development? *David Heinemeier Hansson*
+
+* Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) *#310 state:resolved*
+
+
+## 2.1.0 (May 31st, 2008) ##
+
+* script/dbconsole fires up the command-line database client. #102 *Steve Purcell*
+
+* Fix bug where plugin init.rb files from frozen gem specs weren't being run. (pjb3) *#122 state:resolved*
+
+* Made the location of the routes file configurable with config.routes_configuration_file (Scott Fleckenstein) *#88*
+
+* Rails Edge info returns the latest git commit hash *Francesc Esplugas*
+
+* Added Rails.public_path to control where HTML and assets are expected to be loaded from (defaults to Rails.root + "/public") #11581 *Nick Sieger*
+
+* rake time:zones:local finds correct base utc offset for zones in the Southern Hemisphere *Geoff Buesing*
+
+* Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. *Rick Olson*
+
+* script/performance/profiler compatibility with the ruby-prof >= 0.5.0. Closes #9176. *Jonathan del Strother*
+
+* Flesh out rake gems:unpack to unpack all gems, and add rake gems:build for native extensions. #11513 *ddollar*
+
+ rake gems:unpack # unpacks all gems
+ rake gems:unpack GEM=mygem # unpacks only the gem 'mygem'
+
+ rake gems:build # builds all unpacked gems
+ rake gems:build GEM=mygem # builds only the gem 'mygem'
+
+* Add config.active_support for future configuration options. Also, add more new Rails 3 config settings to new_rails_defaults.rb *Rick Olson*
+
+* Add Rails.logger, Rails.root, Rails.env and Rails.cache shortcuts for RAILS_* constants *Pratik Naik*
+
+* Allow files in plugins to be reloaded like the rest of the application. *Rick Olson*
+
+ Enables or disables plugin reloading.
+
+ config.reload_plugins = true
+
+ You can get around this setting per plugin.
+ If #reload_plugins? == false (DEFAULT), add this to your plugin's init.rb to make it reloadable:
+
+ Dependencies.load_once_paths.delete lib_path
+
+ If #reload_plugins? == true, add this to your plugin's init.rb to only load it once:
+
+ Dependencies.load_once_paths << lib_path
+
+* Small tweak to allow plugins to specify gem dependencies. *Rick Olson*
+
+ \# OLD open_id_authentication plugin init.rb
+ require 'yadis'
+ require 'openid'
+ ActionController::Base.send :include, OpenIdAuthentication
+
+ \# NEW
+ config.gem "ruby-openid", :lib => "openid", :version => "1.1.4"
+ config.gem "ruby-yadis", :lib => "yadis", :version => "0.3.4"
+
+ config.after_initialize do
+ ActionController::Base.send :include, OpenIdAuthentication
+ end
+
+* Added config.gem for specifying which gems are required by the application, as well as rake tasks for installing and freezing gems. *Rick Olson*
+
+ Rails::Initializer.run do |config|
+ config.gem "bj"
+ config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
+ config.gem "aws-s3", :lib => "aws/s3"
+ end
+
+ \# List required gems.
+ rake gems
+
+ \# Install all required gems:
+ rake gems:install
+
+ \# Unpack specified gem to vendor/gems/gem_name-x.x.x
+ rake gems:unpack GEM=bj
+
+* Removed the default .htaccess configuration as there are so many good deployment options now (kept it as an example in README) *David Heinemeier Hansson*
+
+* config.time_zone accepts TZInfo::Timezone identifiers as well as Rails TimeZone identifiers *Geoff Buesing*
+
+* Rails::Initializer#initialize_time_zone raises an error if value assigned to config.time_zone is not recognized. Rake time zone tasks only require ActiveSupport instead of entire environment *Geoff Buesing*
+
+* Stop adding the antiquated test/mocks/* directories and only add them to the path if they're still there for legacy reasons *David Heinemeier Hansson*
+
+* Added that gems can now be plugins if they include rails/init.rb #11444 *John Barnette*
+
+* Added Plugin#about method to programmatically access the about.yml in a plugin #10979 *James Adam*
+
+ plugin = Rails::Plugin.new(path_to_my_plugin)
+ plugin.about["author"] # => "James Adam"
+ plugin.about["url"] # => "http://interblah.net"
+
+* Improve documentation. *Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert*
+
+* Added config.time_zone = 'UTC' in the default environment.rb *Geoff Buesing*
+
+* Added rake tasks time:zones:all, time:zones:us and time:zones:local for finding time zone names for config.time_zone option *Geoff Buesing*
+
+* Add config.time_zone for configuring the default Time.zone value. #10982 *Geoff Buesing*
+
+* Added support for installing plugins hosted at git repositories #11294 *Jack Danger Canty*
+
+* Fixed that script/generate would not look for plugin generators in plugin_paths #11000 *glv*
+
+* Fixed database rake tasks to work with charset/collation and show proper error messages on failure. Closes #11301 *matt*
+
+* Added a -e/--export to script/plugin install, uses svn export. #10847 *jon@blankpad.net)*
+
+* Reshuffle load order so that routes and observers are initialized after plugins and app initializers. Closes #10980 *Rick Olson*
+
+* Git support for script/generate. #10690 *ssoroka*
+
+* Update scaffold to use labels instead of bold tags. Closes #10757 *zach-inglis-lt3*
+
+* Resurrect WordNet synonym lookups. #10710 *tom./, matt*
+
+* Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) *David Heinemeier Hansson*
+
+* Added that rails:update is run when you do rails:freeze:edge to ensure you also get the latest JS and config files #10565 *jeff*
+
+* SQLite: db:drop:all doesn't fail silently if the database is already open. #10577 *Cheah Chu Yeow, mrichman*
+
+* Introduce native mongrel handler and push mutex into dispatcher. *Jeremy Kemper*
+
+* Ruby 1.9 compatibility. #1689, #10546 *Cheah Chu Yeow, frederico*
+
+
+## 2.0.2 (December 16th, 2007) ##
+
+* Changed the default database from mysql to sqlite3, so now running "rails myapp" will have a config/database.yml that's setup for SQLite3 (which in OS X Leopard is installed by default, so is the gem, so everything Just Works with no database configuration at all). To get a Rails application preconfigured for MySQL, just run "rails -d mysql myapp" *David Heinemeier Hansson*
+
+* Turned on ActionView::Base.cache_template_loading by default in config/environments/production.rb to prevent file system stat calls for every template loading to see if it changed (this means that you have to restart the application to see template changes in production mode) *David Heinemeier Hansson*
+
+* Introduce `rake secret` to output a crytographically secure secret key for use with cookie sessions #10363 *revans*
+
+* Fixed that local database creation should consider 127.0.0.1 local #9026 *parcelbrat*
+
+* Fixed that functional tests generated for scaffolds should use fixture calls instead of hard-coded IDs #10435 *boone*
+
+* Added db:migrate:redo and db:migrate:reset for rerunning existing migrations #10431, #10432 *matt*
+
+* RAILS_GEM_VERSION may be double-quoted also. #10443 *James Cox*
+
+* Update rails:freeze:gems to work with RubyGems 0.9.5. *Jeremy Kemper*
+
+
+## 2.0.1 (December 7th, 2007) ##
+
+* Fixed Active Record bug
+
+
+## 2.0.0 (December 6th, 2007) ##
+
+* The test task stops with a warning if you have pending migrations. #10377 *Josh Knowles*
+
+* Add warning to documentation about using transactional fixtures when the code under test uses transactions itself. Closes #7548 *Thijs van der Vossen*
+
+* Update Prototype to 1.6.0.1. *sam*
+
+* Update script.aculo.us to 1.8.0.1. *madrobby*
+
+* Added db:fixtures:identity as a way of locating what ID a foxy fixture was assigned #10332 *John Barnette*
+
+* Generated fixtures should not specify ids since theyre expected to be foxy fixtures #10330 *John Barnette*
+
+* Update to Prototype -r8232. *sam*
+
+* Introduce SecretKeyGenerator for more secure session secrets than CGI::Session's pseudo-random id generator. Consider extracting to Active Support later. #10286 *Hongli Lai (Phusion)*
+
+* RAILS_GEM_VERSION may be set to any valid gem version specifier. #10057 *Chad Woolley, Cheah Chu Yeow*
+
+* Load config/preinitializer.rb, if present, before loading the environment. #9943 *Chad Woolley*
+
+* FastCGI handler ignores unsupported signals like USR2 on Windows. *Grzegorz Derebecki*
+
+* Only load ActionMailer::TestCase if ActionMailer is loaded. Closes #10137 *defunkt*
+
+* Fixed that db:reset would use migrations instead of loading db/schema.rb *David Heinemeier Hansson*
+
+* Ensure the plugin loader only loads plugins once. Closes #10102 *haruki_zaemon*
+
+* Refactor Plugin Loader. Add plugin lib paths early, and add lots of tests. Closes #9795 *James Adam*
+
+* Added --skip-timestamps to generators that produce models #10036 *Tim Pope*
+
+* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. *sam, madrobby*
+
+* Added db:rollback to rollback the schema one version (or multiple as specified by STEP) *Jeffrey Allan Hardy*
+
+* Fix typo in test_helper. Closes #9925 *viktor tron*
+
+* Request profiler. *Jeremy Kemper*
+
+* config/boot.rb correctly detects RAILS_GEM_VERSION. #9834 *alexch, thewoolleyman*
+
+* Fixed incorrect migration number if script/generate executed outside of Rails root #7080 *Jeremy McAnally*
+
+* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0. *sam, madrobby*
+
+* Generated fixtures use the actual primary key instead of id. #4343 *Frederick Ros, Tarmo Tänav*
+
+* Extend the console +helper+ method to allow you to include custom helpers. e.g:
+ >> helper :posts
+ >> helper.some_method_from_posts_helper(Post.find(1))
+
+* db:create works with remote databases whereas db:create:all only creates
+ databases on localhost. #9753 *Trevor Wennblom*
+
+* Removed calls to fixtures in generated tests as fixtures :all is now present by default in test_helper.rb *David Heinemeier Hansson*
+
+* Add --prefix option to script/server when using mongrel. *dacat*
+
+
+## 2.0.0 Preview Release (September 29th, 2007) Includes duplicates of changes from 1.1.4 - 1.2.3 ##
+
+* Fixed that installing plugins from SVN repositories that use trunk/ will work #8188 *evan*
+
+* Moved the SourceAnnotationExtractor to a separate file in case libraries try to load the rails rake tasks twice. *Rick Olson*
+
+* Moved Dispatcher to ActionController::Dispatcher. *Jeremy Kemper*
+
+* Changed the default logger from Ruby's own Logger with the clean_logger extensions to ActiveSupport::BufferedLogger for performance reasons [David Heinemeier Hansson]. (You can change it back with config.logger = Logger.new("/path/to/log", level).)
+
+* Added a default 422.html page to be rendered when ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, or ActionController::InvalidAuthenticityToken is raised *David Heinemeier Hansson*
+
+* Added --skip-fixture option to script/generate model #6862 *sandofsky*
+
+* Print Rails version when starting console #7440 *eyematz*
+
+* Fixed the placement of fixture files for nested models when generating through script/generate model #7547 *jkit*
+
+* Added TEMPLATE option to rake doc:app to set a custom output template #7737 *Jakob Skjerning*
+
+* Added VERBOSE option to rake db:migrate to turn off output #8204 *John Barnette*
+
+* Fixed that rake doc:app should use UTF-8 #8906 *farzy*
+
+* Fixes rake annotations to search erb and builder files as well #9150 *m.langenberg*
+
+* Removed web_service generator *Michael Koziarski*
+
+* Added the :all option to config.plugins that'll include the rest of the plugins not already explicitly named #9613 [Frederick Cheung]. Example:
+
+ # Loads :classic_pagination before all the other plugins
+ config.plugins = [ :classic_pagination, :all ]
+
+* Added symbols as a legal way of specifying plugins in config.plugins #9629 *tom*
+
+* Removed deprecated task names, like clear_logs, in favor of the new namespaced style *David Heinemeier Hansson*
+
+* Support multiple config.after_initialize blocks so plugins and apps can more easily cooperate. #9582 *Zach Dennis*
+
+* Added db:drop:all to drop all databases declared in config/database.yml *David Heinemeier Hansson*
+
+* Use attribute pairs instead of the migration name to create add and remove column migrations. Closes #9166 *Pratik Naik*
+
+ For example:
+
+ ruby script/generation migration AddSomeStuffToCustomers first_name:string last_name:string
+
+ or
+
+ ruby script/generation migration RemoveSomeStuffFromCustomers first_name:string last_name:string
+
+* Add ActiveResource to Rails::Info. Closes #8741 *Chris Kampmeier*
+
+* use Gem.find_name instead of search when freezing gems. Prevent false positives for other gems with rails in the name. Closes #8729 *wselman*
+
+* Automatically generate add/remove column commands in specially named migrations like AddLocationToEvent. Closes #9006 *Ryan Davis*
+
+* Default to plural table name in Rails Generator if ActiveRecord is not present. Closes #8963 *evan*
+
+* Added rake routes for listing all the defined routes in the system. #8795 *Josh Peek*
+
+* db:create creates the database for the current environment if it's on localhost. db:create:all creates local databases for all environments. #8783 *matt*
+
+* Generators: look for generators in all gems, not just those suffixed with _generator, in the gem's generators or rails_generators directory. Allow use of the rails_generators directory instead of the standard generators directory in plugins also. #8730 *Dr Nic, topfunky*
+
+* MySQL, PostgreSQL: database.yml defaults to utf-8. #8701 *matt*
+
+* Added db:version to get the current schema number *via Err The Blog*
+
+* Added --skip-migration option to scaffold and resource generators #8656 *Michael Glaesemann*
+
+* Fix that FCGIs would leave log files open when asked to shut down by USR2. #3028 *Sebastian Kanthak, Josh Peek*
+
+* Fixed that dispatcher preparation callbacks only run once in production mode. Mock Routes.reload so that dispatcher preparation callback tests run. *Rick Olson*
+
+* Fix syntax error in dispatcher than wrecked failsafe responses. #8625 *mtitorenko*
+
+* Scaffolded validation errors set the appropriate HTTP status for XML responses. #6946, #8622 *Manfred Stienstra, mmmultiworks*
+
+* Sexy migrations for the session_migration generator. #8561 *Vladislav*
+
+* Console reload! runs to_prepare callbacks also. #8393 *f.svehla*
+
+* Generated migrations include timestamps by default. #8501 *Shane Vitarana*
+
+* Drop Action Web Service from rails:freeze:edge. *Jeremy Kemper*
+
+* Add db:create, drop, reset, charset, and collation tasks. #8448 *matt*
+
+* Scaffold generator depends on model generator instead of duplicating it. #7222 *bscofield*
+
+* Scaffold generator tests. #8443 *pelle*
+
+* Generated scaffold functional tests use assert_difference. #8421 *Norbert Crombach*
+
+* Update to Prototype 1.5.1. *Sam Stephenson*
+
+* Update to script.aculo.us 1.7.1_beta3. *Thomas Fuchs*
+
+* Generators use *.html.erb view template naming. #8278 *Tim Pope*
+
+* Updated resource_scaffold and model generators to use short-hand style migrations *David Heinemeier Hansson*
+
+* Updated initializer to only load #{RAILS_ENV}.rb once. Added deprecation warning for config.breakpoint_server. *Nicholas Seckar*
+
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back *David Heinemeier Hansson*
+
+ To use the new debugger, start your server with script/server --debugger and insert a call to 'debugger'
+ (instead of 'breakpoint') where you want to jump into the debugger.
+
+ BACKWARDS INCOMPATIBILITY NOTE: You must remove the default line 12 from config/environments/development.rb:
+
+ config.breakpoint_server = true
+
+ This configuration option is no longer available. Rails will fail to start in development mode as long as
+ that's still present.
+
+* Resource scaffolding returns the created entity.to_xml. *Jeremy Kemper*
+
+* Resource scaffolding responds to new.xml. #8185 *Eric Mill*
+
+* Include Active Resource in rails:freeze:edge rake task. *Thomas Fuchs*
+
+* Include Active Resource instead of Action Web Service [David Heinemeier Hansson] You can add AWS back with this in config/environment.rb:
+
+ config.load_paths += %W( #{RAILS_ROOT}/vendor/rails/actionwebservice/lib )
+
+ ...or just gem 'actionwebservice'
+
+* Give generate scaffold a more descriptive database message. Closes #7316 *Jeremy McAnally*
+
+* Canonicalize RAILS_ROOT by using File.expand_path on Windows, which doesn't have to worry about symlinks, and Pathname#realpath elsewhere, which respects symlinks in relative paths but is incompatible with Windows. #6755 *Jeremy Kemper, trevor*
+
+* Deprecation: remove components from controller paths. *Jeremy Kemper*
+
+* Add environment variable RAILS_DEFAULT_DATABASE, which allows the builtin default of 'mysql' to be overridden. *Nicholas Seckar*
+
+* Windows: include MinGW in RUBY_PLATFORM check. #2982 *okkez000@gmail.com, Kaspar Schiess*
+
+* Split out the basic plugin locator functionality into an abstract super class. Add a FileSystemLocator to do the job of checking the plugin_paths for plugins. Add plugin_locators configuration option which will iterate over the set of plugin locators and load each of the plugin loaders they return. Rename locater everywhere to locator. *Marcel Molina Jr.*
+
+* Split plugin location and loading out of the initializer and into a new Plugin namespace, which includes Plugin::Locater and Plugin::Loader. The loader class that is used can be customized using the config.plugin_loader option. Those monkey patching the plugin loading subsystem take note, the internals changing here will likely break your modifications. The good news is that it should be substantially easier to hook into the plugin locating and loading process now. *Marcel Molina Jr.*
+
+* Added assumption that all plugin creators desire to be sharing individuals and release their work under the MIT license *David Heinemeier Hansson*
+
+* Added source-annotations extractor tasks to rake [Jamis Buck]. This allows you to add FIXME, OPTIMIZE, and TODO comments to your source code that can then be extracted in concert with rake notes (shows all), rake notes:fixme, rake notes:optimize and rake notes:todo.
+
+* Added fixtures :all to test_helper.rb to assume that most people just want all their fixtures loaded all the time *David Heinemeier Hansson*
+
+* Added config/initializers where all ruby files within it are automatically loaded after the Rails configuration is done, so you don't have to litter the environment.rb file with a ton of mixed stuff *David Heinemeier Hansson*
+
+* For new apps, generate a random secret for the cookie-based session store. *Jeremy Kemper*
+
+* Stop swallowing errors during rake test *Michael Koziarski*
+
+* Update Rails Initializer to use ActionController::Base#view_paths *Rick Olson*
+
+* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ *Chad Fowler*
+
+* Plugins may be symlinked in vendor/plugins. #4245 *brandon, progrium@gmail.com*
+
+* Resource generator depends on the model generator rather than duplicating it. #7269 *bscofield*
+
+* Add/Update usage documentation for script/destroy, resource generator and scaffold_resource generator. Closes #7092, #7271, #7267. *bscofield*
+
+* Update to script.aculo.us 1.7.0. *Thomas Fuchs*
+
+* Update to Prototype 1.5.0. *Sam Stephenson*
+
+* Generator: use destination path for diff tempfiles. #7015 *alfeld*
+
+* Fixed that webrick would strip leading newlines and hang connection #4156 *psross*
+
+* Ensure plugins are in the Dependencies.load_once_paths collection by default. *Rick Olson*
+ If you really want your plugins to reload, add this to the very top of init.rb:
+
+ Dependencies.load_once_paths.delete(lib_path)
+
+* Allow config.to_prepare to work, make the dispatcher safe to 're require'. *Michael Koziarski, Nicholas Seckar*
+
+* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 *fearoffish*
+
+* Fix Webrick Daemon dispatching bug regarding a bad current working directory. Closes #4899 *Rick Olson*
+
+* Make config.plugins affect the load path and the dependencies system. Allows you to control plugin loading order, and keep disabled plugins off the load path. *James Adam*
+
+* Don't generate a components directory in new Rails apps. *Jeremy Kemper*
+
+* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) *David Heinemeier Hansson*
+
+* Added one-letter aliases for the three default environments to script/console, so script/console p will load the production environment (t for test, d for development) *David Heinemeier Hansson*
+
+* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment *David Heinemeier Hansson*
+
+* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. *Nicholas Seckar*
+
+* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) *David Heinemeier Hansson*
+
+* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available *David Heinemeier Hansson*
+
+* Update to Prototype and script.aculo.us [5579]. *Thomas Fuchs*
+
+* Made script/server work with -e and -d when using Mongrel *David Heinemeier Hansson*
+
+* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again *Thomas Fuchs*
+
+* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action *Tobias Lütke*
+
+* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 *fearoffish*
+
+* Update to Prototype 1.5.0_rc2. *Sam Stephenson*
+
+* Add grep-based fallback to reaper, to work in pidless setups *Jamis Buck*
+
+* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished. *Jeremy Kemper*
+
+* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. *Nicholas Seckar*
+
+* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT. #6584 *jchris*
+
+* Remove temporary crutch to help ApplicationController be unloaded. Closes #6496. *Nicholas Seckar*
+
+* scaffold_resource generator uses _path named routes and head instead of render :nothing => true. #6545 *Josh Susser*
+
+* Generator can show diff on file collision to help you decide whether to skip or overwrite. #6364 *jeffw, Jeremy Kemper*
+
+* Generated directories are recursively svn added, like mkdir -p. #6416 *NeilW*
+
+* resource and scaffold_resource generators add a restful route to config/routes.rb *Jeremy Kemper*
+
+* Revert environment changes for autoload_paths. *Michael Koziarski*
+
+* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions *Thomas Fuchs*
+
+* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests *Michael Koziarski*
+
+* Added option to script/process/spawner of specifying the binding address #5133 *Dee Zsombor*
+
+* Update environment.rb comments to include config.autoload_paths. Closes #6478 *caio*
+
+* Update scaffold to use new form_tag block functionality. Closes #6480. *Bob Silva*
+
+* Plugin generator: check for class collisions. #4833 *vinbarnes@gmail.com*
+
+* Mailer generator: handle mailers in modules, set mime_version in unit test. *Jeremy Kemper*
+
+* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. *Michael Koziarski*
+
+* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
+
+ config.plugins = %w[ routing_navigator simply_helpful ]
+
+* Clean up html on included error pages. *Tim Lucas*
+
+* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language *David Heinemeier Hansson*
+
+* Update to latest Prototype and script.aculo.us trunk versions *Thomas Fuchs*
+
+* PostgreSQL: db:test:purge closes open database connections first. #6236 *alex*
+
+* Fixed test:uncommitted on Windows (backslash issue) #4999 *paul@paulbutcher.com*
+
+* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 *Bob Silva*
+
+* Fixed rename_table on SQLite tables with indexes defined #5942 *brandon@opensoul.org*
+
+* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations *David Heinemeier Hansson*
+
+* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples:
+
+ ./script/generate model post title:string created_on:date body:text published:boolean
+
+* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined *David Heinemeier Hansson*
+
+* script/runner can run files, pass on arguments, and be used as a shebang. #6286 *Tuxie, dlpond*
+ #!/usr/bin/env /path/to/my/app/script/runner
+ # Example: just start using your models as if you are in script/console
+ Product.find(:all).each { |product| product.check_inventory }
+
+* Look for rake tasks in plugin subdirs. #6259 *obrie*
+
+* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb *David Heinemeier Hansson*
+
+* Updated prototype.js to 1.5.0_rc1 with latest fixes. *Rick Olson*
+
+ - XPATH support
+ - Make Form.getElements() return elements in the correct order
+ - fix broken Form.serialize return
+
+* session_migration generator adds an index on updated_at. #6207 *grg*
+
+* script/server creates the tmp/pids directory. #6204 *jonathan*
+
+* Fix script/console --sandbox for internal transactions changes. #5738 *Chris McGrath, charles.gerungan@gmail.com*
+
+* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
+ documented in initializer.rb, the workaround is:
+
+ config.autoload_paths += Dir*RAILS_ROOT + '/app/models/*/'*
+
+ References #6031. *Nicholas Seckar*
+
+* Update to script.aculo.us 1.6.3 *Thomas Fuchs*
+
+* Update to Prototype 1.5.0_rc1 *sam*
+
+* Formally Deprecate the old rake tasks. *Michael Koziarski*
+
+* Thoroughly test the FCGI dispatcher. #5970 *Kevin Clark*
+
+* Remove Dir.chdir in the Webrick DispatchServlet#initialize method. Fix bad path errors when trying to load config/routes.rb. *Rick Olson*
+
+* Tighten rescue clauses. #5985 *james@grayproductions.net*
+
+* Cleaning up tests. *Kevin Clark, Jeremy Kemper*
+
+* Add Dependencies.load_once_paths. *Nicholas Seckar*
+
+* Updated to script.aculo.us 1.6.2 *Thomas Fuchs*
+
+* Assign Routing.controller_paths; fix script/about and rails info controller. *Nicholas Seckar*
+
+* Don't warn dispatcher of Reloadable deprecations. *Nicholas Seckar*
+
+* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base *Rick Olson*
+
+ ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed.
+ ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback.
+
+* Add missing mock directories from the autoload_paths configuration. *Rick Olson*
+
+* Nested controller scaffolding also nests the generated layout. *iain d broadfoot*
+
+* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. *Nicholas Seckar*
+
+* Add autoload_paths support to Initializer. *Nicholas Seckar*
+
+* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. *Rick Olson*
+
+* Clarify usage of script/plugin source. Closes #5344. *James Adam*
+
+* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. *Nicholas Seckar*
+
+* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279. *Rick Olson*
+
+* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 *mats@imediatec.co.uk*
+
+* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 *Shugo Maeda*
+
+* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. *Rick Olson*
+
+* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. *Jamis Buck*
+
+* Fix script/plugin about so it uses about.yml and not meta.yml. *James Adam*
+
+* Dispatcher processes rescued actions with the same controller that processed the request. #4625 *sd@notso.net*
+
+* rails -d frontbase to create a new project with a frontbase database.yml. #4945 *mlaster@metavillage.com*
+
+* Ensure the logger is initialized. #5629 *mike@clarkware.com*
+
+* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples:
+
+ spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available
+ spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI
+ spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel
+ spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available
+ spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
+
+ Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster.
+
+* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 *evan@protest.net*
+
+* db:test:clone should remove existing tables before reloading the schema. #5607 *sveit@tradeharbor.com*
+
+* Fixed migration generation for class names like ACLController #5197 *brad@madriska.com*
+
+* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi@drecom.co.jp]. Examples:
+
+ irb(#<TopController:0x40822a68>):002:0> show_source_list
+ 0001 class TopController < ApplicationController
+ 0002 def show
+ 0003-> breakpoint
+ 0004 end
+ 0005
+ 0006 def index
+ 0007 end
+ 0008
+ => "/path/to/rails/root/app/controllers/top_controller.rb"
+
+ irb(#<TopController:0x40822a68>):004:0> show_call_stack 3
+ vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+ vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+ app/controllers/top_controller.rb:3:in `show'
+ => "/path/to/rails/root/app/controllers/top_controller.rb:3"
+
+* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 *nils@alumni.rice.edu*
+
+* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 *mike@clarkware.com*
+
+* Don't assume Active Record is available. #5497 *bob@sporkmonger.com*
+
+* Mongrel: script/server works on Win32. #5499 *jeremydurham@gmail.com*
+
+* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. *Nick Sieger*
+
+* Mongrel support for script/server. #5475 *jeremydurham@gmail.com*
+
+* Fix script/plugin so it doesn't barf on invalid URLs *Rick Olson*
+
+* Fix plugin install bug at dir with space. (closes #5359) *Yoshimasa NIWA*
+
+* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. *Rick Olson*
+
+* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 *takiuchi@drecom.co.jp*
+
+* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 *elliot@townx.org*
+
+* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. *Jamis Buck*
+
+* Make "script/plugin install" work with svn+ssh URLs. *Sam Stephenson*
+
+* Added lib/ to the directories that will get application docs generated *David Heinemeier Hansson*
+
+* Add observer generator. Closes #5167. *François Beausoleil*
+
+* Session migration generator obeys pluralize_table_names. #5145 *James Adam*
+
+* rake test:recent understands subdirectories. #2925 *jerrett@bravenet.com*
+
+* The app generator detects the XAMPP package's MySQL socket location. #3832 *elliot@townx.org*
+
+* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 *rcoder, rails-bug@owl.me.uk*
+
+* Distinguish the spawners for different processes *David Heinemeier Hansson*
+
+* Added -n/--process to script/process/spawner name the process pid (default is dispatch) *David Heinemeier Hansson*
+
+* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) *Julian Tarkhanov*
+
+* Replace Ruby's deprecated append_features in favor of included. *Marcel Molina Jr.*
+
+* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids *David Heinemeier Hansson*
+
+* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids *David Heinemeier Hansson*
+
+
+## 1.2.3 (March 12th, 2007) ##
+
+* Ruby 1.8.6 compatibility
+
+* Windows: include MinGW in RUBY_PLATFORM check. #2982 *okkez000@gmail.com, Kaspar Schiess*
+
+* Stop swallowing errors during rake test *Michael Koziarski*
+
+
+## 1.2.2 (February 5th, 2007) ##
+
+* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ *Chad Fowler*
+
+* Require the dispatcher for Rails::Configuration#to_prepare. *Rick Olson*
+
+
+## 1.2.1 (January 16th, 2007) ##
+
+* Updated to Active Record 1.15.1, Action Pack 1.13.1, Action Mailer 1.3.1, Action Web Service 1.2.1
+
+
+## 1.2.0 (January 16th, 2007) ##
+
+* Update to Prototype 1.5.0. *Sam Stephenson*
+
+* Generator: use destination path for diff tempfiles. #7015 *alfeld*
+
+* Fixed that webrick would strip leading newlines and hang connection #4156 *psross*
+
+* Ensure plugins are in the Dependencies.load_once_paths collection by default. *Rick Olson*
+ If you really want your plugins to reload, add this to the very top of init.rb:
+
+ Dependencies.load_once_paths.delete(lib_path)
+
+* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 *fearoffish*
+
+* Fix Webrick Daemon dispatching bug regarding a bad current working directory. Closes #4899 *Rick Olson*
+
+* Make config.plugins affect the load path and the dependencies system. Allows you to control plugin loading order, and keep disabled plugins off the load path. *James Adam*
+
+* Don't generate a components directory in new Rails apps. *Jeremy Kemper*
+
+* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) *David Heinemeier Hansson*
+
+* Deprecated the name route "root" as it'll be used as a shortcut for map.connect '' in Rails 2.0 *David Heinemeier Hansson*
+
+* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment *David Heinemeier Hansson*
+
+* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. *Nicholas Seckar*
+
+* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) *David Heinemeier Hansson*
+
+* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available *David Heinemeier Hansson*
+
+* Update to Prototype and script.aculo.us [5579]. *Sam Stephenson, Thomas Fuchs*
+
+* Made script/server work with -e and -d when using Mongrel *David Heinemeier Hansson*
+
+* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action *Tobias Lütke*
+
+* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 *fearoffish*
+
+* Add grep-based fallback to reaper, to work in pidless setups *Jamis Buck*
+
+* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished. *Jeremy Kemper*
+
+* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. *Nicholas Seckar*
+
+* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT. #6584 *jchris*
+
+* scaffold_resource generator uses _path named routes and head instead of render :nothing => true. #6545 *Josh Susser*
+
+* Generator can show diff on file collision to help you decide whether to skip or overwrite. #6364 *jeffw, Jeremy Kemper*
+
+* Generated directories are recursively svn added, like mkdir -p. #6416 *NeilW*
+
+* resource and scaffold_resource generators add a restful route to config/routes.rb *Jeremy Kemper*
+
+* Revert environment changes for autoload_paths. *Michael Koziarski*
+
+* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests *Michael Koziarski*
+
+* Added option to script/process/spawner of specifying the binding address #5133 *Dee Zsombor*
+
+* Update environment.rb comments to include config.autoload_paths. Closes #6478 *caio*
+
+* Update scaffold to use new form_tag block functionality. Closes #6480. *Bob Silva*
+
+* Plugin generator: check for class collisions. #4833 *vinbarnes@gmail.com*
+
+* Mailer generator: handle mailers in modules, set mime_version in unit test. *Jeremy Kemper*
+
+* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. *Michael Koziarski*
+
+* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
+
+ config.plugins = %w[ routing_navigator simply_helpful ]
+
+* Clean up html on included error pages. *Tim Lucas*
+
+* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language *David Heinemeier Hansson*
+
+* Update to latest Prototype and script.aculo.us trunk versions *Thomas Fuchs*
+
+* PostgreSQL: db:test:purge closes open database connections first. #6236 *alex*
+
+* Fixed test:uncommitted on Windows (backslash issue) #4999 *paul@paulbutcher.com*
+
+* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 *Bob Silva*
+
+* Fixed rename_table on SQLite tables with indexes defined #5942 *brandon@opensoul.org*
+
+* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations *David Heinemeier Hansson*
+
+* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples:
+
+ ./script/generate model post title:string created_on:date body:text published:boolean
+
+* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined *David Heinemeier Hansson*
+
+* script/runner can run files, pass on arguments, and be used as a shebang. #6286 *Tuxie, dlpond*
+ #!/usr/bin/env /path/to/my/app/script/runner
+ # Example: just start using your models as if you are in script/console
+ Product.find(:all).each { |product| product.check_inventory }
+
+* Look for rake tasks in plugin subdirs. #6259 *obrie*
+
+* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb *David Heinemeier Hansson*
+
+* session_migration generator adds an index on updated_at. #6207 *grg*
+
+* script/server creates the tmp/pids directory. #6204 *jonathan*
+
+* Fix script/console --sandbox for internal transactions changes. #5738 *Chris McGrath, charles.gerungan@gmail.com*
+
+* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
+ documented in initializer.rb, the workaround is:
+
+ config.autoload_paths += Dir*RAILS_ROOT + '/app/models/*/'*
+
+ References #6031. *Nicholas Seckar*
+
+* Update to script.aculo.us 1.6.3 *Thomas Fuchs*
+
+* Formally Deprecate the old rake tasks. *Michael Koziarski*
+
+* Thoroughly test the FCGI dispatcher. #5970 *Kevin Clark*
+
+* Remove Dir.chdir in the Webrick DispatchServlet#initialize method. Fix bad path errors when trying to load config/routes.rb. *Rick Olson*
+
+* Tighten rescue clauses. #5985 *james@grayproductions.net*
+
+* Cleaning up tests. *Kevin Clark, Jeremy Kemper*
+
+* Add Dependencies.load_once_paths. *Nicholas Seckar*
+
+* Assign Routing.controller_paths; fix script/about and rails info controller. *Nicholas Seckar*
+
+* Don't warn dispatcher of Reloadable deprecations. *Nicholas Seckar*
+
+* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base *Rick Olson*
+
+ ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed.
+ ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback.
+
+* Add missing mock directories from the autoload_paths configuration. *Rick Olson*
+
+* Nested controller scaffolding also nests the generated layout. *iain d broadfoot*
+
+* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. *Nicholas Seckar*
+
+* Add autoload_paths support to Initializer. *Nicholas Seckar*
+
+* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. *Rick Olson*
+
+* Clarify usage of script/plugin source. Closes #5344. *James Adam*
+
+* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. *Nicholas Seckar*
+
+* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279. *Rick Olson*
+
+* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 *mats@imediatec.co.uk*
+
+* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 *Shugo Maeda*
+
+* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. *Rick Olson*
+
+* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. *Jamis Buck*
+
+* Fix script/plugin about so it uses about.yml and not meta.yml. *James Adam*
+
+* Dispatcher processes rescued actions with the same controller that processed the request. #4625 *sd@notso.net*
+
+* rails -d frontbase to create a new project with a frontbase database.yml. #4945 *mlaster@metavillage.com*
+
+* Ensure the logger is initialized. #5629 *mike@clarkware.com*
+
+* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples:
+
+ spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available
+ spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI
+ spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel
+ spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available
+ spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds
+
+ Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster.
+
+* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 *evan@protest.net*
+
+* db:test:clone should remove existing tables before reloading the schema. #5607 *sveit@tradeharbor.com*
+
+* Fixed migration generation for class names like ACLController #5197 *brad@madriska.com*
+
+* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi@drecom.co.jp]. Examples:
+
+ irb(#<TopController:0x40822a68>):002:0> show_source_list
+ 0001 class TopController < ApplicationController
+ 0002 def show
+ 0003-> breakpoint
+ 0004 end
+ 0005
+ 0006 def index
+ 0007 end
+ 0008
+ => "/path/to/rails/root/app/controllers/top_controller.rb"
+
+ irb(#<TopController:0x40822a68>):004:0> show_call_stack 3
+ vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+ vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint'
+ app/controllers/top_controller.rb:3:in `show'
+ => "/path/to/rails/root/app/controllers/top_controller.rb:3"
+
+* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 *nils@alumni.rice.edu*
+
+* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 *mike@clarkware.com*
+
+* Don't assume Active Record is available. #5497 *bob@sporkmonger.com*
+
+* Mongrel: script/server works on Win32. #5499 *jeremydurham@gmail.com*
+
+* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. *Nick Sieger*
+
+* Mongrel support for script/server. #5475 *jeremydurham@gmail.com*
+
+* Fix script/plugin so it doesn't barf on invalid URLs *Rick Olson*
+
+* Fix plugin install bug at dir with space. (closes #5359) *Yoshimasa NIWA*
+
+* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. *Rick Olson*
+
+* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 *takiuchi@drecom.co.jp*
+
+* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 *elliot@townx.org*
+
+* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. *Jamis Buck*
+
+* Make "script/plugin install" work with svn+ssh URLs. *Sam Stephenson*
+
+* Added lib/ to the directories that will get application docs generated *David Heinemeier Hansson*
+
+* Add observer generator. Closes #5167. *François Beausoleil*
+
+* Session migration generator obeys pluralize_table_names. #5145 *James Adam*
+
+* rake test:recent understands subdirectories. #2925 *jerrett@bravenet.com*
+
+* The app generator detects the XAMPP package's MySQL socket location. #3832 *elliot@townx.org*
+
+* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 *rcoder, rails-bug@owl.me.uk*
+
+* Distinguish the spawners for different processes *David Heinemeier Hansson*
+
+* Added -n/--process to script/process/spawner name the process pid (default is dispatch) *David Heinemeier Hansson*
+
+* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) *Julian Tarkhanov*
+
+* Replace Ruby's deprecated append_features in favor of included. *Marcel Molina Jr.*
+
+* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids *David Heinemeier Hansson*
+
+* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids *David Heinemeier Hansson*
+
+
+## 1.1.6 (August 10th, 2006) ##
+
+* Additional security patch
+
+
+## 1.1.5 (August 8th, 2006) ##
+
+* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. #4857 *Alisdair McDiarmid*
+
+* Change the scaffolding layout to use yield rather than @content_for_layout. *Marcel Molina Jr.*
+
+* Includes critical security patch
+
+
+## 1.1.4 (June 29th, 2006) ##
+
+* Remove use of opts.on { |options[:name] } style hash assignment. References #4440. *headius@headius.com*
+
+* Updated to Action Pack 1.12.3, ActionWebService 1.1.4, ActionMailer 1.2.3
+
+
+## 1.1.3 (June 27th, 2006) ##
+
+* Updated to Active Record 1.14.3, Action Pack 1.12.2, ActionWebService 1.1.3, ActionMailer 1.2.2
+
+
+## 1.1.2 (April 9th, 2006) ##
+
+* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. Closes #4857. *Alisdair McDiarmid*
+
+* Change the scaffolding layout to use yield rather than @content_for_layout. *Marcel Molina Jr.*
+
+* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) *David Heinemeier Hansson*
+
+* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem *David Heinemeier Hansson*
+
+
+## 1.1.1 (April 6th, 2006) ##
+
+* Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) *ruben.nine@gmail.com*
+
+* Update to Prototype 1.5.0_rc0 *Sam Stephenson*
+
+* Fixed that the -r/--ruby path option of the rails command was not being respected #4549 *ryan.raaum@gmail.com*
+
+* Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html *David Heinemeier Hansson*
+
+* Fixed that rake clone_structure_to_test should quit on pgsql if the dump is unsuccesful #4585 *augustz@augustz.com*
+
+* Fixed that rails --version should have the return code of 0 (success) #4560 *blair@orcaware.com*
+
+* Install alias so Rails::InfoController is accessible at /rails_info. Closes #4546. *Nicholas Seckar*
+
+* Fixed that spawner should daemonize if running in repeat mode *David Heinemeier Hansson*
+
+* Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release *David Heinemeier Hansson*
+
+* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us *Sam Stephenson, Thomas Fuchs*
+
+* Use --simple-prompt instead of --prompt-mode simple for console compatibility with Windows/Ruby 1.8.2 #4532 *starr@starrnhorne.com*
+
+* Make Rails::VERSION implicitly loadable #4491. *Nicholas Seckar*
+
+* Fixed rake rails:freeze:gems #4518 *benji@silverinsanity.com*
+
+* Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with *David Heinemeier Hansson*
+
+* Added gem binding of apps generated through the rails command to the gems of they were generated with *Nicholas Seckar*
+
+* Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf *David Heinemeier Hansson*
+
+* Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf *David Heinemeier Hansson*
+
+* Avoid passing escapeHTML non-string in Rails' info controller *Nicholas Seckar*
+
+
+## 1.1.0 (March 27th, 2006) ##
+
+* Allow db:fixtures:load to load a subset of the applications fixtures. *Chad Fowler*
+
+ ex.
+
+ rake db:fixtures:load FIXTURES=customers,plans
+
+* Update to Prototype 1.5.0_pre1 *Sam Stephenson*
+
+* Update to script.aculo.us 1.6 *Thomas Fuchs*
+
+* Add an integration_test generator *Jamis Buck*
+
+* Make all ActionView helpers available in the console from the helper method for debugging purposes. n.b.: Only an 80% solution. Some stuff won't work, most will. *Marcel Molina Jr.*
+
+ ex.
+
+ >> puts helper.options_for_select([%w(a 1), %w(b 2), %w(c 3)])
+ <option value="1">a</option>
+ <option value="2">b</option>
+ <option value="3">c</option>
+ => nil
+
+* Replaced old session rake tasks with db:sessions:create to generate a migration, and db:sessions:clear to remove sessions. *Rick Olson*
+
+* Reject Ruby 1.8.3 when loading Rails; extract version checking code. *Chad Fowler*
+
+* Remove explicit loading of RailsInfo and RailsInfoController. *Nicholas Seckar*
+
+* Move RailsInfo and RailsInfoController to Rails::Info and Rails::InfoController. *Nicholas Seckar*
+
+* Extend load path with Railties' builtin directory to make adding support code easy. *Nicholas Seckar*
+
+* Fix the rails_info controller by explicitly loading it, and marking it as not reloadable. *Nicholas Seckar*
+
+* Fixed rails:freeze:gems for Windows #3274 *paul@paulbutcher.com*
+
+* Added 'port open?' check to the spawner when running in repeat mode so we don't needlessly boot the dispatcher if the port is already in use anyway #4089 *guy.naor@famundo.com*
+
+* Add verification to generated scaffolds, don't allow get for unsafe actions *Michael Koziarski*
+
+* Don't replace application.js in public/javascripts if it already exists *Cody Fauser*
+
+* Change test:uncommitted to delay execution of `svn status` by using internal Rake API's. *Nicholas Seckar*
+
+* Use require_library_or_gem to load rake in commands/server.rb. Closes #4205. *rob.rasmussen@gmail.com*
+
+* Use the Rake API instead of shelling out to create the tmp directory in commands/server.rb. *Chad Fowler*
+
+* Added a backtrace to the evil WSOD (White Screen of Death). Closes #4073. TODO: Clearer exceptions *Rick Olson*
+
+* Added tracking of database and framework versions in script/about #4088 *charles.gerungan@gmail.com/Rick Olson*
+
+* Added public/javascripts/application.js as a sample since it'll automatically be included in javascript_include_tag :defaults *David Heinemeier Hansson*
+
+* Added socket cleanup for lighttpd, both before and after *David Heinemeier Hansson*
+
+* Added automatic creation of tmp/ when running script/server *David Heinemeier Hansson*
+
+* Added silence_stream that'll work on both STDERR or STDOUT or any other stream and deprecated silence_stderr in the process *David Heinemeier Hansson*
+
+* Added reload! method to script/console to reload all models and others that include Reloadable without quitting the console #4056 *esad@esse.at*
+
+* Added that rake rails:freeze:edge will now just export all the contents of the frameworks instead of just lib, so stuff like rails:update:scripts, rails:update:javascripts, and script/server on lighttpd still just works #4047 *David Heinemeier Hansson*
+
+* Added fix for upload problems with lighttpd from Safari/IE to config/lighttpd.conf #3999 *Thijs van der Vossen*
+
+* Added test:uncommitted to test changes since last checkin to Subversion #4035 *technomancy@gmail.com*
+
+* Help script/about print the correct svn revision when in a non-English locale. #4026 *babie7a0@ybb.ne.jp*
+
+* Add 'app' accessor to script/console as an instance of Integration::Session *Jamis Buck*
+
+* Generator::Base#usage takes an optional message argument which defaults to Generator::Base#usage_message. *Jeremy Kemper*
+
+* Remove the extraneous AR::Base.threaded_connections setting from the webrick server. *Jeremy Kemper*
+
+* Add integration test support to app generation and testing *Jamis Buck*
+
+* Added namespaces to all tasks, so for example load_fixtures is now db:fixtures:load. All the old task names are still valid, they just point to the new namespaced names. "rake -T" will only show the namespaced ones, though *David Heinemeier Hansson*
+
+* CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by default instead of :sql. This means that we'll assume you want to live in the world of db/schema.rb where the grass is green and the girls are pretty. If your schema contains un-dumpable elements, such as constraints or database-specific column types, you just got an invitation to either 1) patch the dumper to include foreign key support, 2) stop being db specific, or 3) just change the default in config/environment.rb to config.active_record.schema_format = :sql -- we even include an example for that on new Rails skeletons now. Brought to you by the federation of opinionated framework builders! *David Heinemeier Hansson*
+
+* Added -r/--repeat option to script/process/spawner that offers the same loop protection as the spinner did. This deprecates the script/process/spinner, so it's no longer included in the default Rails skeleton, but still available for backwards compatibility #3461 *ror@andreas-s.net*
+
+* Added collision option to template generation in generators #3329 [anna@wota.jp]. Examples:
+
+ m.template "stuff.config" , "config/stuff.config" , :collision => :skip
+ m.template "auto-stamping", "config/generator.log", :collision => :force
+
+* Added more information to script/plugin's doings to ease debugging #3755 *Rick Olson*
+
+* Changed the default configuration for lighttpd to use tmp/sockets instead of log/ for the FastCGI sockets *David Heinemeier Hansson*
+
+* Added a default configuration of the FileStore for fragment caching if tmp/cache is available, which makes action/fragment caching ready to use out of the box with no additional configuration *David Heinemeier Hansson*
+
+* Changed the default session configuration to place sessions in tmp/sessions, if that directory is available, instead of /tmp (this essentially means a goodbye to 9/10 White Screen of Death errors and should have web hosting firms around the world cheering) *David Heinemeier Hansson*
+
+* Added tmp/sessions, tmp/cache, and tmp/sockets as default directories in the Rails skeleton *David Heinemeier Hansson*
+
+* Added that script/generate model will now automatically create a migration file for the model created. This can be turned off by calling the generator with --skip-migration *David Heinemeier Hansson*
+
+* Added -d/--database option to the rails command, so you can do "rails --database=sqlite2 myapp" to start a new application preconfigured to use SQLite2 as the database. Removed the configuration examples from SQLite and PostgreSQL from the default MySQL configuration *David Heinemeier Hansson*
+
+* Allow script/server -c /path/to/lighttpd.conf *Jeremy Kemper*
+
+* Remove hardcoded path to reaper script in script/server *Jeremy Kemper*
+
+* Update script.aculo.us to V1.5.3 *Thomas Fuchs*
+
+* Added SIGTRAP signal handler to RailsFCGIHandler that'll force the process into a breakpoint after the next request. This breakpoint can then be caught with script/breakpointer and give you access to the Ruby image inside that process. Useful for debugging memory leaks among other things *David Heinemeier Hansson*
+
+* Changed default lighttpd.conf to use CWD from lighttpd 1.4.10 that allows the same configuration to be used for both detach and not. Also ensured that auto-repeaping of FCGIs only happens when lighttpd is not detached. *David Heinemeier Hansson*
+
+* Added Configuration#after_initialize for registering a block which gets called after the framework is fully initialized. Useful for things like per-environment configuration of plugins. *Michael Koziarski*
+
+* Added check for RAILS_FRAMEWORK_ROOT constant that allows the Rails framework to be found in a different place than vendor/rails. Should be set in boot.rb. *David Heinemeier Hansson*
+
+* Fixed that static requests could unlock the mutex guarding dynamic requests in the WEBrick servlet #3433 *tom@craz8.com*
+
+* Fixed documentation tasks to work with Rake 0.7.0 #3563 *kazuhiko@fdiary.net*
+
+* Update to Prototype 1.5.0_pre0 *Sam Stephenson*
+
+* Sort the list of plugins so we load in a consistent order *Rick Olson*
+
+* Show usage when script/plugin is called without arguments *tom@craz8.com*
+
+* Corrected problems with plugin loader where plugins set 'name' incorrectly #3297 *anna@wota.jp*
+
+* Make migration generator only report on exact duplicate names, not partial dupliate names. #3442 *jeremy@planetargon.com Marcel Molina Jr.*
+
+* Fix typo in mailer generator USAGE. #3458 *chriztian.steinmeier@gmail.com*
+
+* Ignore version mismatch between pg_dump and the database server. #3457 *Simon Stapleton*
+
+* Reap FCGI processes after lighttpd exits. *Sam Stephenson*
+
+* Honor ActiveRecord::Base.pluralize_table_names when creating and destroying session store table. #3204. *rails@bencurtis.com, Marcel Molina Jr.*
+
+## 1.0.0 (December 13th, 2005) ##
+
+* Update instructions on how to find and install generators. #3172. *Chad Fowler*
+
+* Generator looks in vendor/generators also. *Chad Fowler*
+
+* Generator copies files in binary mode. #3156 *minimudboy@gmail.com*
+
+* Add builtin/ to the gemspec. Closes #3047. *Nicholas Seckar, Sam Stephenson*
+
+* Add install.rb file to plugin generation which is loaded, if it exists, when you install a plugin. *Marcel Molina Jr.*
+
+* Run initialize_logger in script/lighttpd to ensure the log file exists before tailing it. *Sam Stephenson*
+
+* Make load_fixtures include csv fixtures. #3053. *me@mdaines.com*
+
+* Fix freeze_gems so that the latest rails version is dumped by default. *Nicholas Seckar*
+
+* script/plugin: handle root paths and plugin names which contain spaces. #2995 *justin@aspect.net*
+
+* Model generator: correct relative path to test_helper in unit test. *Jeremy Kemper*
+
+* Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. #2931. *Blair Zajac*
+
+* Have the lighttpd server script report the actual ip to which the server is bound. #2903. *Adam*
+
+* Add plugin library directories to the load path after the lib directory so that libraries in the lib directory get precedence. #2910. *James Adam*
+
+* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. *anonymous*
+
+* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 *Dreamer3*
+
+* Introducing the session_migration generator. Creates an add_session_table migration. Allows generator to specify migrations directory. #2958, #2960 *Rick Olson*
+
+* script/console uses RAILS_ENV environment variable if present. #2932 [Blair Zajac <blair@orcaware.com>
+
+* Windows: eliminate the socket option in database.yml. #2924 *Wayne Vucenic <waynev@gmail.com>*
+
+* Eliminate nil from newly generated logfiles. #2927 *Blair Zajac <blair@orcaware.com>*
+
+* Rename Version constant to VERSION. #2802 *Marcel Molina Jr.*
+
+* Eliminate Subversion dependencies in scripts/plugin. Correct install options. Introduce --force option to reinstall a plugin. Remove useless --long option for list. Use --quiet to quiet the download output and --revision to update to a specific svn revision. #2842 *Chad Fowler, Rick Olson*
+
+* SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment. #2846 *Rick Olson*
+
+* Make sure that legacy db tasks also reference :database for SQLite #2830 *kazuhiko@fdiary.net*
+
+* Pass __FILE__ when evaluating plugins' init.rb. #2817 *James Adam*
+
+* Better svn status matching for generators. #2814 *François Beausoleil <francois.beausoleil@gmail.com>, Blair Zajac <blair@orcaware.com>*
+
+* Don't reload routes until plugins have been loaded so they have a chance to extend the routing capabilities *David Heinemeier Hansson*
+
+* Don't detach or fork for script/server tailing *Nicholas Seckar*
+
+* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path *David Heinemeier Hansson*
+
+* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [David Heinemeier Hansson] #2660
+
+* Correct versioning in :freeze_gems Rake task. #2778 *Jakob Skjerning, Jeremy Kemper*
+
+* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. *Sam Stephenson*
+
+
+## 0.14.3 (RC4) (November 7th, 2005) ##
+
+## Add 'add_new_scripts' rake task for adding new rails scripts to script/ Jamis Buck ##
+
+* Remove bogus hyphen from script/process/reaper calls to 'ps'. #2767 *anonymous*
+
+* Copy lighttpd.conf when it is first needed, instead of on app creation *Jamis Buck*
+
+* Use require_library_or_gem 'fcgi' in script/server *Sam Stephenson*
+
+* Added default lighttpd config in config/lighttpd.conf and added a default runner for lighttpd in script/server (works like script/server, but using lighttpd and FastCGI). It will use lighttpd if available, otherwise WEBrick. You can force either or using 'script/server lighttpd' or 'script/server webrick' *David Heinemeier Hansson*
+
+* New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins']. *Jeremy Kemper*
+
+* Plugins are discovered in nested paths, so you can organize your plugins directory as you like. *Jeremy Kemper*
+
+* Refactor load_plugin from load_plugins. #2757 *alex.r.moon@gmail.com*
+
+* Make use of silence_stderr in script/lighttpd, script/plugin, and Rails::Info *Sam Stephenson*
+
+* Enable HTTP installation of plugins when svn isn't avaialable. Closes #2661. *Chad Fowler*
+
+* Added script/about to display formatted Rails::Info output *Sam Stephenson*
+
+* Added Rails::Info to catalog assorted information about a Rails application's environment *Sam Stephenson*
+
+* Tail the logfile when running script/server lighttpd in the foreground *Sam Stephenson*
+
+* Try to guess the port number from config/lighttpd.conf in script/server lighttpd *Sam Stephenson*
+
+* Don't reap spawn-fcgi. #2727 *matthew@walker.wattle.id.au*
+
+* Reaper knows how to find processes even if the dispatch path is very long. #2711 *matthew@walker.wattle.id.au*
+
+* Make fcgi handler respond to TERM signals with an explicit exit *Jamis Buck*
+
+* Added demonstration of fixture use to the test case generated by the model generator *David Heinemeier Hansson*
+
+* If specified, pass PostgreSQL client character encoding to createdb. #2703 *Kazuhiko <kazuhiko@fdiary.net>*
+
+* Catch CGI multipart parse errors. Wrap dispatcher internals in a failsafe response handler. *Jeremy Kemper*
+
+* The freeze_gems Rake task accepts the VERSION environment variable to decide which version of Rails to pull into vendor/rails. *Chad Fowler, Jeremy Kemper*
+
+* Removed script.aculo.us.js, builder.js and slider.js (preperation for move of scriptaculous extensions to plugins, core scriptaculous will remain in Railties) *Thomas Fuchs*
+
+* The freeze_edge Rake task does smarter svn detection and can export a specific revision by passing the REVISION environment variable. For example: rake freeze_edge REVISION=1234. #2663 *Rick Olson*
+
+* Comment database.yml and include PostgreSQL and SQLite examples. *Jeremy Kemper*
+
+* Improve script/plugin on Windows. #2646 *Chad Fowler*
+
+* The *_plugindoc Rake tasks look deeper into the plugins' lib directories. #2652 *bellis@deepthought.org*
+
+* The PostgreSQL :db_structure_dump Rake task limits its dump to the schema search path in database.yml. *Anatol Pomozov <anatol.pomozov@gmail.com>*
+
+* Add task to generate rdoc for all installed plugins. *Marcel Molina Jr.*
+
+* Update script.aculo.us to V1.5_rc4 *Thomas Fuchs*
+
+* Add default Mac + DarwinPorts MySQL socket locations to the app generator. *Jeremy Kemper*
+
+* Migrations may be destroyed: script/destroy migration foo. #2635 *Charles M. Gerungan <charles.gerungan@gmail.com>, Jamis Buck, Jeremy Kemper*
+
+* Added that plugins can carry generators and that generator stub files can be created along with new plugins using script/generate plugin <name> --with-generator *David Heinemeier Hansson*
+
+* Removed app/apis as a default empty dir since its automatically created when using script/generate web_service *David Heinemeier Hansson*
+
+* Added script/plugin to manage plugins (install, remove, list, etc) *Ryan Tomayko*
+
+* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) *David Heinemeier Hansson*
+
+* Added plugin generator to create a stub structure for a new plugin in vendor/plugins (see "script/generate plugin" for help) *David Heinemeier Hansson*
+
+* Fixed scaffold generator when started with only 1 parameter #2609 *self@mattmower.com*
+
+* rake should run functional tests even if the unit tests have failures *Jim Weirich*
+
+* Back off cleanpath to be symlink friendly. Closes #2533 *Nicholas Seckar*
+
+* Load rake task files in alphabetical order so you can build dependencies and count on them #2554 *Blair Zajac*
+
+
+## 0.14.2 (RC3) (October 26th, 2005) ##
+
+* Constants set in the development/test/production environment file are set in Object
+
+* Scaffold generator pays attention to the controller name. #2562 *self@mattmower.com*
+
+* Include tasks from vendor/plugins/*/tasks in the Rakefile #2545 *Rick Olson*
+
+
+## 0.14.1 (RC2) (October 19th, 2005) ##
+
+* Don't clean RAILS_ROOT on windows
+
+* Remove trailing '/' from RAILS_ROOT *Nicholas Seckar*
+
+* Upgraded to Active Record 1.12.1 and Action Pack 1.10.1
+
+
+## 0.14.0 (RC1) (October 16th, 2005) ##
+
+* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators *Tobias Lütke*
+
+* Fix rake dev and related commands *Nicholas Seckar*
+
+* The rails command tries to deduce your MySQL socket by running `mysql_config
+ --socket`. If it fails, default to /path/to/your/mysql.sock
+
+* Made the rails command use the application name for database names in the tailored database.yml file. Example: "rails ~/projects/blog" will use "blog_development" instead of "rails_development". *Florian Weber*
+
+* Added Rails framework freezing tasks: freeze_gems (freeze to current gems), freeze_edge (freeze to Rails SVN trunk), unfreeze_rails (float with newest gems on system)
+
+* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. *Tobias Lütke*
+
+* Added cleaning of RAILS_ROOT to useless elements such as '../non-dot-dot/'. Provides cleaner backtraces and error messages. *Nicholas Seckar*
+
+* Made the instantiated/transactional fixtures settings be controlled through Rails::Initializer. Transactional and non-instantiated fixtures are default from now on. *Florian Weber*
+
+* Support using different database adapters for development and test with ActiveRecord::Base.schema_format = :ruby *Sam Stephenson*
+
+* Make webrick work with session(:off)
+
+* Add --version, -v option to the Rails command. Closes #1840. *stancell*
+
+* Update Prototype to V1.4.0_pre11, script.aculo.us to V1.5_rc3 [2504] and fix the rails generator to include the new .js files *Thomas Fuchs*
+
+* Make the generator skip a file if it already exists and is identical to the new file.
+
+* Add experimental plugin support #2335
+
+* Made Rakefile aware of new .js files in script.aculo.us *Thomas Fuchs*
+
+* Make table_name and controller_name in generators honor AR::Base.pluralize_table_names. #1216 #2213 *kazuhiko@fdiary.net*
+
+* Clearly label functional and unit tests in rake stats output. #2297 *lasse.koskela@gmail.com*
+
+* Make the migration generator only check files ending in *.rb when calculating the next file name #2317 *Chad Fowler*
+
+* Added prevention of duplicate migrations from the generator #2240 *François Beausoleil*
+
+* Add db_schema_dump and db_schema_import rake tasks to work with the new ActiveRecord::SchemaDumper (for dumping a schema to and reading a schema from a ruby file).
+
+* Reformed all the config/environments/* files to conform to the new Rails::Configuration approach. Fully backwards compatible.
+
+* Added create_sessions_table, drop_sessions_table, and purge_sessions_table as rake tasks for databases that supports migrations (MySQL, PostgreSQL, SQLite) to get a table for use with CGI::Session::ActiveRecordStore
+
+* Added dump of schema version to the db_structure_dump task for databases that support migrations #1835 *Rick Olson*
+
+* Fixed script/profiler for Ruby 1.8.2 #1863 *Rick Olson*
+
+* Fixed clone_structure_to_test task for SQLite #1864 *jon@burningbush.us*
+
+* Added -m/--mime-types option to the WEBrick server, so you can specify a Apache-style mime.types file to load #2059 *ask@develooper.com*
+
+* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 *Kevin Clark*
+
+* Added -c/--charset option to WEBrick server, so you can specify a default charset (which without changes is UTF-8) #2084 *wejn@box.cz*
+
+* Make the default stats task extendable by modifying the STATS_DIRECTORIES constant
+
+* Allow the selected environment to define RAILS_DEFAULT_LOGGER, and have Rails::Initializer use it if it exists.
+
+* Moved all the shared tasks from Rakefile into Rails, so that the Rakefile is empty and doesn't require updating.
+
+* Added Rails::Initializer and Rails::Configuration to abstract all of the common setup out of config/environment.rb (uses config/boot.rb to bootstrap the initializer and paths)
+
+* Fixed the scaffold generator to fail right away if the database isn't accessible instead of in mid-air #1169 *Chad Fowler*
+
+* Corrected project-local generator location in scripts.rb #2010 *Michael Schuerig*
+
+* Don't require the environment just to clear the logs #2093 *Scott Barron*
+
+* Make the default rakefile read *.rake files from config/tasks (for easy extension of the rakefile by e.g. generators)
+
+* Only load breakpoint in development mode and when BREAKPOINT_SERVER_PORT is defined.
+
+* Allow the --toggle-spin switch on process/reaper to be negated
+
+* Replace render_partial with render :partial in scaffold generator *Nicholas Seckar*
+
+* Added -w flag to ps in process/reaper #1934 *Scott Barron*
+
+* Allow ERb in the database.yml file (just like with fixtures), so you can pull out the database configuration in environment variables #1822 *Duane Johnson*
+
+* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help.
+
+* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 *Marcel Molina Jr.*
+
+* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 *Michael Schubert*
+
+* Dropped the 'immediate close-down' of FCGI processes since it didn't work consistently and produced bad responses when it didn't. So now a TERM ensures exit after the next request (just as if the process is handling a request when it receives the signal). This means that you'll have to 'nudge' all FCGI processes with a request in order to ensure that they have all reloaded. This can be done by something like ./script/process/repear --nudge 'http://www.myapp.com' --instances 10, which will load the myapp site 10 times (and thus hit all of the 10 FCGI processes once, enough to shut down).
+
+
+## 0.13.1 (11 July, 2005) ##
+
+* Look for app-specific generators in RAILS_ROOT/generators rather than the clunky old RAILS_ROOT/script/generators. Nobody really uses this feature except for the unit tests, so it's a negligible-impact change. If you want to work with third-party generators, drop them in ~/.rails/generators or simply install gems.
+
+* Fixed that each request with the WEBrick adapter would open a new database connection #1685 *Sam Stephenson*
+
+* Added support for SQL Server in the database rake tasks #1652 [Ken Barker] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
+
+* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
+
+* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 *court3nay*
+
+* Improved documentation for environment config files #1625 *court3nay*
+
+
+## 0.13.0 (6 July, 2005) ##
+
+* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
+
+* Added migration generator: ./script/generate migration add_system_settings
+
+* Added "migrate" as rake task to execute all the pending migrations from db/migrate
+
+* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 *Marcel Molina Jr.*
+
+* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 *Michael Koziarski*
+
+* SIGTERM also gracefully exits dispatch.fcgi. Ignore SIGUSR1 on Windows.
+
+* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi *Stefan Kaes*
+
+* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
+
+* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
+
+* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 *Michael Schubert*
+
+* ActionMailer::Base subclasses are reloaded with the other rails components #1262
+
+* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false)
+
+* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 *Scott Barron*
+
+* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 *Michael Koziarski*
+
+* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 *Nicholas Seckar*
+
+* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 *Nicholas Seckar*
+
+* Added -e/--environment option to script/runner #1408 *François Beausoleil*
+
+* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id]. #1367
+
+* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 *Caleb Tennis*
+
+* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. *Jamis Buck*
+
+* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. *Jamis Buck*
+
+* Added console --profile for profiling an IRB session #1154 *Jeremy Kemper*
+
+* Changed console_sandbox into console --sandbox #1154 *Jeremy Kemper*
+
+
+## 0.12.1 (20th April, 2005) ##
+
+* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1
+
+
+## 0.12.0 (19th April, 2005) ##
+
+* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 *rails@cogentdude.com*
+
+* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples:
+
+ # runs the one statement 10 times
+ script/benchmarker 10 'Person.expensive_method(10)'
+
+ # pits the two statements against each other with 50 runs each
+ script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)'
+
+* Added script/profiler to easily profile a single statement from within the environment. Examples:
+
+ script/profiler 'Person.expensive_method(10)'
+ script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times
+
+* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 *Lucas Carlson*
+
+* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 *k@v2studio.com*
+
+* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 *pburleson*
+
+* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
+
+
+## 0.11.1 (27th March, 2005) ##
+
+* Fixed the dispatch.fcgi use of a logger
+
+* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3
+
+
+## 0.11.0 (22th March, 2005) ##
+
+* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 *Nicholas Seckar*
+
+* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 *dblack/Nicholas Seckar*
+
+* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
+
+* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 *Tobias Lütke*
+
+* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 *Nicholas Seckar*
+
+* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 *Nicholas Seckar/Tobias Lütke*
+
+* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
+
+ ./script/runner 'ReminderService.deliver'
+ ./script/runner 'Mailer.receive(STDIN.read)'
+
+ This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code.
+
+* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 *dave@cherryville.org*
+
+* Fixed the Rakefile's interaction with postgresql to:
+
+ 1. Use PGPASSWORD and PGHOST in the environment to fix prompting for
+ passwords when connecting to a remote db and local socket connections.
+ 2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik]
+ 3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson]
+ 4. Use the port if available #875 [madrobby]
+
+* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2
+
+
+## 0.10.1 (7th March, 2005) ##
+
+* Fixed rake stats to ignore editor backup files like model.rb~ #791 *Sebastian Kanthak*
+
+* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 *Tobias Lütke*
+
+* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
+
+* Fixed that scaffold generator new template should use local variable instead of instance variable #778 *Dan Peterson*
+
+* Allow unit tests to run on a remote server for PostgreSQL #781 *adamm@galacticasoftware.com*
+
+* Added web_service generator (run ./script/generate web_service for help) #776 *Leon Bredt*
+
+* Added app/apis and components to code statistics report #729 *Scott Barron*
+
+* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 *Nicholas Seckar*
+
+* Fixed rails_generator to be usable without RubyGems #686 *Cristi BALAN*
+
+* Fixed -h/--help for generate and destroy generators #331
+
+* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log)
+
+* Fixed that association#count would produce invalid sql when called sequentialy #659 *kanis@comcard.de*
+
+* Fixed test/mocks/testing to the correct test/mocks/test #740
+
+* Added early failure if the Ruby version isn't 1.8.2 or above #735
+
+* Removed the obsolete -i/--index option from the WEBrick servlet #743
+
+* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1
+
+
+## 0.10.0 (24th February, 2005) ##
+
+* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 *Marcel Molina Jr.*
+
+* Fixed that script/server -d was broken so daemon mode couldn't be used #687 *Nicholas Seckar*
+
+* Upgraded to breakpoint 92 which fixes:
+
+ * overload IRB.parse_opts(), fixes #443
+ => breakpoints in tests work even when running them via rake
+ * untaint handlers, might fix an issue discussed on the Rails ML
+ * added verbose mode to breakpoint_client
+ * less noise caused by breakpoint_client by default
+ * ignored TerminateLineInput exception in signal handler
+ => quiet exit on Ctrl-C
+
+* Added support for independent components residing in /components. Example:
+
+ Controller: components/list/items_controller.rb
+ (holds a List::ItemsController class with uses_component_template_root called)
+
+ Model : components/list/item.rb
+ (namespace is still shared, so an Item model in app/models will take precedence)
+
+ Views : components/list/items/show.rhtml
+
+
+* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 *Jeremy Kemper*
+
+* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 *Jeremy Kemper*
+
+* Changed script/console to default to development environment and drop --no-inspect #650 *Jeremy Kemper*
+
+* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 *Jeremy Kemper*
+
+* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 *rasputnik*
+
+* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 *Jeremy Kemper*
+
+* Added Action Web Service as a new add-on framework for Action Pack *Leon Bredt*
+
+* Added Active Support as an independent utility and standard library extension bundle
+
+* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0
+
+
+## 0.9.5 (January 25th, 2005) ##
+
+* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require_dependency to do it.
+
+* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 *Florian Gross*
+
+* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 *rasputnik*
+
+* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 *brandt@kurowski.net*
+
+* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter
+
+* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload
+
+* Fixed that generate scaffold would produce bad functional tests
+
+* Fixed that FCGI can also display SyntaxErrors
+
+* Upgraded to Active Record 1.6.0, Action Pack 1.4.0
+
+
+## 0.9.4.1 (January 18th, 2005) ##
+
+* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 *Marcel Molina Jr.*
+
+* Fixed binding of caller #496 *Alexey*
+
+* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1
+
+
+## 0.9.4 (January 17th, 2005) ##
+
+* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller *Tobias Lütke*
+
+* Flipped code-to-test ratio around to be more readable #468 *Scott Baron*
+
+* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 *Lucas Carlson*
+
+* Fixed that auto reloading would some times not work or would reload the models twice #475 *Tobias Lütke*
+
+* Added rewrite rules to deal with caching to public/.htaccess
+
+* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
+
+* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
+ RAILS_ENV instead of just forcing development #424 *Tobias Lütke*
+
+* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 *Chris McGrath*
+
+* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
+
+* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes
+
+* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6
+
+
+## 0.9.3 (January 4th, 2005) ##
+
+* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416
+
+* Added automated rewriting of the shebang lines on installs through the gem rails command #379 *Manfred Stienstra*
+
+* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries
+ for functional testing.
+
+* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date.
+ It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) *Florian Gross*
+
+* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that
+ the environment options needs to change from:
+
+ Before in development.rb:
+ ActionController::Base.reload_dependencies = true  
+ ActiveRecord::Base.reload_associations     = true
+
+ Now in development.rb:
+ Dependencies.mechanism = :load
+
+ Before in production.rb and test.rb:
+ ActionController::Base.reload_dependencies = false
+ ActiveRecord::Base.reload_associations     = false
+
+ Now in production.rb and test.rb:
+ Dependencies.mechanism = :require
+
+* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351
+
+* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 *Lucas Carlson*
+
+* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0
+
+
+## 0.9.2 ##
+
+* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping *Kent Sibilev*
+
+* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration *Scott Baron*
+
+* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record.
+
+* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 *rainmkr*
+
+* Added support for a -h/--help parameter in the generator #331 *Ulysses*
+
+* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories *mjobin*
+
+* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0
+
+
+## 0.9.1 ##
+
+* Upgraded to Action Pack 1.0.1 for important bug fix
+
+* Updated gem dependencies
+
+
+## 0.9.0 ##
+
+* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb
+
+* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible
+ for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb
+
+* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to
+ see help.
+
+* Added breakpoint support through the script/breakpointer client. This means that you can break out of execution at any point in
+ the code, investigate and change the model, AND then resume execution! Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find_all
+ breakpoint "Breaking out from the list"
+ end
+ end
+
+ So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window.
+ Here you can do things like:
+
+ Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
+ #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+ >> @posts.first.title = "hello from a breakpoint"
+ => "hello from a breakpoint"
+
+ ...and even better is that you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+ Finally, when you're ready to resume execution, you press CTRL-D
+
+* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you
+ can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server):
+
+ SetEnv RAILS_ENV production
+
+ ...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this:
+
+ ruby public/dispatcher.servlet -e production
+
+* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that.
+
+* Added a start_server in the root of the Rails application to make it even easier to get started
+
+* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods
+
+* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe)
+
+* Added support for controllers in directories. So you can have:
+
+ app/controllers/account_controller.rb # URL: /account/
+ app/controllers/admin/account_controller.rb # URL: /admin/account/
+
+ NOTE: You need to update your public/.htaccess with the new rules to pick it up
+
+* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use
+ those environments for development. This is turned on by default, but can be turned off with
+ ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments.
+
+* Added support for sub-directories in app/models. So now you can have something like Basecamp with:
+
+ app/models/accounting
+ app/models/project
+ app/models/participants
+ app/models/settings
+
+ It's poor man's namespacing, but only for file-system organization. You still require files just like before.
+ Nothing changes inside the files themselves.
+
+
+* Fixed a few references in the tests generated by new_mailer *Jeremy Kemper*
+
+* Added support for mocks in testing with test/mocks
+
+* Cleaned up the environments a bit and added global constant RAILS_ROOT
+
+
+## 0.8.5 (9) ##
+
+* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]:
+
+ def test_complex_stuff
+ @david.projects << @new_project
+ breakpoint "Let's have a closer look at @david"
+ end
+
+ You need to install dev-utils yourself for this to work ("gem install dev-util").
+
+* Added shared generator behavior so future upgrades should be possible without manually copying over files *Jeremy Kemper*
+
+* Added the new helper style to both controller and helper templates *Jeremy Kemper*
+
+* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding *Jeremy Kemper*
+
+* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style
+
+* Fixed that new_model was generating singular table/fixture names
+
+* Upgraded to Action Mailer 0.4.0
+
+* Upgraded to Action Pack 0.9.5
+
+* Upgraded to Active Record 1.1.0
+
+
+## 0.8.0 (15) ##
+
+* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is
+
+* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task.
+
+* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully *alexey*
+
+* Added user option for all postgresql tool calls in the rakefile *elvstone*
+
+* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" *alexey*
+
+* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based
+ on the Ruby runtime configuration. *Marcel Molina Jr.*
+
+* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb *Caio Chassot*
+
+* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector.
+
+* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target *Andreas Schwarz*
+
+* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant.
+ Class compilation cache can be turned on with "-c" or "--cache-classes".
+
+* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) *Kevin Temp*
+
+* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason *Andreas Schwarz*
+
+* Added new_mailer generator to work with Action Mailer
+
+* Included new framework: Action Mailer 0.3
+
+* Upgraded to Action Pack 0.9.0
+
+* Upgraded to Active Record 1.0.0
+
+
+## 0.7.0 ##
+
+* Added an optional second argument to the new_model script that allows the programmer to specify the table name,
+ which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures.
+ *Kevin Radloff*
+
+* script/new_model now turns AccountHolder into account_holder instead of accountholder *Kevin Radloff*
+
+* Fixed the faulty handleing of static files with WEBrick *Andreas Schwarz*
+
+* Unified function_test_helper and unit_test_helper into test_helper
+
+* Fixed bug with the automated production => test database dropping on PostgreSQL *dhawkins*
+
+* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation
+ and can generate more than one fixture at a time. Which makes it possible for assignments like:
+
+ @people, @projects, @project_access, @companies, @accounts =
+ create_fixtures "people", "projects", "project_access", "companies", "accounts"
+
+* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file)
+
+* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection)
+
+
+## 0.6.5 ##
+
+* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile)
+
+* The new_model generator will now use the same rules for plural wordings as Active Record
+ (so Category will give categories, not categorys) *Kevin Radloff*
+
+* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded *danp*
+
+* Made the GEM work with Windows
+
+* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers
+
+* PostgreSQL are now supported for the automated production => test database dropping *Kevin Radloff*
+
+* Errors thrown by the dispatcher are now properly handled in FCGI.
+
+* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes)
+
+* Upgraded to Active Record 0.9.4 (a bunch of fixes)
+
+
+## 0.6.0 ##
+
+* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used
+ to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all
+ controllers will also automatically have available.
+
+* Added environments that can be included from any script to get the full Active Record and Action Controller
+ context running. This can be used by maintenance scripts or to interact with the model through IRB. Example:
+
+ require 'config/environments/production'
+
+ for account in Account.find_all
+ account.recalculate_interests
+ end
+
+ A short migration script for an account model that had it's interest calculation strategy changed.
+
+* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick)
+
+* Simplified the default Apache config so even remote requests are served off CGI as a default.
+ You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls).
+ This should make it easier for new comers that start on an external server.
+
+* Added more of the necessary Apache options to .htaccess to make it easier to setup
+
+* Upgraded to Action Pack 0.7.9 (lots of fixes)
+
+* Upgraded to Active Record 0.9.3 (lots of fixes)
+
+
+## 0.5.7 ##
+
+* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL
+ (through GET requests or otherwise)
+
+* Added lib in root as a place to store app specific libraries
+
+* Added lib and vendor to load_path, so anything store within can be loaded directly.
+ Hence lib/redcloth.rb can be loaded with require "redcloth"
+
+* Upgraded to Action Pack 0.7.8 (lots of fixes)
+
+* Upgraded to Active Record 0.9.2 (minor upgrade)
+
+
+## 0.5.6 ##
+
+* Upgraded to Action Pack 0.7.7 (multipart form fix)
+
+* Updated the generated template stubs to valid XHTML files
+
+* Ensure that controllers generated are capitalized, so "new_controller TodoLists"
+ gives the same as "new_controller Todolists" and "new_controller todolists".
+
+
+## 0.5.5 ##
+
+* Works on Windows out of the box! (Dropped symlinks)
+
+* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" *Florian Gross*
+
+* Report errors about initialization to browser (instead of attempting to use uninitialized logger)
+
+* Upgraded to Action Pack 0.7.6
+
+* Upgraded to Active Record 0.9.1
+
+* Added distinct 500.html instead of reusing 404.html
+
+* Added MIT license
+
+
+## 0.5.0 ##
+
+* First public release
diff --git a/railties/MIT-LICENSE b/railties/MIT-LICENSE
index c73d1af096..03bde18130 100644
--- a/railties/MIT-LICENSE
+++ b/railties/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2011 David Heinemeier Hansson
+Copyright (c) 2004-2012 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/railties/README.rdoc b/railties/README.rdoc
index ae40600401..6248b5feed 100644
--- a/railties/README.rdoc
+++ b/railties/README.rdoc
@@ -21,7 +21,9 @@ Source code can be downloaded as part of the Rails project on GitHub
== License
-Railties is released under the MIT license.
+Railties is released under the MIT license:
+
+* http://www.opensource.org/licenses/MIT
== Support
diff --git a/railties/guides/assets/images/rails_guides_kindle_cover.jpg b/railties/guides/assets/images/rails_guides_kindle_cover.jpg
new file mode 100644
index 0000000000..9eb16720a9
--- /dev/null
+++ b/railties/guides/assets/images/rails_guides_kindle_cover.jpg
Binary files differ
diff --git a/railties/guides/assets/stylesheets/fixes.css b/railties/guides/assets/stylesheets/fixes.css
index 54efa5b9b7..bf86b29efa 100644
--- a/railties/guides/assets/stylesheets/fixes.css
+++ b/railties/guides/assets/stylesheets/fixes.css
@@ -12,5 +12,5 @@
.syntaxhighlighter table thead,
.syntaxhighlighter table caption,
.syntaxhighlighter textarea {
- line-height: 1.2em !important;
+ line-height: 1.25em !important;
}
diff --git a/railties/guides/assets/stylesheets/kindle.css b/railties/guides/assets/stylesheets/kindle.css
new file mode 100644
index 0000000000..b26cd1786a
--- /dev/null
+++ b/railties/guides/assets/stylesheets/kindle.css
@@ -0,0 +1,11 @@
+p { text-indent: 0; }
+
+p, H1, H2, H3, H4, H5, H6, H7, H8, table { margin-top: 1em;}
+
+.pagebreak { page-break-before: always; }
+#toc H3 {
+ text-indent: 1em;
+}
+#toc .document {
+ text-indent: 2em;
+} \ No newline at end of file
diff --git a/railties/guides/assets/stylesheets/main.css b/railties/guides/assets/stylesheets/main.css
index cf6e9e5cc8..4a775f632c 100644
--- a/railties/guides/assets/stylesheets/main.css
+++ b/railties/guides/assets/stylesheets/main.css
@@ -341,6 +341,14 @@ h6 {
margin-top: 0.25em;
}
+#mainCol dd.kindle, #subCol dd.kindle {
+ background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
+}
+
#mainCol div.warning, #subCol dd.warning {
background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top;
border: none;
diff --git a/railties/guides/code/getting_started/Gemfile b/railties/guides/code/getting_started/Gemfile
index 898510dcaa..768985070c 100644
--- a/railties/guides/code/getting_started/Gemfile
+++ b/railties/guides/code/getting_started/Gemfile
@@ -1,8 +1,9 @@
-source 'http://rubygems.org'
+source 'https://rubygems.org'
+
+gem 'rails', '3.2.0'
-gem 'rails', '3.1.0'
# Bundle edge Rails instead:
-# gem 'rails', :git => 'git://github.com/rails/rails.git'
+# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'sqlite3'
@@ -10,13 +11,23 @@ gem 'sqlite3'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
- gem 'sass-rails', " ~> 3.1.0"
- gem 'coffee-rails', "~> 3.1.0"
- gem 'uglifier'
+ gem 'sass-rails', '~> 3.2.3'
+ gem 'coffee-rails', '~> 3.2.1'
+
+ # See https://github.com/sstephenson/execjs#readme for more supported runtimes
+ # gem 'therubyracer'
+
+ gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
+# To use ActiveModel has_secure_password
+# gem 'bcrypt-ruby', '~> 3.0.0'
+
+# To use Jbuilder templates for JSON
+# gem 'jbuilder'
+
# Use unicorn as the web server
# gem 'unicorn'
diff --git a/railties/guides/code/getting_started/README b/railties/guides/code/getting_started/README.rdoc
index 7c36f2356e..d2014bd35f 100644
--- a/railties/guides/code/getting_started/README
+++ b/railties/guides/code/getting_started/README.rdoc
@@ -86,8 +86,8 @@ programming in general.
Debugger support is available through the debugger command when you start your
Mongrel or WEBrick server with --debugger. This means that you can break out of
execution at any point in the code, investigate and change the model, and then,
-resume execution! You need to install ruby-debug to run the server in debugging
-mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
+resume execution! You need to install ruby-debug19 to run the server in debugging
+mode. With gems, use <tt>sudo gem install ruby-debug19</tt>. Example:
class WeblogController < ActionController::Base
def index
@@ -191,7 +191,6 @@ The default directory structure of a generated Ruby on Rails application:
`-- vendor
|-- assets
`-- stylesheets
- `-- plugins
app
Holds all the code that's specific to this particular application.
@@ -256,6 +255,5 @@ test
directory.
vendor
- External libraries that the application depends on. Also includes the plugins
- subdirectory. If the app has frozen rails, those gems also go here, under
- vendor/rails/. This directory is in the load path.
+ External libraries that the application depends on. If the app has frozen rails,
+ those gems also go here, under vendor/rails/. This directory is in the load path.
diff --git a/railties/guides/code/getting_started/app/assets/javascripts/application.js b/railties/guides/code/getting_started/app/assets/javascripts/application.js
index 37c7bfcdb5..9097d830e2 100644
--- a/railties/guides/code/getting_started/app/assets/javascripts/application.js
+++ b/railties/guides/code/getting_started/app/assets/javascripts/application.js
@@ -1,9 +1,15 @@
-// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
-// be included in the compiled file accessible from http://example.com/assets/application.js
+// This is a manifest file that'll be compiled into application.js, which will include all the files
+// listed below.
+//
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
+// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
+//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
+// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
+// GO AFTER THE REQUIRES BELOW.
+//
//= require jquery
//= require jquery_ujs
//= require_tree .
diff --git a/railties/guides/code/getting_started/app/assets/stylesheets/application.css b/railties/guides/code/getting_started/app/assets/stylesheets/application.css
index fc25b5723f..3b5cc6648e 100644
--- a/railties/guides/code/getting_started/app/assets/stylesheets/application.css
+++ b/railties/guides/code/getting_started/app/assets/stylesheets/application.css
@@ -1,7 +1,13 @@
/*
- * This is a manifest file that'll automatically include all the stylesheets available in this directory
- * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
- * the top of the compiled file, but it's generally better to create a new file per style scope.
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
+ * listed below.
+ *
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
+ *
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
+ * compiled file, but it's generally better to create a new file per style scope.
+ *
*= require_self
- *= require_tree .
-*/ \ No newline at end of file
+ *= require_tree .
+*/
diff --git a/railties/guides/code/getting_started/app/controllers/posts_controller.rb b/railties/guides/code/getting_started/app/controllers/posts_controller.rb
index 7e903c984c..1581d4eb16 100644
--- a/railties/guides/code/getting_started/app/controllers/posts_controller.rb
+++ b/railties/guides/code/getting_started/app/controllers/posts_controller.rb
@@ -62,7 +62,7 @@ class PostsController < ApplicationController
respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
- format.json { head :ok }
+ format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @post.errors, status: :unprocessable_entity }
@@ -78,7 +78,7 @@ class PostsController < ApplicationController
respond_to do |format|
format.html { redirect_to posts_url }
- format.json { head :ok }
+ format.json { head :no_content }
end
end
end
diff --git a/railties/guides/code/getting_started/app/views/layouts/application.html.erb b/railties/guides/code/getting_started/app/views/layouts/application.html.erb
index 1e1e4b9a99..6578a41da2 100644
--- a/railties/guides/code/getting_started/app/views/layouts/application.html.erb
+++ b/railties/guides/code/getting_started/app/views/layouts/application.html.erb
@@ -2,11 +2,11 @@
<html>
<head>
<title>Blog</title>
- <%= stylesheet_link_tag "application" %>
+ <%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
-<body style="background: #EEEEEE;">
+<body>
<%= yield %>
diff --git a/railties/guides/code/getting_started/config/application.rb b/railties/guides/code/getting_started/config/application.rb
index e914b5a80e..d2cd5c028b 100644
--- a/railties/guides/code/getting_started/config/application.rb
+++ b/railties/guides/code/getting_started/config/application.rb
@@ -4,7 +4,7 @@ require 'rails/all'
if defined?(Bundler)
# If you precompile assets before deploying to production, use this line
- Bundler.require *Rails.groups(:assets => %w(development test))
+ Bundler.require(*Rails.groups(:assets => %w(development test)))
# If you want your assets lazily compiled in production, use this line
# Bundler.require(:default, :assets, Rails.env)
end
@@ -18,10 +18,6 @@ module Blog
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
- # Only load the plugins named here, in the order given (default is alphabetical).
- # :all can be used as a placeholder for all plugins not explicitly named.
- # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
-
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
@@ -39,10 +35,21 @@ module Blog
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
- # Enable the asset pipeline
+ # Use SQL instead of Active Record's schema dumper when creating the database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types.
+ # config.active_record.schema_format = :sql
+
+ # Enforce whitelist mode for mass assignment.
+ # This will create an empty whitelist of attributes available for mass-assignment for all models
+ # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
+ # parameters by using an attr_accessible or attr_protected declaration.
+ # config.active_record.whitelist_attributes = true
+
+ # Enable the asset pipeline.
config.assets.enabled = true
- # Version of your assets, change this if you want to expire all your assets
+ # Version of your assets, change this if you want to expire all your assets.
config.assets.version = '1.0'
end
end
diff --git a/railties/guides/code/getting_started/config/environments/development.rb b/railties/guides/code/getting_started/config/environments/development.rb
index 89932bf19b..cec2b20c0b 100644
--- a/railties/guides/code/getting_started/config/environments/development.rb
+++ b/railties/guides/code/getting_started/config/environments/development.rb
@@ -1,30 +1,34 @@
Blog::Application.configure do
- # Settings specified here will take precedence over those in config/application.rb
+ # Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
- # Log error messages when you accidentally call methods on nil.
- config.whiny_nils = true
-
- # Show full error reports and disable caching
+ # Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
- # Don't care if the mailer can't send
+ # Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
- # Print deprecation notices to the Rails logger
+ # Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
- # Only use best-standards-support built into browsers
+ # Only use best-standards-support built into browsers.
config.action_dispatch.best_standards_support = :builtin
- # Do not compress assets
+ # Raise exception on mass assignment protection for ActiveRecord models.
+ config.active_record.mass_assignment_sanitizer = :strict
+
+ # Log the query plan for queries taking more than this (works
+ # with SQLite, MySQL, and PostgreSQL).
+ config.active_record.auto_explain_threshold_in_seconds = 0.5
+
+ # Do not compress assets.
config.assets.compress = false
- # Expands the lines which load the assets
+ # Expands the lines which load the assets.
config.assets.debug = true
end
diff --git a/railties/guides/code/getting_started/config/environments/production.rb b/railties/guides/code/getting_started/config/environments/production.rb
index dee8acfdfe..cfb8c960d6 100644
--- a/railties/guides/code/getting_started/config/environments/production.rb
+++ b/railties/guides/code/getting_started/config/environments/production.rb
@@ -1,63 +1,67 @@
Blog::Application.configure do
- # Settings specified here will take precedence over those in config/application.rb
+ # Settings specified here will take precedence over those in config/application.rb.
- # Code is not reloaded between requests
+ # Code is not reloaded between requests.
config.cache_classes = true
- # Full error reports are disabled and caching is turned on
+ # Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
- # Disable Rails's static asset server (Apache or nginx will already do this)
+ # Disable Rails's static asset server (Apache or nginx will already do this).
config.serve_static_assets = false
- # Compress JavaScripts and CSS
+ # Compress JavaScripts and CSS.
config.assets.compress = true
- # Don't fallback to assets pipeline if a precompiled asset is missed
+ # Don't fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
- # Generate digests for assets URLs
+ # Generate digests for assets URLs.
config.assets.digest = true
- # Defaults to Rails.root.join("public/assets")
+ # Defaults to Rails.root.join("public/assets").
# config.assets.manifest = YOUR_PATH
- # Specifies the header that your server uses for sending files
+ # Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
- # See everything in the log (default is :info)
+ # See everything in the log (default is :info).
# config.log_level = :debug
- # Prepend all log lines with the following tags
+ # Prepend all log lines with the following tags.
# config.log_tags = [ :subdomain, :uuid ]
- # Use a different logger for distributed setups
+ # Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
- # Use a different cache store in production
+ # Use a different cache store in production.
# config.cache_store = :mem_cache_store
- # Enable serving of images, stylesheets, and JavaScripts from an asset server
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = "http://assets.example.com"
- # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added).
# config.assets.precompile += %w( search.js )
- # Disable delivery errors, bad email addresses will be ignored
+ # Disable delivery errors, bad email addresses will be ignored.
# config.action_mailer.raise_delivery_errors = false
- # Enable threaded mode
+ # Enable threaded mode.
# config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
- # the I18n.default_locale when a translation can not be found)
+ # the I18n.default_locale when a translation can not be found).
config.i18n.fallbacks = true
- # Send deprecation notices to registered listeners
+ # Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
+
+ # Log the query plan for queries taking more than this (works
+ # with SQLite, MySQL, and PostgreSQL).
+ # config.active_record.auto_explain_threshold_in_seconds = 0.5
end
diff --git a/railties/guides/code/getting_started/config/environments/test.rb b/railties/guides/code/getting_started/config/environments/test.rb
index 833241ace3..f2bc932fb3 100644
--- a/railties/guides/code/getting_started/config/environments/test.rb
+++ b/railties/guides/code/getting_started/config/environments/test.rb
@@ -1,5 +1,5 @@
Blog::Application.configure do
- # Settings specified here will take precedence over those in config/application.rb
+ # Settings specified here will take precedence over those in config/application.rb.
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
@@ -7,36 +7,28 @@ Blog::Application.configure do
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
- # Configure static asset server for tests with Cache-Control for performance
+ # Configure static asset server for tests with Cache-Control for performance.
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
- # Log error messages when you accidentally call methods on nil
- config.whiny_nils = true
-
- # Show full error reports and disable caching
+ # Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
- # Raise exceptions instead of rendering exception templates
+ # Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
- # Disable request forgery protection in test environment
- config.action_controller.allow_forgery_protection = false
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
- # Use SQL instead of Active Record's schema dumper when creating the test database.
- # This is necessary if your schema can't be completely dumped by the schema dumper,
- # like if you have constraints or database-specific column types
- # config.active_record.schema_format = :sql
+ # Raise exception on mass assignment protection for Active Record models.
+ config.active_record.mass_assignment_sanitizer = :strict
- # Print deprecation notices to the stderr
+ # Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
-
- # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
- config.assets.allow_debugging = true
end
diff --git a/railties/guides/code/getting_started/config/initializers/inflections.rb b/railties/guides/code/getting_started/config/initializers/inflections.rb
index 9e8b0131f8..5d8d9be237 100644
--- a/railties/guides/code/getting_started/config/initializers/inflections.rb
+++ b/railties/guides/code/getting_started/config/initializers/inflections.rb
@@ -8,3 +8,8 @@
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
+#
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/railties/guides/code/getting_started/config/routes.rb b/railties/guides/code/getting_started/config/routes.rb
index 31f0d210db..b048ac68f1 100644
--- a/railties/guides/code/getting_started/config/routes.rb
+++ b/railties/guides/code/getting_started/config/routes.rb
@@ -1,7 +1,7 @@
Blog::Application.routes.draw do
- resources :posts do
- resources :comments
- end
+ resources :posts do
+ resources :comments
+ end
get "home/index"
@@ -60,5 +60,5 @@ Blog::Application.routes.draw do
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
- # match ':controller(/:action(/:id(.:format)))'
+ # match ':controller(/:action(/:id))(.:format)'
end
diff --git a/railties/guides/code/getting_started/public/500.html b/railties/guides/code/getting_started/public/500.html
index b80307fc16..f3648a0dbc 100644
--- a/railties/guides/code/getting_started/public/500.html
+++ b/railties/guides/code/getting_started/public/500.html
@@ -20,7 +20,6 @@
<!-- This file lives in public/500.html -->
<div class="dialog">
<h1>We're sorry, but something went wrong.</h1>
- <p>We've been notified about this issue and we'll take a look at it shortly.</p>
</div>
</body>
</html>
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index 4682ead66e..d6a98f9ac4 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -47,6 +47,11 @@
# Set to "1" to indicate generated guides should be marked as edge. This
# inserts a badge and changes the preamble of the home page.
#
+# KINDLE
+# Set to "1" to generate the .mobi with all the guides. The kindlegen
+# executable must be in your PATH. You can get it for free from
+# http://www.amazon.com/kindlepublishing
+#
# ---------------------------------------------------------------------------
require 'set'
@@ -65,37 +70,81 @@ module RailsGuides
class Generator
attr_reader :guides_dir, :source_dir, :output_dir, :edge, :warnings, :all
- GUIDES_RE = /\.(?:textile|html\.erb)$/
+ GUIDES_RE = /\.(?:textile|erb)$/
def initialize(output=nil)
- @lang = ENV['GUIDES_LANGUAGE']
+ set_flags_from_environment
+
+ if kindle?
+ check_for_kindlegen
+ register_kindle_mime_types
+ end
+
initialize_dirs(output)
create_output_dir_if_needed
- set_flags_from_environment
+ end
+
+ def set_flags_from_environment
+ @edge = ENV['EDGE'] == '1'
+ @warnings = ENV['WARNINGS'] == '1'
+ @all = ENV['ALL'] == '1'
+ @kindle = ENV['KINDLE'] == '1'
+ @version = ENV['RAILS_VERSION'] || `git rev-parse --short HEAD`.chomp
+ @lang = ENV['GUIDES_LANGUAGE']
+ end
+
+ def register_kindle_mime_types
+ Mime::Type.register_alias("application/xml", :opf, %w(opf))
+ Mime::Type.register_alias("application/xml", :ncx, %w(ncx))
end
def generate
generate_guides
copy_assets
+ generate_mobi if kindle?
end
private
+
+ def kindle?
+ @kindle
+ end
+
+ def check_for_kindlegen
+ if `which kindlegen`.blank?
+ raise "Can't create a kindle version without `kindlegen`."
+ end
+ end
+
+ def generate_mobi
+ opf = "#{output_dir}/rails_guides.opf"
+ out = "#{output_dir}/kindlegen.out"
+
+ system "kindlegen #{opf} -o #{mobi} > #{out} 2>&1"
+ puts "Guides compiled as Kindle book to #{mobi}"
+ puts "(kindlegen log at #{out})."
+ end
+
+ def mobi
+ "ruby_on_rails_guides_#@version%s.mobi" % (@lang.present? ? ".#@lang" : '')
+ end
+
def initialize_dirs(output)
@guides_dir = File.join(File.dirname(__FILE__), '..')
- @source_dir = File.join(@guides_dir, "source", @lang.to_s)
- @output_dir = output || File.join(@guides_dir, "output", @lang.to_s)
+ @source_dir = "#@guides_dir/source/#@lang"
+ @output_dir = if output
+ output
+ elsif kindle?
+ "#@guides_dir/output/kindle/#@lang"
+ else
+ "#@guides_dir/output/#@lang"
+ end.sub(%r</$>, '')
end
def create_output_dir_if_needed
FileUtils.mkdir_p(output_dir)
end
- def set_flags_from_environment
- @edge = ENV['EDGE'] == '1'
- @warnings = ENV['WARNINGS'] == '1'
- @all = ENV['ALL'] == '1'
- end
-
def generate_guides
guides_to_generate.each do |guide|
output_file = output_file_for(guide)
@@ -105,13 +154,20 @@ module RailsGuides
def guides_to_generate
guides = Dir.entries(source_dir).grep(GUIDES_RE)
+
+ if kindle?
+ Dir.entries("#{source_dir}/kindle").grep(GUIDES_RE).map do |entry|
+ guides << "kindle/#{entry}"
+ end
+ end
+
ENV.key?('ONLY') ? select_only(guides) : guides
end
def select_only(guides)
prefixes = ENV['ONLY'].split(",").map(&:strip)
guides.select do |guide|
- prefixes.any? {|p| guide.start_with?(p)}
+ prefixes.any? { |p| guide.start_with?(p) || guide.start_with?("kindle") }
end
end
@@ -120,35 +176,47 @@ module RailsGuides
end
def output_file_for(guide)
- guide.sub(GUIDES_RE, '.html')
+ if guide =~/\.textile$/
+ guide.sub(/\.textile$/, '.html')
+ else
+ guide.sub(/\.erb$/, '')
+ end
+ end
+
+ def output_path_for(output_file)
+ File.join(output_dir, File.basename(output_file))
end
def generate?(source_file, output_file)
fin = File.join(source_dir, source_file)
- fout = File.join(output_dir, output_file)
+ fout = output_path_for(output_file)
all || !File.exists?(fout) || File.mtime(fout) < File.mtime(fin)
end
def generate_guide(guide, output_file)
- puts "Generating #{output_file}"
- File.open(File.join(output_dir, output_file), 'w') do |f|
- view = ActionView::Base.new(source_dir, :edge => edge)
+ output_path = output_path_for(output_file)
+ puts "Generating #{guide} as #{output_file}"
+ layout = kindle? ? 'kindle/layout' : 'layout'
+
+ File.open(output_path, 'w') do |f|
+ view = ActionView::Base.new(source_dir, :edge => @edge, :version => @version, :mobi => "kindle/#{mobi}")
view.extend(Helpers)
- if guide =~ /\.html\.erb$/
+ if guide =~ /\.(\w+)\.erb$/
# Generate the special pages like the home.
- result = view.render(:layout => 'layout', :file => guide)
+ # Passing a template handler in the template name is deprecated. So pass the file name without the extension.
+ result = view.render(:layout => layout, :formats => [$1], :file => $`)
else
body = File.read(File.join(source_dir, guide))
body = set_header_section(body, view)
body = set_index(body, view)
- result = view.render(:layout => 'layout', :text => textile(body))
+ result = view.render(:layout => layout, :text => textile(body))
warn_about_broken_links(result) if @warnings
end
- f.write result
+ f.write(result)
end
end
@@ -215,7 +283,7 @@ module RailsGuides
anchors = Set.new
html.scan(/<h\d\s+id="([^"]+)/).flatten.each do |anchor|
if anchors.member?(anchor)
- puts "*** DUPLICATE ID: #{anchor}, please put and explicit ID, e.g. h4(#explicit-id), or consider rewording"
+ puts "*** DUPLICATE ID: #{anchor}, please use an explicit ID, e.g. h4(#explicit-id), or consider rewording"
else
anchors << anchor
end
diff --git a/railties/guides/rails_guides/helpers.rb b/railties/guides/rails_guides/helpers.rb
index 463df8a7a8..e6ef656474 100644
--- a/railties/guides/rails_guides/helpers.rb
+++ b/railties/guides/rails_guides/helpers.rb
@@ -12,6 +12,22 @@ module RailsGuides
result
end
+ def documents_by_section
+ @documents_by_section ||= YAML.load_file(File.expand_path('../../source/documents.yaml', __FILE__))
+ end
+
+ def documents_flat
+ documents_by_section.map {|section| section['documents']}.flatten
+ end
+
+ def finished_documents(documents)
+ documents.reject { |document| document['work_in_progress'] }
+ end
+
+ def docs_for_menu(position)
+ position == 'L' ? documents_by_section.to(3) : documents_by_section.from(4)
+ end
+
def author(name, nick, image = 'credits_pic_blank.gif', &block)
image = "images/#{image}"
diff --git a/railties/guides/rails_guides/indexer.rb b/railties/guides/rails_guides/indexer.rb
index fb46491817..89fbccbb1d 100644
--- a/railties/guides/rails_guides/indexer.rb
+++ b/railties/guides/rails_guides/indexer.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/object/blank'
-require 'active_support/ordered_hash'
require 'active_support/core_ext/string/inflections'
module RailsGuides
@@ -21,7 +20,7 @@ module RailsGuides
def process(string, current_level=3, counters=[1])
s = StringScanner.new(string)
- level_hash = ActiveSupport::OrderedHash.new
+ level_hash = {}
while !s.eos?
re = %r{^h(\d)(?:\((#.*?)\))?\s*\.\s*(.*)$}
diff --git a/railties/guides/rails_guides/levenshtein.rb b/railties/guides/rails_guides/levenshtein.rb
index f99c6e6ba8..489aa3ea7a 100644
--- a/railties/guides/rails_guides/levenshtein.rb
+++ b/railties/guides/rails_guides/levenshtein.rb
@@ -1,6 +1,6 @@
module RailsGuides
module Levenshtein
- # Based on the pseudocode in http://en.wikipedia.org/wiki/Levenshtein_distance.
+ # Based on the pseudocode in http://en.wikipedia.org/wiki/Levenshtein_distance
def self.distance(s1, s2)
s = s1.unpack('U*')
t = s2.unpack('U*')
diff --git a/railties/guides/source/3_1_release_notes.textile b/railties/guides/source/3_1_release_notes.textile
index c4da87dc34..f88d8624ba 100644
--- a/railties/guides/source/3_1_release_notes.textile
+++ b/railties/guides/source/3_1_release_notes.textile
@@ -21,6 +21,113 @@ Rails 3.1 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby ve
TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing.
+h4. What to update in your apps
+
+The following changes are meant for upgrading your application to Rails 3.1.3, the latest 3.1.x version of Rails.
+
+h5. Gemfile
+
+Make the following changes to your +Gemfile+.
+
+<ruby>
+gem 'rails', '= 3.1.3'
+gem 'mysql2'
+
+# Needed for the new asset pipeline
+group :assets do
+ gem 'sass-rails', "~> 3.1.5"
+ gem 'coffee-rails', "~> 3.1.1"
+ gem 'uglifier', ">= 1.0.3"
+end
+
+# jQuery is the default JavaScript library in Rails 3.1
+gem 'jquery-rails'
+</ruby>
+
+h5. config/application.rb
+
+* The asset pipeline requires the following additions:
+
+<ruby>
+config.assets.enabled = true
+config.assets.version = '1.0'
+</ruby>
+
+* If your application is using the "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts:
+
+<ruby>
+# Defaults to '/assets'
+config.assets.prefix = '/asset-files'
+</ruby>
+
+h5. config/environments/development.rb
+
+* Remove the RJS setting <tt>config.action_view.debug_rjs = true</tt>.
+
+* Add the following, if you enable the asset pipeline.
+
+<ruby>
+# Do not compress assets
+config.assets.compress = false
+
+# Expands the lines which load the assets
+config.assets.debug = true
+</ruby>
+
+h5. config/environments/production.rb
+
+* Again, most of the changes below are for the asset pipeline. You can read more about these in the "Asset Pipeline":asset_pipeline.html guide.
+
+<ruby>
+# Compress JavaScripts and CSS
+config.assets.compress = true
+
+# Don't fallback to assets pipeline if a precompiled asset is missed
+config.assets.compile = false
+
+# Generate digests for assets URLs
+config.assets.digest = true
+
+# Defaults to Rails.root.join("public/assets")
+# config.assets.manifest = YOUR_PATH
+
+# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+# config.assets.precompile += %w( search.js )
+
+
+# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+# config.force_ssl = true
+
+</ruby>
+
+h5. config/environments/test.rb
+
+<ruby>
+# Configure static asset server for tests with Cache-Control for performance
+config.serve_static_assets = true
+config.static_cache_control = "public, max-age=3600"
+</ruby>
+
+h5. config/initializers/wrap_parameters.rb
+
+* Add this file with the following contents, if you wish to wrap parameters into a nested hash. This is on by default in new applications.
+
+<ruby>
+# Be sure to restart your server when you modify this file.
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters :format => [:json]
+end
+
+# Disable root element in JSON by default.
+ActiveSupport.on_load(:active_record) do
+ self.include_root_in_json = false
+end
+</ruby>
+
h3. Creating a Rails 3.1 application
<shell>
@@ -46,7 +153,7 @@ $ rails new myapp --edge
If you have a local checkout of the Rails repository and want to generate an application using that, you can pass the +--dev+ flag:
<shell>
-$ ruby /path/to/rails/bin/rails new myapp --dev
+$ ruby /path/to/rails/railties/bin/rails new myapp --dev
</shell>
h3. Rails Architectural Changes
diff --git a/railties/guides/source/3_2_release_notes.textile b/railties/guides/source/3_2_release_notes.textile
new file mode 100644
index 0000000000..d669a7fdfa
--- /dev/null
+++ b/railties/guides/source/3_2_release_notes.textile
@@ -0,0 +1,540 @@
+h2. Ruby on Rails 3.2 Release Notes
+
+Highlights in Rails 3.2:
+
+* Faster Development Mode
+* New Routing Engine
+* Automatic Query Explains
+* Tagged Logging
+
+These release notes cover the major changes, but do not include each bug-fix and changes. If you want to see everything, check out the "list of commits":https://github.com/rails/rails/commits/3-2-stable in the main Rails repository on GitHub.
+
+endprologue.
+
+h3. Upgrading to Rails 3.2
+
+If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 3.1 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 3.2. Then take heed of the following changes:
+
+h4. Rails 3.2 requires at least Ruby 1.8.7
+
+Rails 3.2 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.2 is also compatible with Ruby 1.9.2.
+
+TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump on to 1.9.2 or 1.9.3 for smooth sailing.
+
+h4. What to update in your apps
+
+* Update your Gemfile to depend on
+** <tt>rails = 3.2.0</tt>
+** <tt>sass-rails ~> 3.2.3</tt>
+** <tt>coffee-rails ~> 3.2.1</tt>
+** <tt>uglifier >= 1.0.3</tt>
+
+* Rails 3.2 deprecates <tt>vendor/plugins</tt> and Rails 4.0 will remove them completely. You can start replacing these plugins by extracting them as gems and adding them in your Gemfile. If you choose not to make them gems, you can move them into, say, <tt>lib/my_plugin/*</tt> and add an appropriate initializer in <tt>config/initializers/my_plugin.rb</tt>.
+
+* There are a couple of new configuration changes you'd want to add in <tt>config/environments/development.rb</tt>:
+
+<ruby>
+# Raise exception on mass assignment protection for Active Record models
+config.active_record.mass_assignment_sanitizer = :strict
+
+# Log the query plan for queries taking more than this (works
+# with SQLite, MySQL, and PostgreSQL)
+config.active_record.auto_explain_threshold_in_seconds = 0.5
+</ruby>
+
+The <tt>mass_assignment_sanitizer</tt> config also needs to be added in <tt>config/environments/test.rb</tt>:
+
+<ruby>
+# Raise exception on mass assignment protection for Active Record models
+config.active_record.mass_assignment_sanitizer = :strict
+</ruby>
+
+h3. Creating a Rails 3.2 application
+
+<shell>
+# You should have the 'rails' rubygem installed
+$ rails new myapp
+$ cd myapp
+</shell>
+
+h4. Vendoring Gems
+
+Rails now uses a +Gemfile+ in the application root to determine the gems you require for your application to start. This +Gemfile+ is processed by the "Bundler":https://github.com/carlhuda/bundler gem, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
+
+More information: "Bundler homepage":http://gembundler.com
+
+h4. Living on the Edge
+
++Bundler+ and +Gemfile+ makes freezing your Rails application easy as pie with the new dedicated +bundle+ command. If you want to bundle straight from the Git repository, you can pass the +--edge+ flag:
+
+<shell>
+$ rails new myapp --edge
+</shell>
+
+If you have a local checkout of the Rails repository and want to generate an application using that, you can pass the +--dev+ flag:
+
+<shell>
+$ ruby /path/to/rails/railties/bin/rails new myapp --dev
+</shell>
+
+h3. Major Features
+
+h4. Faster Development Mode & Routing
+
+Rails 3.2 comes with a development mode that's noticeably faster. Inspired by "Active Reload":https://github.com/paneq/active_reload, Rails reloads classes only when files actually change. The performance gains are dramatic on a larger application. Route recognition also got a bunch faster thanks to the new "Journey":https://github.com/rails/journey engine.
+
+h4. Automatic Query Explains
+
+Rails 3.2 comes with a nice feature that explains queries generated by ARel by defining an +explain+ method in <tt>ActiveRecord::Relation</tt>. For example, you can run something like <tt>puts Person.active.limit(5).explain</tt> and the query ARel produces is explained. This allows to check for the proper indexes and further optimizations.
+
+Queries that take more than half a second to run are *automatically* explained in the development mode. This threshold, of course, can be changed.
+
+h4. Tagged Logging
+
+When running a multi-user, multi-account application, it's a great help to be able to filter the log by who did what. TaggedLogging in Active Support helps in doing exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications.
+
+h3. Documentation
+
+From Rails 3.2, the Rails guides are available for the Kindle and free Kindle Reading Apps for the iPad, iPhone, Mac, Android, etc.
+
+h3. Railties
+
+* Speed up development by only reloading classes if dependencies files changed. This can be turned off by setting <tt>config.reload_classes_only_on_change</tt> to false.
+
+* New applications get a flag <tt>config.active_record.auto_explain_threshold_in_seconds</tt> in the environments configuration files. With a value of <tt>0.5</tt> in <tt>development.rb</tt> and commented out in <tt>production.rb</tt>. No mention in <tt>test.rb</tt>.
+
+* Added <tt>config.exceptions_app</tt> to set the exceptions application invoked by the +ShowException+ middleware when an exception happens. Defaults to <tt>ActionDispatch::PublicExceptions.new(Rails.public_path)</tt>.
+
+* Added a <tt>DebugExceptions</tt> middleware which contains features extracted from <tt>ShowExceptions</tt> middleware.
+
+* Display mounted engines' routes in <tt>rake routes</tt>.
+
+* Allow to change the loading order of railties with <tt>config.railties_order</tt> like:
+
+<ruby>
+config.railties_order = [Blog::Engine, :main_app, :all]
+</ruby>
+
+* Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box.
+
+* Update <tt>Rails::Rack::Logger</tt> middleware to apply any tags set in <tt>config.log_tags</tt> to <tt>ActiveSupport::TaggedLogging</tt>. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications.
+
+* Default options to +rails new+ can be set in <tt>~/.railsrc</tt>. You can specify extra command-line arguments to be used every time 'rails new' runs in the <tt>.railsrc</tt> configuration file in your home directory.
+
+* Add an alias +d+ for +destroy+. This works for engines too.
+
+* Attributes on scaffold and model generators default to string. This allows the following: <tt>rails g scaffold Post title body:text author</tt>
+
+* Allow scaffold/model/migration generators to accept "index" and "uniq" modifiers. For example,
+
+<ruby>
+rails g scaffold Post title:string:index author:uniq price:decimal{7,2}
+</ruby>
+
+will create indexes for +title+ and +author+ with the latter being an unique index. Some types such as decimal accept custom options. In the example, +price+ will be a decimal column with precision and scale set to 7 and 2 respectively.
+
+* Turn gem has been removed from default Gemfile.
+
+* Remove old plugin generator +rails generate plugin+ in favor of +rails plugin new+ command.
+
+* Remove old <tt>config.paths.app.controller</tt> API in favor of <tt>config.paths["app/controller"]</tt>.
+
+h4(#railties_deprecations). Deprecations
+
+* +Rails::Plugin+ is deprecated and will be removed in Rails 4.0. Instead of adding plugins to +vendor/plugins+ use gems or bundler with path or git dependencies.
+
+h3. Action Mailer
+
+* Upgraded <tt>mail</tt> version to 2.4.0.
+
+* Removed the old Action Mailer API which was deprecated since Rails 3.0.
+
+h3. Action Pack
+
+h4. Action Controller
+
+* Make <tt>ActiveSupport::Benchmarkable</tt> a default module for <tt>ActionController::Base,</tt> so the <tt>#benchmark</tt> method is once again available in the controller context like it used to be.
+
+* Added +:gzip+ option to +caches_page+. The default option can be configured globally using <tt>page_cache_compression</tt>.
+
+* Rails will now use your default layout (such as "layouts/application") when you specify a layout with <tt>:only</tt> and <tt>:except</tt> condition, and those conditions fail.
+
+<ruby>
+class CarsController
+ layout 'single_car', :only => :show
+end
+</ruby>
+
+Rails will use 'layouts/single_car' when a request comes in :show action, and use 'layouts/application' (or 'layouts/cars', if exists) when a request comes in for any other actions.
+
+* form_for is changed to use "#{action}_#{as}" as the css class and id if +:as+ option is provided. Earlier versions used "#{as}_#{action}".
+
+* <tt>ActionController::ParamsWrapper</tt> on ActiveRecord models now only wrap <tt>attr_accessible</tt> attributes if they were set. If not, only the attributes returned by the class method +attribute_names+ will be wrapped. This fixes the wrapping of nested attributes by adding them to +attr_accessible+.
+
+* Log "Filter chain halted as CALLBACKNAME rendered or redirected" every time a before callback halts.
+
+* <tt>ActionDispatch::ShowExceptions</tt> is refactored. The controller is responsible for choosing to show exceptions. It's possible to override +show_detailed_exceptions?+ in controllers to specify which requests should provide debugging information on errors.
+
+* Responders now return 204 No Content for API requests without a response body (as in the new scaffold).
+
+* <tt>ActionController::TestCase</tt> cookies is refactored. Assigning cookies for test cases should now use <tt>cookies[]</tt>
+
+<ruby>
+cookies[:email] = 'user@example.com'
+get :index
+assert_equal 'user@example.com', cookies[:email]
+</ruby>
+
+To clear the cookies, use +clear+.
+
+<ruby>
+cookies.clear
+get :index
+assert_nil cookies[:email]
+</ruby>
+
+We now no longer write out HTTP_COOKIE and the cookie jar is persistent between requests so if you need to manipulate the environment for your test you need to do it before the cookie jar is created.
+
+* <tt>send_file</tt> now guesses the MIME type from the file extension if +:type+ is not provided.
+
+* MIME type entries for PDF, ZIP and other formats were added.
+
+* Allow fresh_when/stale? to take a record instead of an options hash.
+
+* Changed log level of warning for missing CSRF token from <tt>:debug</tt> to <tt>:warn</tt>.
+
+* Assets should use the request protocol by default or default to relative if no request is available.
+
+h5(#actioncontroller_deprecations). Deprecations
+
+* Deprecated implied layout lookup in controllers whose parent had a explicit layout set:
+
+<ruby>
+class ApplicationController
+ layout "application"
+end
+
+class PostsController < ApplicationController
+end
+</ruby>
+
+In the example above, Posts controller will no longer automatically look up for a posts layout. If you need this functionality you could either remove <tt>layout "application"</tt> from +ApplicationController+ or explicitly set it to +nil+ in +PostsController+.
+
+* Deprecated <tt>ActionController::UnknownAction</tt> in favour of <tt>AbstractController::ActionNotFound</tt>.
+
+* Deprecated <tt>ActionController::DoubleRenderError</tt> in favour of <tt>AbstractController::DoubleRenderError</tt>.
+
+* Deprecated <tt>method_missing</tt> in favour of +action_missing+ for missing actions.
+
+* Deprecated <tt>ActionController#rescue_action</tt>, <tt>ActionController#initialize_template_class</tt> and <tt>ActionController#assign_shortcuts</tt>.
+
+h4. Action Dispatch
+
+* Add <tt>config.action_dispatch.default_charset</tt> to configure default charset for <tt>ActionDispatch::Response</tt>.
+
+* Added <tt>ActionDispatch::RequestId</tt> middleware that'll make a unique X-Request-Id header available to the response and enables the <tt>ActionDispatch::Request#uuid</tt> method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog.
+
+* The <tt>ShowExceptions</tt> middleware now accepts a exceptions application that is responsible to render an exception when the application fails. The application is invoked with a copy of the exception in +env["action_dispatch.exception"]+ and with the <tt>PATH_INFO</tt> rewritten to the status code.
+
+* Allow rescue responses to be configured through a railtie as in <tt>config.action_dispatch.rescue_responses</tt>.
+
+h5(#actiondispatch_deprecations). Deprecations
+
+* Deprecated the ability to set a default charset at the controller level, use the new <tt>config.action_dispatch.default_charset</tt> instead.
+
+h4. Action View
+
+* Add +button_tag+ support to <tt>ActionView::Helpers::FormBuilder</tt>. This support mimics the default behavior of +submit_tag+.
+
+<ruby>
+<%= form_for @post do |f| %>
+ <%= f.button %>
+<% end %>
+</ruby>
+
+* Date helpers accept a new option <tt>:use_two_digit_numbers => true</tt>, that renders select boxes for months and days with a leading zero without changing the respective values. For example, this is useful for displaying ISO 8601-style dates such as '2011-08-01'.
+
+* You can provide a namespace for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generated HTML id.
+
+<ruby>
+<%= form_for(@offer, :namespace => 'namespace') do |f| %>
+ <%= f.label :version, 'Version' %>:
+ <%= f.text_field :version %>
+<% end %>
+</ruby>
+
+* Limit the number of options for +select_year+ to 1000. Pass +:max_years_allowed+ option to set your own limit.
+
+* +content_tag_for+ and +div_for+ can now take a collection of records. It will also yield the record as the first argument if you set a receiving argument in your block. So instead of having to do this:
+
+<ruby>
+@items.each do |item|
+ content_tag_for(:li, item) do
+ Title: <%= item.title %>
+ end
+end
+</ruby>
+
+You can do this:
+
+<ruby>
+content_tag_for(:li, @items) do |item|
+ Title: <%= item.title %>
+end
+</ruby>
+
+* Added +font_path+ helper method that computes the path to a font asset in <tt>public/fonts</tt>.
+
+h5(#actionview_deprecations). Deprecations
+
+* Passing formats or handlers to render :template and friends like <tt>render :template => "foo.html.erb"</tt> is deprecated. Instead, you can provide :handlers and :formats directly as an options: <tt> render :template => "foo", :formats => [:html, :js], :handlers => :erb</tt>.
+
+h4. Sprockets
+
+* Adds a configuration option <tt>config.assets.logger</tt> to control Sprockets logging. Set it to +false+ to turn off logging and to +nil+ to default to +Rails.logger+.
+
+h3. Active Record
+
+* Boolean columns with 'on' and 'ON' values are type cast to true.
+
+* When the +timestamps+ method creates the +created_at+ and +updated_at+ columns, it makes them non-nullable by default.
+
+* Implemented <tt>ActiveRecord::Relation#explain</tt>.
+
+* Implements <tt>AR::Base.silence_auto_explain</tt> which allows the user to selectively disable automatic EXPLAINs within a block.
+
+* Implements automatic EXPLAIN logging for slow queries. A new configuration parameter +config.active_record.auto_explain_threshold_in_seconds+ determines what's to be considered a slow query. Setting that to nil disables this feature. Defaults are 0.5 in development mode, and nil in test and production modes. Rails 3.2 supports this feature in SQLite, MySQL (mysql2 adapter), and PostgreSQL.
+
+* Added <tt>ActiveRecord::Base.store</tt> for declaring simple single-column key/value stores.
+
+<ruby>
+class User < ActiveRecord::Base
+ store :settings, accessors: [ :color, :homepage ]
+end
+
+u = User.new(color: 'black', homepage: '37signals.com')
+u.color # Accessor stored attribute
+u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
+</ruby>
+
+* Added ability to run migrations only for a given scope, which allows to run migrations only from one engine (for example to revert changes from an engine that need to be removed).
+
+<ruby>
+rake db:migrate SCOPE=blog
+</ruby>
+
+* Migrations copied from engines are now scoped with engine's name, for example <tt>01_create_posts.blog.rb</tt>.
+
+* Implemented <tt>ActiveRecord::Relation#pluck</tt> method that returns an array of column values directly from the underlying table. This also works with serialized attributes.
+
+<ruby>
+Client.where(:active => true).pluck(:id)
+# SELECT id from clients where active = 1
+</ruby>
+
+* Generated association methods are created within a separate module to allow overriding and composition. For a class named MyModel, the module is named <tt>MyModel::GeneratedFeatureMethods</tt>. It is included into the model class immediately after the +generated_attributes_methods+ module defined in Active Model, so association methods override attribute methods of the same name.
+
+* Add <tt>ActiveRecord::Relation#uniq</tt> for generating unique queries.
+
+<ruby>
+Client.select('DISTINCT name')
+</ruby>
+
+..can be written as:
+
+<ruby>
+Client.select(:name).uniq
+</ruby>
+
+This also allows you to revert the uniqueness in a relation:
+
+<ruby>
+Client.select(:name).uniq.uniq(false)
+</ruby>
+
+* Support index sort order in SQLite, MySQL and PostgreSQL adapters.
+
+* Allow the +:class_name+ option for associations to take a symbol in addition to a string. This is to avoid confusing newbies, and to be consistent with the fact that other options like :foreign_key already allow a symbol or a string.
+
+<ruby>
+has_many :clients, :class_name => :Client # Note that the symbol need to be capitalized
+</ruby>
+
+* In development mode, <tt>db:drop</tt> also drops the test database in order to be symmetric with <tt>db:create</tt>.
+
+* Case-insensitive uniqueness validation avoids calling LOWER in MySQL when the column already uses a case-insensitive collation.
+
+* Transactional fixtures enlist all active database connections. You can test models on different connections without disabling transactional fixtures.
+
+* Add +first_or_create+, +first_or_create!+, +first_or_initialize+ methods to Active Record. This is a better approach over the old +find_or_create_by+ dynamic methods because it's clearer which arguments are used to find the record and which are used to create it.
+
+<ruby>
+User.where(:first_name => "Scarlett").first_or_create!(:last_name => "Johansson")
+</ruby>
+
+* Added a <tt>with_lock</tt> method to Active Record objects, which starts a transaction, locks the object (pessimistically) and yields to the block. The method takes one (optional) parameter and passes it to +lock!+.
+
+This makes it possible to write the following:
+
+<ruby>
+class Order < ActiveRecord::Base
+ def cancel!
+ transaction do
+ lock!
+ # ... cancelling logic
+ end
+ end
+end
+</ruby>
+
+as:
+
+<ruby>
+class Order < ActiveRecord::Base
+ def cancel!
+ with_lock do
+ # ... cancelling logic
+ end
+ end
+end
+</ruby>
+
+h4(#activerecord_deprecations). Deprecations
+
+* Automatic closure of connections in threads is deprecated. For example the following code is deprecated:
+
+<ruby>
+Thread.new { Post.find(1) }.join
+</ruby>
+
+It should be changed to close the database connection at the end of the thread:
+
+<ruby>
+Thread.new {
+ Post.find(1)
+ Post.connection.close
+}.join
+</ruby>
+
+Only people who spawn threads in their application code need to worry about this change.
+
+* The +set_table_name+, +set_inheritance_column+, +set_sequence_name+, +set_primary_key+, +set_locking_column+ methods are deprecated. Use an assignment method instead. For example, instead of +set_table_name+, use <tt>self.table_name=</tt>.
+
+<ruby>
+class Project < ActiveRecord::Base
+ self.table_name = "project"
+end
+</ruby>
+
+Or define your own <tt>self.table_name</tt> method:
+
+<ruby>
+class Post < ActiveRecord::Base
+ def self.table_name
+ "special_" + super
+ end
+end
+
+Post.table_name # => "special_posts"
+
+</ruby>
+
+h3. Active Model
+
+* Add <tt>ActiveModel::Errors#added?</tt> to check if a specific error has been added.
+
+* Add ability to define strict validations with <tt>strict => true</tt> that always raises exception when fails.
+
+* Provide mass_assignment_sanitizer as an easy API to replace the sanitizer behavior. Also support both :logger (default) and :strict sanitizer behavior.
+
+h4(#activemodel_deprecations). Deprecations
+
+* Deprecated <tt>define_attr_method</tt> in <tt>ActiveModel::AttributeMethods</tt> because this only existed to support methods like +set_table_name+ in Active Record, which are themselves being deprecated.
+
+* Deprecated <tt>Model.model_name.partial_path</tt> in favor of <tt>model.to_partial_path</tt>.
+
+h3. Active Resource
+
+* Redirect responses: 303 See Other and 307 Temporary Redirect now behave like 301 Moved Permanently and 302 Found.
+
+h3. Active Support
+
+* Added <tt>ActiveSupport:TaggedLogging</tt> that can wrap any standard +Logger+ class to provide tagging capabilities.
+
+<ruby>
+Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
+
+Logger.tagged("BCX") { Logger.info "Stuff" }
+# Logs "[BCX] Stuff"
+
+Logger.tagged("BCX", "Jason") { Logger.info "Stuff" }
+# Logs "[BCX] [Jason] Stuff"
+
+Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } }
+# Logs "[BCX] [Jason] Stuff"
+</ruby>
+
+* The +beginning_of_week+ method in +Date+, +Time+ and +DateTime+ accepts an optional argument representing the day in which the week is assumed to start.
+
+* <tt>ActiveSupport::Notifications.subscribed</tt> provides subscriptions to events while a block runs.
+
+* Defined new methods <tt>Module#qualified_const_defined?</tt>, <tt>Module#qualified_const_get</tt> and <tt>Module#qualified_const_set</tt> that are analogous to the corresponding methods in the standard API, but accept qualified constant names.
+
+* Added +#deconstantize+ which complements +#demodulize+ in inflections. This removes the rightmost segment in a qualified constant name.
+
+* Added <tt>safe_constantize</tt> that constantizes a string but returns +nil+ instead of raising an exception if the constant (or part of it) does not exist.
+
+* <tt>ActiveSupport::OrderedHash</tt> is now marked as extractable when using <tt>Array#extract_options!</tt>.
+
+* Added <tt>Array#prepend</tt> as an alias for <tt>Array#unshift</tt> and <tt>Array#append</tt> as an alias for <tt>Array#<<</tt>.
+
+* The definition of a blank string for Ruby 1.9 has been extended to Unicode whitespace. Also, in Ruby 1.8 the ideographic space U+3000 is considered to be whitespace.
+
+* The inflector understands acronyms.
+
+* Added <tt>Time#all_day</tt>, <tt>Time#all_week</tt>, <tt>Time#all_quarter</tt> and <tt>Time#all_year</tt> as a way of generating ranges.
+
+<ruby>
+Event.where(:created_at => Time.now.all_week)
+Event.where(:created_at => Time.now.all_day)
+</ruby>
+
+* Added <tt>instance_accessor: false</tt> as an option to <tt>Class#cattr_accessor</tt> and friends.
+
+* <tt>ActiveSupport::OrderedHash</tt> now has different behavior for <tt>#each</tt> and <tt>#each_pair</tt> when given a block accepting its parameters with a splat.
+
+* Added <tt>ActiveSupport::Cache::NullStore</tt> for use in development and testing.
+
+* Removed <tt>ActiveSupport::SecureRandom</tt> in favor of <tt>SecureRandom</tt> from the standard library.
+
+h4(#activesupport_deprecations). Deprecations
+
+* +ActiveSupport::Base64+ is deprecated in favor of <tt>::Base64</tt>.
+
+* Deprecated <tt>ActiveSupport::Memoizable</tt> in favor of Ruby memoization pattern.
+
+* <tt>Module#synchronize</tt> is deprecated with no replacement. Please use monitor from ruby's standard library.
+
+* Deprecated <tt>ActiveSupport::MessageEncryptor#encrypt</tt> and <tt>ActiveSupport::MessageEncryptor#decrypt</tt>.
+
+* <tt>ActiveSupport::BufferedLogger#silence</tt> is deprecated. If you want to squelch logs for a certain block, change the log level for that block.
+
+* <tt>ActiveSupport::BufferedLogger#open_log</tt> is deprecated. This method should not have been public in the first place.
+
+* <tt>ActiveSupport::BufferedLogger's</tt> behavior of automatically creating the directory for your log file is deprecated. Please make sure to create the directory for your log file before instantiating.
+
+* <tt>ActiveSupport::BufferedLogger#auto_flushing</tt> is deprecated. Either set the sync level on the underlying file handle like this. Or tune your filesystem. The FS cache is now what controls flushing.
+
+<ruby>
+f = File.open('foo.log', 'w')
+f.sync = true
+ActiveSupport::BufferedLogger.new f
+</ruby>
+
+* <tt>ActiveSupport::BufferedLogger#flush</tt> is deprecated. Set sync on your filehandle, or tune your filesystem.
+
+h3. Credits
+
+See the "full list of contributors to Rails":http://contributors.rubyonrails.org/ for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them.
+
+Rails 3.2 Release Notes were compiled by "Vijay Dev":https://github.com/vijaydev.
diff --git a/railties/guides/source/_license.html.erb b/railties/guides/source/_license.html.erb
new file mode 100644
index 0000000000..00b4466f50
--- /dev/null
+++ b/railties/guides/source/_license.html.erb
@@ -0,0 +1,2 @@
+<p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0</a> License</p>
+<p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p>
diff --git a/railties/guides/source/_welcome.html.erb b/railties/guides/source/_welcome.html.erb
new file mode 100644
index 0000000000..9d2e9c1d68
--- /dev/null
+++ b/railties/guides/source/_welcome.html.erb
@@ -0,0 +1,19 @@
+<h2>Ruby on Rails Guides (<%= @version %>)</h2>
+
+<% if @edge %>
+<p>
+ These are <b>Edge Guides</b>, based on the current <a href="https://github.com/rails/rails/tree/<%= @version %>">master</a> branch.
+</p>
+<p>
+ If you are looking for the ones for the stable version, please check
+ <a href="http://guides.rubyonrails.org">http://guides.rubyonrails.org</a> instead.
+</p>
+<% else %>
+<p>
+ These are the new guides for Rails 3.2 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>.
+ These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.
+</p>
+<% end %>
+<p>
+ The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>.
+</p>
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index b34c223b31..bc85f07ecc 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -16,7 +16,7 @@ h3. What Does a Controller Do?
Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
-For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
+For most conventional "RESTful":http://en.wikipedia.org/wiki/Representational_state_transfer applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates data from the user to the model.
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index ad5b848d2c..26c95be031 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -362,21 +362,14 @@ When using named routes you only need to supply the +:host+:
Email clients have no web context and so paths have no base URL to form complete web addresses. Thus, when using named routes only the "_url" variant makes sense.
-It is also possible to set a default host that will be used in all mailers by setting the +:host+ option in the +ActionMailer::Base.default_url_options+ hash as follows:
+It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt> option as a configuration option in <tt>config/application.rb</tt>:
<ruby>
-class UserMailer < ActionMailer::Base
- default_url_options[:host] = "example.com"
-
- def welcome_email(user)
- @user = user
- @url = user_url(@user)
- mail(:to => user.email,
- :subject => "Welcome to My Awesome Site")
- end
-end
+config.action_mailer.default_url_options = { :host => "example.com" }
</ruby>
+If you use this setting, you should pass the <tt>:only_path => false</tt> option when using +url_for+. This will ensure that absolute URLs are generated because the +url_for+ view helper will, by default, generate relative URLs when a <tt>:host</tt> option isn't explicitly provided.
+
h4. Sending Multipart Emails
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.erb+ and +welcome_email.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile
index 40cde6ad84..2c0b81121f 100644
--- a/railties/guides/source/action_view_overview.textile
+++ b/railties/guides/source/action_view_overview.textile
@@ -16,7 +16,7 @@ Action View and Action Controller are the two major components of Action Pack. I
Action View templates are written using embedded Ruby in tags mingled with HTML. To avoid cluttering the templates with boilerplate code, a number of helper classes provide common behavior for forms, dates, and strings. It's also easy to add new helpers to your application as it evolves.
-Note: Some features of Action View are tied to Active Record, but that doesn't mean that Action View depends on Active Record. Action View is an independent package that can be used with any sort of backend.
+NOTE. Some features of Action View are tied to Active Record, but that doesn't mean that Action View depends on Active Record. Action View is an independent package that can be used with any sort of backend.
h3. Using Action View with Rails
@@ -570,7 +570,7 @@ stylesheet_link_tag :monkey # =>
h5. auto_discovery_link_tag
-Returns a link tag that browsers and news readers can use to auto-detect an RSS or ATOM feed.
+Returns a link tag that browsers and news readers can use to auto-detect an RSS or Atom feed.
<ruby>
auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "RSS Feed"}) # =>
@@ -585,6 +585,14 @@ Computes the path to an image asset in the +public/images+ directory. Full paths
image_path("edit.png") # => /images/edit.png
</ruby>
+h5. image_url
+
+Computes the url to an image asset in the +public/images+ directory. This will call +image_path+ internally and merge with your current host or your asset host.
+
+<ruby>
+image_url("edit.png") # => http://www.example.com/images/edit.png
+</ruby>
+
h5. image_tag
Returns an html image tag for the source. The source can be a full path or a file that exists in your +public/images+ directory.
@@ -629,6 +637,14 @@ Computes the path to a JavaScript asset in the +public/javascripts+ directory. I
javascript_path "common" # => /javascripts/common.js
</ruby>
+h5. javascript_url
+
+Computes the url to a JavaScript asset in the +public/javascripts+ directory. This will call +javascript_path+ internally and merge with your current host or your asset host.
+
+<ruby>
+javascript_url "common" # => http://www.example.com/javascripts/common.js
+</ruby>
+
h5. stylesheet_link_tag
Returns a stylesheet link tag for the sources specified as arguments. If you don't specify an extension, +.css+ will be appended automatically.
@@ -659,11 +675,19 @@ Computes the path to a stylesheet asset in the +public/stylesheets+ directory. I
stylesheet_path "application" # => /stylesheets/application.css
</ruby>
+h5. stylesheet_url
+
+Computes the url to a stylesheet asset in the +public/stylesheets+ directory. This will call +stylesheet_path+ internally and merge with your current host or your asset host.
+
+<ruby>
+stylesheet_url "application" # => http://www.example.com/stylesheets/application.css
+</ruby>
+
h4. AtomFeedHelper
h5. atom_feed
-This helper makes building an ATOM feed easy. Here's a full usage example:
+This helper makes building an Atom feed easy. Here's a full usage example:
*config/routes.rb*
@@ -1124,6 +1148,79 @@ If <tt>@post.author_id</tt> is 1, this would return:
</select>
</html>
+h5. collection_radio_buttons
+
+Returns +radio_button+ tags for the collection of existing return values of +method+ for +object+'s class.
+
+Example object structure for use with this method:
+
+<ruby>
+class Post < ActiveRecord::Base
+ belongs_to :author
+end
+
+class Author < ActiveRecord::Base
+ has_many :posts
+ def name_with_initial
+ "#{first_name.first}. #{last_name}"
+ end
+end
+</ruby>
+
+Sample usage (selecting the associated Author for an instance of Post, +@post+):
+
+<ruby>
+collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
+</ruby>
+
+If <tt>@post.author_id</tt> is 1, this would return:
+
+<html>
+<input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
+<label for="post_author_id_1">D. Heinemeier Hansson</label>
+<input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
+<label for="post_author_id_2">D. Thomas</label>
+<input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
+<label for="post_author_id_3">M. Clark</label>
+</html>
+
+h5. collection_check_boxes
+
+Returns +check_box+ tags for the collection of existing return values of +method+ for +object+'s class.
+
+Example object structure for use with this method:
+
+<ruby>
+class Post < ActiveRecord::Base
+ has_and_belongs_to_many :author
+end
+
+class Author < ActiveRecord::Base
+ has_and_belongs_to_many :posts
+ def name_with_initial
+ "#{first_name.first}. #{last_name}"
+ end
+end
+</ruby>
+
+Sample usage (selecting the associated Authors for an instance of Post, +@post+):
+
+<ruby>
+collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
+</ruby>
+
+If <tt>@post.author_ids</tt> is [1], this would return:
+
+<html>
+<input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
+<label for="post_author_ids_1">D. Heinemeier Hansson</label>
+<input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
+<label for="post_author_ids_2">D. Thomas</label>
+<input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
+<label for="post_author_ids_3">M. Clark</label>
+<input name="post[author_ids][]" type="hidden" value="" />
+</html>
+
h5. country_options_for_select
Returns a string of option tags for pretty much any country in the world.
diff --git a/railties/guides/source/active_model_basics.textile b/railties/guides/source/active_model_basics.textile
index 9c8ad24cee..98b3533000 100644
--- a/railties/guides/source/active_model_basics.textile
+++ b/railties/guides/source/active_model_basics.textile
@@ -56,7 +56,7 @@ class Person
before_update :reset_me
def update
- _run_update_callbacks do
+ run_callbacks(:update) do
# This will call when we are trying to call update on object.
end
end
diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile
index 66ad7b0255..487f8b70f9 100644
--- a/railties/guides/source/active_record_basics.textile
+++ b/railties/guides/source/active_record_basics.textile
@@ -101,11 +101,11 @@ h3. Overriding the Naming Conventions
What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions.
-You can use the +ActiveRecord::Base.set_table_name+ method to specify the table name that should be used:
+You can use the +ActiveRecord::Base.table_name=+ method to specify the table name that should be used:
<ruby>
class Product < ActiveRecord::Base
- set_table_name "PRODUCT"
+ self.table_name = "PRODUCT"
end
</ruby>
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 2e1f89cb78..21bbc64255 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -8,6 +8,7 @@ This guide covers different ways to retrieve data from the database using Active
* Use dynamic finders methods
* Check for the existence of particular records
* Perform various calculations on Active Record models
+* Run EXPLAIN on relations
endprologue.
@@ -201,7 +202,7 @@ end
But this approach becomes increasingly impractical as the table size increases, since +User.all.each+ instructs Active Record to fetch _the entire table_ in a single pass, build a model object per row, and then keep the entire array of model objects in memory. Indeed, if we have a large number of records, the entire collection may exceed the amount of memory available.
-Rails provides two methods that address this problem by dividing records into memory-friendly batches for processing. The first method, +find_each+, retrieves a batch of records and then yields _each_ record to the block individually as a model. The second method, +find_in_batches+, retrieves a batch of records and then yields _the entire batch_ to the block as an array of models.
+Rails provides two methods that address this problem by dividing records into memory-friendly batches for processing. The first method, +find_each+, retrieves a batch of records and then yields _each_ record to the block individually as a model. The second method, +find_in_batches+, retrieves a batch of records and then yields _the entire batch_ to the block as an array of models.
TIP: The +find_each+ and +find_in_batches+ methods are intended for use in the batch processing of a large number of records that wouldn't fit in memory all at once. If you just need to loop over a thousand records the regular find methods are the preferred option.
@@ -403,6 +404,8 @@ Or ordering by multiple fields:
<ruby>
Client.order("orders_count ASC, created_at DESC")
+# OR
+Client.order("orders_count ASC", "created_at DESC")
</ruby>
h3. Selecting Specific Fields
@@ -435,10 +438,26 @@ ActiveModel::MissingAttributeError: missing attribute: <attribute>
Where +&lt;attribute&gt;+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
-You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this:
+If you would like to only grab a single record per unique value in a certain field, you can use +uniq+:
<ruby>
-Client.select("DISTINCT(name)")
+Client.select(:name).uniq
+</ruby>
+
+This would generate SQL like:
+
+<sql>
+SELECT DISTINCT name FROM clients
+</sql>
+
+You can also remove the uniqueness constraint:
+
+<ruby>
+query = Client.select(:name).uniq
+# => Returns unique names
+
+query.uniq(false)
+# => Returns all names, even if there are duplicates
</ruby>
h3. Limit and Offset
@@ -589,9 +608,33 @@ SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
This method accepts *no* arguments.
+h3. Null Relation
+
+The +none+ method returns a chainable relation with no records. Any subsequent conditions chained to the returned relation will continue generating empty relations. This is useful in scenarios where you need a chainable response to a method or a scope that could return zero results.
+
+<ruby>
+Post.none # returns an empty Relation and fires no queries.
+</ruby>
+
+<ruby>
+# The visible_posts method below is expected to return a Relation.
+@posts = current_user.visible_posts.where(:name => params[:name])
+
+def visible_posts
+ case role
+ when 'Country Manager'
+ Post.where(:country => country)
+ when 'Reviewer'
+ Post.published
+ when 'Bad User'
+ Post.none # => returning [] or nil breaks the caller code in this case
+ end
+end
+</ruby>
+
h3. Readonly Objects
-Active Record provides +readonly+ method on a relation to explicitly disallow modification or deletion of any of the returned object. Any attempt to alter or destroy a readonly record will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception.
+Active Record provides +readonly+ method on a relation to explicitly disallow modification of any of the returned objects. Any attempt to alter a readonly record will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception.
<ruby>
client = Client.readonly.first
@@ -631,15 +674,13 @@ c2.save # Raises an ActiveRecord::StaleObjectError
You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict.
-NOTE: You must ensure that your database schema defaults the +lock_version+ column to +0+.
-
This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
-To override the name of the +lock_version+ column, +ActiveRecord::Base+ provides a class method called +set_locking_column+:
+To override the name of the +lock_version+ column, +ActiveRecord::Base+ provides a class attribute called +locking_column+:
<ruby>
class Client < ActiveRecord::Base
- set_locking_column :lock_client_column
+ self.locking_column = :lock_client_column
end
</ruby>
@@ -675,6 +716,17 @@ Item.transaction do
end
</ruby>
+If you already have an instance of your model, you can start a transaction and acquire the lock in one go using the following code:
+
+<ruby>
+item = Item.first
+item.with_lock do
+ # This block is called within a transaction,
+ # item is already locked.
+ item.increment!(:views)
+end
+</ruby>
+
h3. Joining Tables
Active Record provides a finder method called +joins+ for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to use the +joins+ method.
@@ -741,7 +793,7 @@ SELECT categories.* FROM categories
INNER JOIN posts ON posts.category_id = categories.id
</sql>
-Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)").
+Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)").
h5. Joining Multiple Associations
@@ -937,7 +989,7 @@ If you're working with dates or times within scopes, due to how they are evaluat
<ruby>
class Post < ActiveRecord::Base
- scope :last_week, lambda { where("created_at < ?", Time.zone.now ) }
+ scope :created_before_now, lambda { where("created_at < ?", Time.zone.now ) }
end
</ruby>
@@ -949,21 +1001,21 @@ When a +lambda+ is used for a +scope+, it can take arguments:
<ruby>
class Post < ActiveRecord::Base
- scope :1_week_before, lambda { |time| where("created_at < ?", time)
+ scope :created_before, lambda { |time| where("created_at < ?", time) }
end
</ruby>
This may then be called using this:
<ruby>
-Post.1_week_before(Time.zone.now)
+Post.created_before(Time.zone.now)
</ruby>
However, this is just duplicating the functionality that would be provided to you by a class method.
<ruby>
class Post < ActiveRecord::Base
- def self.1_week_before(time)
+ def self.created_before(time)
where("created_at < ?", time)
end
end
@@ -972,7 +1024,7 @@ end
Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects:
<ruby>
-category.posts.1_week_before(time)
+category.posts.created_before(time)
</ruby>
h4. Working with scopes
@@ -1129,6 +1181,30 @@ h3. +select_all+
Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
</ruby>
+h3. +pluck+
+
+<tt>pluck</tt> can be used to query a single column from the underlying table of a model. It accepts a column name as argument and returns an array of values of the specified column with the corresponding data type.
+
+<ruby>
+Client.where(:active => true).pluck(:id)
+# SELECT id FROM clients WHERE active = 1
+
+Client.uniq.pluck(:role)
+# SELECT DISTINCT role FROM clients
+</ruby>
+
++pluck+ makes it possible to replace code like
+
+<ruby>
+Client.select(:id).map { |c| c.id }
+</ruby>
+
+with
+
+<ruby>
+Client.pluck(:id)
+</ruby>
+
h3. Existence of Objects
If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+.
@@ -1258,3 +1334,122 @@ Client.sum("orders_count")
</ruby>
For options, please see the parent section, "Calculations":#calculations.
+
+h3. Running EXPLAIN
+
+You can run EXPLAIN on the queries triggered by relations. For example,
+
+<ruby>
+User.where(:id => 1).joins(:posts).explain
+</ruby>
+
+may yield
+
+<plain>
+EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
+<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus>
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus>
+| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
+<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus>
+2 rows in set (0.00 sec)
+</plain>
+
+under MySQL.
+
+Active Record performs a pretty printing that emulates the one of the database
+shells. So, the same query running with the PostgreSQL adapter would yield instead
+
+<plain>
+EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
+ Join Filter: (posts.user_id = users.id)
+ -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
+ Index Cond: (id = 1)
+ -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
+ Filter: (posts.user_id = 1)
+(6 rows)
+</plain>
+
+Eager loading may trigger more than one query under the hood, and some queries
+may need the results of previous ones. Because of that, +explain+ actually
+executes the query, and then asks for the query plans. For example,
+
+<ruby>
+User.where(:id => 1).includes(:posts).explain
+</ruby>
+
+yields
+
+<plain>
+EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
+<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus>
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus>
+| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus>
+1 row in set (0.00 sec)
+
+EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1)
+<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus>
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus>
+| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
+<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus>
+1 row in set (0.00 sec)
+</plain>
+
+under MySQL.
+
+h4. Automatic EXPLAIN
+
+Active Record is able to run EXPLAIN automatically on slow queries and log its
+output. This feature is controlled by the configuration parameter
+
+<ruby>
+config.active_record.auto_explain_threshold_in_seconds
+</ruby>
+
+If set to a number, any query exceeding those many seconds will have its EXPLAIN
+automatically triggered and logged. In the case of relations, the threshold is
+compared to the total time needed to fetch records. So, a relation is seen as a
+unit of work, no matter whether the implementation of eager loading involves
+several queries under the hood.
+
+A threshold of +nil+ disables automatic EXPLAINs.
+
+The default threshold in development mode is 0.5 seconds, and +nil+ in test and
+production modes.
+
+INFO. Automatic EXPLAIN gets disabled if Active Record has no logger, regardless
+of the value of the threshold.
+
+h5. Disabling Automatic EXPLAIN
+
+Automatic EXPLAIN can be selectively silenced with +ActiveRecord::Base.silence_auto_explain+:
+
+<ruby>
+ActiveRecord::Base.silence_auto_explain do
+ # no automatic EXPLAIN is triggered here
+end
+</ruby>
+
+That may be useful for queries you know are slow but fine, like a heavyweight
+report of an admin interface.
+
+As its name suggests, +silence_auto_explain+ only silences automatic EXPLAINs.
+Explicit calls to +ActiveRecord::Relation#explain+ run.
+
+h4. Interpreting EXPLAIN
+
+Interpretation of the output of EXPLAIN is beyond the scope of this guide. The
+following pointers may be helpful:
+
+* SQLite3: "EXPLAIN QUERY PLAN":http://www.sqlite.org/eqp.html
+
+* MySQL: "EXPLAIN Output Format":http://dev.mysql.com/doc/refman/5.6/en/explain-output.html
+
+* PostgreSQL: "Using EXPLAIN":http://www.postgresql.org/docs/current/static/using-explain.html
diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile
index 665e7f9ccc..15d24f9ac1 100644
--- a/railties/guides/source/active_record_validations_callbacks.textile
+++ b/railties/guides/source/active_record_validations_callbacks.textile
@@ -614,7 +614,7 @@ As shown in the example, you can also combine standard validations with your own
h4. Custom Methods
-You can also create methods that verify the state of your models and add messages to the +errors+ collection when they are invalid. You must then register these methods by using one or more of the +validate+, +validate_on_create+ or +validate_on_update+ class methods, passing in the symbols for the validation methods' names.
+You can also create methods that verify the state of your models and add messages to the +errors+ collection when they are invalid. You must then register these methods by using the +validate+ class method, passing in the symbols for the validation methods' names.
You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
@@ -637,12 +637,24 @@ class Invoice < ActiveRecord::Base
end
</ruby>
+By default such validations will run every time you call +valid?+. It is also possible to control when to run these custom validations by giving an +:on+ option to the +validate+ method, with either: +:create+ or +:update+.
+
+<ruby>
+class Invoice < ActiveRecord::Base
+ validate :active_customer, :on => :create
+
+ def active_customer
+ errors.add(:customer_id, "is not active") unless customer.active?
+ end
+end
+</ruby>
+
You can even create your own validation helpers and reuse them in several different models. For example, an application that manages surveys may find it useful to express that a certain field corresponds to a set of choices:
<ruby>
ActiveRecord::Base.class_eval do
def self.validates_as_choice(attr_name, n, options={})
- validates attr_name, :inclusion => { {:in => 1..n}.merge(options) }
+ validates attr_name, :inclusion => { { :in => 1..n }.merge!(options) }
end
end
</ruby>
@@ -659,7 +671,7 @@ h3. Working with Validation Errors
In addition to the +valid?+ and +invalid?+ methods covered earlier, Rails provides a number of methods for working with the +errors+ collection and inquiring about the validity of objects.
-The following is a list of the most commonly used methods. Please refer to the +ActiveRecord::Errors+ documentation for a list of all the available methods.
+The following is a list of the most commonly used methods. Please refer to the +ActiveModel::Errors+ documentation for a list of all the available methods.
h4(#working_with_validation_errors-errors). +errors+
@@ -796,17 +808,9 @@ person.errors.size # => 0
h3. Displaying Validation Errors in the View
-Rails maintains an official plugin, DynamicForm, that provides helpers to display the error messages of your models in your view templates. You can install it as a plugin or as a Gem.
+"DynamicForm":https://github.com/joelmoss/dynamic_form provides helpers to display the error messages of your models in your view templates.
-h4. Installing as a plugin
-
-<shell>
-$ rails plugin install git://github.com/joelmoss/dynamic_form.git
-</shell>
-
-h4. Installing as a Gem
-
-Add this line in your Gemfile:
+You can install it as a gem by adding this line to your Gemfile:
<ruby>
gem "dynamic_form"
@@ -897,13 +901,8 @@ Below is a simple example where we change the Rails behavior to always display t
<ruby>
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
- if instance.error_message.kind_of?(Array)
- %(#{html_tag}<span class="validation-error">&nbsp;
- #{instance.error_message.join(',')}</span>).html_safe
- else
- %(#{html_tag}<span class="validation-error">&nbsp;
- #{instance.error_message}</span>).html_safe
- end
+ errors = Array(instance.error_message).join(',')
+ %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe
end
</ruby>
@@ -957,6 +956,7 @@ h4. Creating an Object
* +before_validation+
* +after_validation+
* +before_save+
+* +around_save+
* +before_create+
* +around_create+
* +after_create+
@@ -967,6 +967,7 @@ h4. Updating an Object
* +before_validation+
* +after_validation+
* +before_save+
+* +around_save+
* +before_update+
* +around_update+
* +after_update+
@@ -975,8 +976,8 @@ h4. Updating an Object
h4. Destroying an Object
* +before_destroy+
-* +after_destroy+
* +around_destroy+
+* +after_destroy+
WARNING. +after_save+ runs both on create and update, but always _after_ the more specific callbacks +after_create+ and +after_update+, no matter the order in which the macro calls were executed.
@@ -986,15 +987,15 @@ The +after_initialize+ callback will be called whenever an Active Record object
The +after_find+ callback will be called whenever Active Record loads a record from the database. +after_find+ is called before +after_initialize+ if both are defined.
-The +after_initialize+ and +after_find+ callbacks are a bit different from the others. They have no +before_*+ counterparts, and they are registered simply by defining them as regular methods with predefined names. If you try to register +after_initialize+ or +after_find+ using macro-style class methods, they will just be ignored. This behavior is due to performance reasons, since +after_initialize+ and +after_find+ will both be called for each record found in the database, which would otherwise significantly slow down the queries.
+The +after_initialize+ and +after_find+ callbacks have no +before_*+ counterparts, but they can be registered just like the other Active Record callbacks.
<ruby>
class User < ActiveRecord::Base
- def after_initialize
+ after_initialize do |user|
puts "You have initialized an object!"
end
- def after_find
+ after_find do |user|
puts "You have found an object!"
end
end
@@ -1021,7 +1022,7 @@ The following methods trigger callbacks:
* +increment!+
* +save+
* +save!+
-* +save(false)+
+* +save(:validate => false)+
* +toggle!+
* +update+
* +update_attribute+
diff --git a/railties/guides/source/active_resource_basics.textile b/railties/guides/source/active_resource_basics.textile
index 851aac1a3f..37abb8a640 100644
--- a/railties/guides/source/active_resource_basics.textile
+++ b/railties/guides/source/active_resource_basics.textile
@@ -71,7 +71,7 @@ person.destroy
h3. Validations
-Module to support validation and errors with Active Resource objects. The module overrides Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned in the web service response. The module also adds an errors collection that mimics the interface of the errors provided by ActiveRecord::Errors.
+Module to support validation and errors with Active Resource objects. The module overrides Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned in the web service response. The module also adds an errors collection that mimics the interface of the errors provided by ActiveModel::Errors.
h4. Validating client side resources by overriding validation methods in base class
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index ff6c5f967f..61fdb5ccc6 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -177,19 +177,6 @@ end
NOTE: Defined in +active_support/core_ext/object/try.rb+.
-h4. +singleton_class+
-
-The method +singleton_class+ returns the singleton class of the receiver:
-
-<ruby>
-String.singleton_class # => #<Class:String>
-String.new.singleton_class # => #<Class:#<String:0x17a1d1c>>
-</ruby>
-
-WARNING: Fixnums and symbols have no singleton classes, +singleton_class+ raises +TypeError+ on them. Moreover, the singleton classes of +nil+, +true+, and +false+, are +NilClass+, +TrueClass+, and +FalseClass+, respectively.
-
-NOTE: Defined in +active_support/core_ext/kernel/singleton_class.rb+.
-
h4. +class_eval(*args, &block)+
You can evaluate code in the context of any object's singleton class using +class_eval+:
@@ -389,7 +376,7 @@ NOTE: Defined in +active_support/core_ext/object/instance_variables.rb+.
h5. +instance_values+
The method +instance_values+ returns a hash that maps instance variable names without "@" to their
-corresponding values. Keys are strings both in Ruby 1.8 and 1.9:
+corresponding values. Keys are strings:
<ruby>
class C
@@ -440,14 +427,16 @@ NOTE: Defined in +active_support/core_ext/kernel/reporting.rb+.
h4. +in?+
-The predicate +in?+ tests if an object is included in another object. An +ArgumentError+ exception will be raised if the argument passed does not respond to +include?+.
+The predicate +in?+ tests if an object is included in another object or a list of objects. An +ArgumentError+ exception will be raised if a single argument is passed and it does not respond to +include?+.
Examples of +in?+:
<ruby>
+1.in?(1,2) # => true
1.in?([1,2]) # => true
"lo".in?("hello") # => true
25.in?(30..50) # => false
+1.in?(1) # => ArgumentError
</ruby>
NOTE: Defined in +active_support/core_ext/object/inclusion.rb+.
@@ -571,7 +560,7 @@ NOTE: Defined in +active_support/core_ext/module/attr_accessor_with_default.rb+.
h5. Internal Attributes
-When you are defining an attribute in a class that is meant to be subclassed name collisions are a risk. That's remarkably important for libraries.
+When you are defining an attribute in a class that is meant to be subclassed, name collisions are a risk. That's remarkably important for libraries.
Active Support defines the macros +attr_internal_reader+, +attr_internal_writer+, and +attr_internal_accessor+. They behave like their Ruby built-in +attr_*+ counterparts, except they name the underlying instance variable in a way that makes collisions less likely.
@@ -703,7 +692,8 @@ NOTE: Defined in +active_support/core_ext/module/introspection.rb+.
h4. Constants
-The method +local_constants+ returns the names of the constants that have been defined in the receiver module:
+The method +local_constants+ returns the names of the constants that have been
+defined in the receiver module:
<ruby>
module X
@@ -715,13 +705,11 @@ module X
end
end
-X.local_constants # => ["X2", "X1", "Y"], assumes Ruby 1.8
-X::Y.local_constants # => ["X1", "Y1"], assumes Ruby 1.8
+X.local_constants # => [:X1, :X2, :Y]
+X::Y.local_constants # => [:Y1, :X1]
</ruby>
-The names are returned as strings in Ruby 1.8, and as symbols in Ruby 1.9. The method +local_constant_names+ always returns strings.
-
-WARNING: This method returns precise results in Ruby 1.9. In older versions of Ruby, however, it may miss some constants in case the same constant exists in the receiver module as well as in any of its ancestors and both constants point to the same object (objects are compared using +Object#object_id+).
+The names are returned as symbols. (The deprecated method +local_constant_names+ returns strings.)
NOTE: Defined in +active_support/core_ext/module/introspection.rb+.
@@ -748,8 +736,8 @@ Math.qualified_const_get("E") # => 2.718281828459045
</ruby>
These methods are analogous to their builtin counterparts. In particular,
-+qualified_constant_defined?+ accepts an optional second argument in 1.9
-to be able to say whether you want the predicate to look in the ancestors.
++qualified_constant_defined?+ accepts an optional second argument to be
+able to say whether you want the predicate to look in the ancestors.
This flag is taken into account for each constant in the expression while
walking down the path.
@@ -770,12 +758,12 @@ end
+qualified_const_defined?+ behaves this way:
<ruby>
-N.qualified_const_defined?("C::X", false) # => false (1.9 only)
-N.qualified_const_defined?("C::X", true) # => true (1.9 only)
-N.qualified_const_defined?("C::X") # => false in 1.8, true in 1.9
+N.qualified_const_defined?("C::X", false) # => false
+N.qualified_const_defined?("C::X", true) # => true
+N.qualified_const_defined?("C::X") # => true
</ruby>
-As the last example implies, in 1.9 the second argument defaults to true,
+As the last example implies, the second argument defaults to true,
as in +const_defined?+.
For coherence with the builtin methods only relative paths are accepted.
@@ -783,30 +771,6 @@ Absolute qualified constant names like +::Math::PI+ raise +NameError+.
NOTE: Defined in +active_support/core_ext/module/qualified_const.rb+.
-h4. Synchronization
-
-The +synchronize+ macro declares a method to be synchronized:
-
-<ruby>
-class Counter
- @@mutex = Mutex.new
- attr_reader :value
-
- def initialize
- @value = 0
- end
-
- def incr
- @value += 1 # non-atomic
- end
- synchronize :incr, :with => '@@mutex'
-end
-</ruby>
-
-The method receives the name of an action, and a +:with+ option with code. The code is evaluated in the context of the receiver each time the method is invoked, and it should evaluate to a +Mutex+ instance or any other object that responds to +synchronize+ and accepts a block.
-
-NOTE: Defined in +active_support/core_ext/module/synchronization.rb+.
-
h4. Reachable
A named module is reachable if it is stored in its corresponding constant. It means you can reach the module object via the constant.
@@ -857,7 +821,7 @@ M.name # => "M"
N = Module.new
N.name # => "N"
-Module.new.name # => "" in 1.8, nil in 1.9
+Module.new.name # => nil
</ruby>
You can check whether a module has a name with the predicate +anonymous?+:
@@ -970,18 +934,6 @@ In the previous example the macro generates +avatar_size+ rather than +size+.
NOTE: Defined in +active_support/core_ext/module/delegation.rb+
-h4. Method Names
-
-The builtin methods +instance_methods+ and +methods+ return method names as strings or symbols depending on the Ruby version. Active Support defines +instance_method_names+ and +method_names+ to be equivalent to them, respectively, but always getting strings back.
-
-For example, +ActionView::Helpers::FormBuilder+ knows this array difference is going to work no matter the Ruby version:
-
-<ruby>
-self.field_helpers = (FormHelper.instance_method_names - ['form_for'])
-</ruby>
-
-NOTE: Defined in +active_support/core_ext/module/method_names.rb+
-
h4. Redefining Methods
There are cases where you need to define a method with +define_method+, but don't know whether a method with that name already exists. If it does, a warning is issued if they are enabled. No big deal, but not clean either.
@@ -1299,7 +1251,7 @@ NOTE: Defined in +active_support/core_ext/string/output_safety.rb+.
h5. Transformation
-As a rule of thumb, except perhaps for concatenation as explained above, any method that may change a string gives you an unsafe string. These are +donwcase+, +gsub+, +strip+, +chomp+, +underscore+, etc.
+As a rule of thumb, except perhaps for concatenation as explained above, any method that may change a string gives you an unsafe string. These are +downcase+, +gsub+, +strip+, +chomp+, +underscore+, etc.
In the case of in-place transformations like +gsub!+ the receiver itself becomes unsafe.
@@ -1849,43 +1801,6 @@ NOTE: Defined in +active_support/core_ext/string/inflections.rb+.
h4(#string-conversions). Conversions
-h5. +ord+
-
-Ruby 1.9 defines +ord+ to be the codepoint of the first character of the receiver. Active Support backports +ord+ for single-byte encodings like ASCII or ISO-8859-1 in Ruby 1.8:
-
-<ruby>
-"a".ord # => 97
-"à".ord # => 224, in ISO-8859-1
-</ruby>
-
-In Ruby 1.8 +ord+ doesn't work in general in UTF8 strings, use the multibyte support in Active Support for that:
-
-<ruby>
-"a".mb_chars.ord # => 97
-"à".mb_chars.ord # => 224, in UTF8
-</ruby>
-
-Note that the 224 is different in both examples. In ISO-8859-1 "à" is represented as a single byte, 224. Its single-character representation in UTF8 has two bytes, namely 195 and 160, but its Unicode codepoint is 224. If we call +ord+ on the UTF8 string "à" the return value will be 195 in Ruby 1.8. That is not an error, because UTF8 is unsupported, the call itself would be bogus.
-
-INFO: +ord+ is equivalent to +getbyte(0)+.
-
-NOTE: Defined in +active_support/core_ext/string/conversions.rb+.
-
-h5. +getbyte+
-
-Active Support backports +getbyte+ from Ruby 1.9:
-
-<ruby>
-"foo".getbyte(0) # => 102, same as "foo".ord
-"foo".getbyte(1) # => 111
-"foo".getbyte(9) # => nil
-"foo".getbyte(-1) # => 111
-</ruby>
-
-INFO: +getbyte+ is equivalent to +[]+.
-
-NOTE: Defined in +active_support/core_ext/string/conversions.rb+.
-
h5. +to_date+, +to_time+, +to_datetime+
The methods +to_date+, +to_time+, and +to_datetime+ are basically convenience wrappers around +Date._parse+:
@@ -1957,9 +1872,24 @@ The method +multiple_of?+ tests whether an integer is multiple of the argument:
NOTE: Defined in +active_support/core_ext/integer/multiple.rb+.
+h4. +ordinal+
+
+The method +ordinal+ returns the ordinal suffix string corresponding to the receiver integer:
+
+<ruby>
+1.ordinal # => "st"
+2.ordinal # => "nd"
+53.ordinal # => "rd"
+2009.ordinal # => "th"
+-21.ordinal # => "st"
+-134.ordinal # => "th"
+</ruby>
+
+NOTE: Defined in +active_support/core_ext/integer/inflections.rb+.
+
h4. +ordinalize+
-The method +ordinalize+ returns the ordinal string corresponding to the receiver integer:
+The method +ordinalize+ returns the ordinal string corresponding to the receiver integer. In comparison, note that the +ordinal+ method returns *only* the suffix string.
<ruby>
1.ordinalize # => "1st"
@@ -1972,38 +1902,12 @@ The method +ordinalize+ returns the ordinal string corresponding to the receiver
NOTE: Defined in +active_support/core_ext/integer/inflections.rb+.
-h3. Extensions to +Float+
-
-h4. +round+
-
-The built-in method +Float#round+ rounds a float to the nearest integer. In Ruby 1.9 this method takes an optional argument to let you specify a precision. Active Support adds that functionality to +round+ in previous versions of Ruby:
-
-<ruby>
-Math::E.round(4) # => 2.7183
-</ruby>
-
-NOTE: Defined in +active_support/core_ext/float/rounding.rb+.
-
h3. Extensions to +BigDecimal+
...
h3. Extensions to +Enumerable+
-h4. +group_by+
-
-Active Support redefines +group_by+ in Ruby 1.8.7 so that it returns an ordered hash as in 1.9:
-
-<ruby>
-entries_by_surname_initial = address_book.group_by do |entry|
- entry.surname.at(0).upcase
-end
-</ruby>
-
-Distinct block return values are added to the hash as they come, so that's the resulting order.
-
-NOTE: Defined in +active_support/core_ext/enumerable.rb+.
-
h4. +sum+
The method +sum+ adds the elements of an enumerable:
@@ -2051,32 +1955,6 @@ end
NOTE: Defined in +active_support/core_ext/enumerable.rb+.
-h4. +each_with_object+
-
-The +inject+ method offers iteration with an accumulator:
-
-<ruby>
-[2, 3, 4].inject(1) {|product, i| product*i } # => 24
-</ruby>
-
-The block is expected to return the value for the accumulator in the next iteration, and this makes building mutable objects a bit cumbersome:
-
-<ruby>
-[1, 2].inject({}) {|h, i| h[i] = i**2; h} # => {1 => 1, 2 => 4}
-</ruby>
-
-See that spurious "+; h+"?
-
-Active Support backports +each_with_object+ from Ruby 1.9, which addresses that use case. It iterates over the collection, passes the accumulator, and returns the accumulator when done. You normally modify the accumulator in place. The example above would be written this way:
-
-<ruby>
-[1, 2].each_with_object({}) {|i, h| h[i] = i**2} # => {1 => 1, 2 => 4}
-</ruby>
-
-WARNING. Note that the item of the collection and the accumulator come in different order in +inject+ and +each_with_object+.
-
-NOTE: Defined in +active_support/core_ext/enumerable.rb+.
-
h4. +index_by+
The method +index_by+ generates a hash with the elements of an enumerable indexed by some key.
@@ -2148,20 +2026,6 @@ The methods +second+, +third+, +fourth+, and +fifth+ return the corresponding el
NOTE: Defined in +active_support/core_ext/array/access.rb+.
-h4. Random Access
-
-Active Support backports +sample+ from Ruby 1.9:
-
-<ruby>
-shape_type = [Circle, Square, Triangle].sample
-# => Square, for example
-
-shape_types = [Circle, Square, Triangle].sample(2)
-# => [Triangle, Circle], for example
-</ruby>
-
-NOTE: Defined in +active_support/core_ext/array/random_access.rb+.
-
h4. Adding Elements
h5. +prepend+
@@ -2388,9 +2252,6 @@ The last point is particularly worth comparing for some enumerables:
<ruby>
Array.wrap(:foo => :bar) # => [{:foo => :bar}]
Array(:foo => :bar) # => [[:foo, :bar]]
-
-Array.wrap("foo\nbar") # => ["foo\nbar"]
-Array("foo\nbar") # => ["foo\n", "bar"], in Ruby 1.8
</ruby>
There's also a related idiom that uses the splat operator:
@@ -2914,14 +2775,6 @@ WARNING: The original +Range#include?+ is still the one aliased to +Range#===+.
NOTE: Defined in +active_support/core_ext/range/include_range.rb+.
-h4. +cover?+
-
-Ruby 1.9 provides +cover?+, and Active Support defines it for previous versions as an alias for +include?+.
-
-The method +include?+ in Ruby 1.9 is different from the one in 1.8 for non-numeric ranges: instead of being based on comparisons between the value and the range's endpoints, it walks the range with +succ+ looking for value. This works better for ranges with holes, but it has different complexity and may not finish in some other cases.
-
-In Ruby 1.9 the old behavior is still available in the new +cover?+, which Active Support backports for forward compatibility. For example, Rails uses +cover?+ for ranges in +validates_inclusion_of+.
-
h4. +overlaps?+
The method +Range#overlaps?+ says whether any two given ranges have non-void intersection:
@@ -3039,15 +2892,30 @@ Active Support defines these methods as well for Ruby 1.8.
h6. +beginning_of_week+, +end_of_week+
-The methods +beginning_of_week+ and +end_of_week+ return the dates for the beginning and end of week, assuming weeks start on Monday:
+The methods +beginning_of_week+ and +end_of_week+ return the dates for the
+beginning and end of the week, respectively. Weeks are assumed to start on
+Monday, but that can be changed passing an argument.
<ruby>
-d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.beginning_of_week # => Mon, 03 May 2010
-d.end_of_week # => Sun, 09 May 2010
+d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
+d.beginning_of_week # => Mon, 03 May 2010
+d.beginning_of_week(:sunday) # => Sun, 02 May 2010
+d.end_of_week # => Sun, 09 May 2010
+d.end_of_week(:sunday) # => Sat, 08 May 2010
</ruby>
-+beginning_of_week+ is aliased to +monday+ and +at_beginning_of_week+. +end_of_week+ is aliased to +sunday+ and +at_end_of_week+.
++beginning_of_week+ is aliased to +at_beginning_of_week+ and +end_of_week+ is aliased to +at_end_of_week+.
+
+h6. +monday+, +sunday+
+
+The methods +monday+ and +sunday+ return the dates for the beginning and
+end of the week, respectively. Weeks are assumed to start on Monday.
+
+<ruby>
+d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
+d.monday # => Mon, 03 May 2010
+d.sunday # => Sun, 09 May 2010
+</ruby>
h6. +prev_week+, +next_week+
@@ -3272,8 +3140,10 @@ The class +DateTime+ is a subclass of +Date+ so by loading +active_support/core_
<ruby>
yesterday
tomorrow
-beginning_of_week (monday, at_beginning_of_week)
-end_on_week (at_end_of_week)
+beginning_of_week (at_beginning_of_week)
+end_of_week (at_end_of_week)
+monday
+sunday
weeks_ago
prev_week
next_week
@@ -3446,8 +3316,10 @@ ago
since (in)
beginning_of_day (midnight, at_midnight, at_beginning_of_day)
end_of_day
-beginning_of_week (monday, at_beginning_of_week)
-end_on_week (at_end_of_week)
+beginning_of_week (at_beginning_of_week)
+end_of_week (at_end_of_week)
+monday
+sunday
weeks_ago
prev_week
next_week
@@ -3572,12 +3444,6 @@ Time.utc_time(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
</ruby>
-h3. Extensions to +Process+
-
-h4. +daemon+
-
-Ruby 1.9 provides +Process.daemon+, and Active Support defines it for previous versions. It accepts the same two arguments, whether it should chdir to the root directory (default, true), and whether it should inherit the standard file descriptors from the parent (default, false).
-
h3. Extensions to +File+
h4. +atomic_write+
diff --git a/railties/guides/source/api_documentation_guidelines.textile b/railties/guides/source/api_documentation_guidelines.textile
index 99eb668513..93120c15a7 100644
--- a/railties/guides/source/api_documentation_guidelines.textile
+++ b/railties/guides/source/api_documentation_guidelines.textile
@@ -134,13 +134,6 @@ h4. Regular Font
When "true" and "false" are English words rather than Ruby keywords use a regular font:
-<ruby>
-# If <tt>reload_plugins?</tt> is false, add this to your plugin's <tt>init.rb</tt>
-# to make it reloadable:
-#
-# Dependencies.load_once_paths.delete lib_path
-</ruby>
-
h3. Description Lists
In lists of options, parameters, etc. use a hyphen between the item and its description (reads better than a colon because normally options are symbols):
diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile
index 6ff5e87b6d..ff2bd08602 100644
--- a/railties/guides/source/asset_pipeline.textile
+++ b/railties/guides/source/asset_pipeline.textile
@@ -6,7 +6,7 @@ By referring to this guide you will be able to:
* Understand what the asset pipeline is and what it does
* Properly organize your application assets
* Understand the benefits of the asset pipeline
-* Adding a pre-processor to the pipeline
+* Add a pre-processor to the pipeline
* Package assets with a gem
endprologue.
@@ -17,7 +17,7 @@ The asset pipeline provides a framework to concatenate and minify or compress Ja
Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets. Rails 3.1 is integrated with Sprockets through Action Pack which depends on the +sprockets+ gem, by default.
-By having this as a core feature of Rails, all developers can benefit from the power of having their assets pre-processed, compressed and minified by one central library, Sprockets. This is part of Rails' "fast by default" strategy as outlined by DHH in his keynote at RailsConf 2011.
+Making the asset pipeline a core feature of Rails means that all developers can benefit from the power of having their assets pre-processed, compressed and minified by one central library, Sprockets. This is part of Rails' "fast by default" strategy as outlined by DHH in his keynote at RailsConf 2011.
In Rails 3.1, the asset pipeline is enabled by default. It can be disabled in +config/application.rb+ by putting this line inside the application class definition:
@@ -25,34 +25,34 @@ In Rails 3.1, the asset pipeline is enabled by default. It can be disabled in +c
config.assets.enabled = false
</ruby>
-You can also disable it while creating a new application by passing the <tt>--skip-sprockets</tt> option.
+You can also disable the asset pipeline while creating a new application by passing the <tt>--skip-sprockets</tt> option.
<plain>
rails new appname --skip-sprockets
</plain>
-It is recommended that you use the defaults for all new apps.
+You should use the defaults for all new applications unless you have a specific reason to avoid the asset pipeline.
h4. Main Features
-The first feature of the pipeline is to concatenate assets. This is important in a production environment, as it reduces the number of requests that a browser must make to render a web page.
+The first feature of the pipeline is to concatenate assets. This is important in a production environment, because it can reduce the number of requests that a browser must make to render a web page. Web browsers are limited in the number of requests that they can make in parallel, so fewer requests can mean faster loading for your application.
-While Rails already has a feature to concatenate these types of assets by placing +:cache => true+ at the end of tags such as +javascript_include_tag+ and +stylesheet_link_tag+, it has a series of limitations. For example, it cannot generate the caches in advance, and it is not able to transparently include assets provided by third-party libraries.
+Rails 2.x introduced the ability to concatenate JavaScript and CSS assets by placing +:cache => true+ at the end of the +javascript_include_tag+ and +stylesheet_link_tag+ methods. But this technique has some limitations. For example, it cannot generate the caches in advance, and it is not able to transparently include assets provided by third-party libraries.
-The default behavior in Rails 3.1 and onward is to concatenate all files into one master file each for JS and CSS. However, you can separate files or groups of files if required (see below). In production, an MD5 fingerprint is inserted into each filename so that the file is cached by the web browser but can be invalidated if the fingerprint is altered.
+Starting with version 3.1, Rails defaults to concatenating all JavaScript files into one master +.js+ file and all CSS files into one master +.css+ file. As you'll learn later in this guide, you can customize this strategy to group files any way you like. In production, Rails inserts an MD5 fingerprint into each filename so that the file is cached by the web browser. You can invalidate the cache by altering this fingerprint, which happens automatically whenever you change the file contents..
-The second feature is to minify or compress assets. For CSS, this usually involves removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own.
+The second feature of the asset pipeline is asset minification or compression. For CSS files, this is done by removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own.
-The third feature is the ability to code these assets using another language, or language extension. These include Sass for CSS, CoffeeScript for JavaScript, and ERB for both.
+The third feature of the asset pipeline is that it allows coding assets via a higher-level language, with precompilation down to the actual assets. Supported languages include Sass for CSS, CoffeeScript for JavaScript, and ERB for both by default.
h4. What is Fingerprinting and Why Should I Care?
-Fingerprinting is a technique whereby the filenames of content that is static or infrequently updated are altered to be unique to the content contained in the file.
+Fingerprinting is a technique that makes the name of a file dependent on the contents of the file. When the file contents change, the filename is also changed. For content that is static or infrequently changed, this provides an easy way to tell whether two versions of a file are identical, even across different servers or deployment dates.
-When a filename is unique and based on its content, HTTP headers can be set to encourage caches everywhere (at ISPs, in browsers) to keep their own copy of the content. When the content is updated, the fingerprint will change and the remote clients will request the new file. This is generally known as _cache busting_.
+When a filename is unique and based on its content, HTTP headers can be set to encourage caches everywhere (whether at CDNs, at ISPs, in networking equipment, or in web browsers) to keep their own copy of the content. When the content is updated, the fingerprint will change. This will cause the remote clients to request a new copy of the content. This is generally known as _cache busting_.
-The most effective technique is to insert a hash of the content into the name, usually at the end. For example a CSS file +global.css+ is hashed and the filename is updated to incorporate the digest, for example becoming:
+The technique that Rails uses for fingerprinting is to insert a hash of the content into the name, usually at the end. For example a CSS file +global.css+ could be renamed with an MD5 digest of its contents:
<plain>
global-908e25f4bf641868d8683022a5b62f54.css
@@ -60,30 +60,32 @@ global-908e25f4bf641868d8683022a5b62f54.css
This is the strategy adopted by the Rails asset pipeline.
-Rails' old strategy was to append a query string to every asset linked with a built-in helper. In the source the generated code looked like this:
+Rails' old strategy was to append a date-based query string to every asset linked with a built-in helper. In the source the generated code looked like this:
<plain>
/stylesheets/global.css?1309495796
</plain>
-This has several disadvantages:
+The query string strategy has several disadvantages:
<ol>
<li>
- <strong>Not all caches will cache content with a query string</strong>.<br>
+ <strong>Not all caches will reliably cache content where the filename only differs by query parameters</strong>.<br>
"Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation.
</li>
<li>
<strong>The file name can change between nodes in multi-server environments.</strong><br>
- The query string in Rails is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request.
+ The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request.
+ </li>
+ <li>
+ <strong>Too much cache invalidation</strong><br />
+ When static assets are deployed with each new release of code, the mtime of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.
</li>
</ol>
-The other problem is that when static assets are deployed with each new release of code, the mtime of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.
+Fingerprinting fixes these problems by avoiding query strings, and by ensuring that filenames are consistent based on their content.
-Fingerprinting fixes these problems by avoiding query strings, and by ensuring filenames are consistent based on their content.
-
-Fingerprinting is enabled by default for production and disabled for all the others environments. You can enable or disable it in your configuration through the +config.assets.digest+ option.
+Fingerprinting is enabled by default for production and disabled for all other environments. You can enable or disable it in your configuration through the +config.assets.digest+ option.
More reading:
@@ -95,34 +97,86 @@ h3. How to Use the Asset Pipeline
In previous versions of Rails, all assets were located in subdirectories of +public+ such as +images+, +javascripts+ and +stylesheets+. With the asset pipeline, the preferred location for these assets is now the +app/assets+ directory. Files in this directory are served by the Sprockets middleware included in the sprockets gem.
-This is not to say that assets can (or should) no longer be placed in +public+; they still can be and will be served as static files by the application or web server. You would only use +app/assets+ if you wish your files to undergo some pre-processing before they are served.
+Assets can still be placed in the +public+ hierarchy. Any assets under +public+ will be served as static files by the application or web server. You should use +app/assets+ for files that must undergo some pre-processing before they are served.
-In production, the default is to precompile these files to +public/assets+ so that they can be more efficiently delivered by the web server.
+In production, Rails precompiles these files to +public/assets+ by default. The precompiled copies are then served as static assets by the web server. The files in +app/assets+ are never served directly in production.
-When a scaffold or controller is generated for the application, Rails also generates a JavaScript file (or CoffeeScript file if the +coffee-rails+ gem is in the +Gemfile+) and a Cascading Style Sheet file (or SCSS file if +sass-rails+ is in the +Gemfile+) for that controller.
+When you generate a scaffold or a controller, Rails also generates a JavaScript file (or CoffeeScript file if the +coffee-rails+ gem is in the +Gemfile+) and a Cascading Style Sheet file (or SCSS file if +sass-rails+ is in the +Gemfile+) for that controller.
-For example, if a +ProjectsController+ is generated, there will be a new file at +app/assets/javascripts/projects.js.coffee+ and another at +app/assets/stylesheets/projects.css.scss+. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as +<%= javascript_include_tag params[:controller] %>+ or +<%= stylesheet_link_tag params[:controller] %>+.
+For example, if you generate a +ProjectsController+, Rails will also add a new file at +app/assets/javascripts/projects.js.coffee+ and another at +app/assets/stylesheets/projects.css.scss+. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as +<%= javascript_include_tag params[:controller] %>+ or +<%= stylesheet_link_tag params[:controller] %>+.
-NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes.
+NOTE: You must have an "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes.
h4. Asset Organization
-Assets can be placed inside an application in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+.
+Pipeline assets can be placed inside an application in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+.
+app/assets+ is for assets that are owned by the application, such as custom images, JavaScript files or stylesheets.
+lib/assets+ is for your own libraries' code that doesn't really fit into the scope of the application or those libraries which are shared across applications.
-+vendor/assets+ is for assets that are owned by outside entities, such as code for JavaScript plugins.
++vendor/assets+ is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks.
+
+h5. Search paths
+
+When a file is referenced from a manifest or a helper, Sprockets searches the three default asset locations for it.
+
+The default locations are: +app/assets/images+ and the subdirectories +javascripts+ and +stylesheets+ in all three asset locations.
+
+For example, these files:
+
+<plain>
+app/assets/javascripts/home.js
+lib/assets/javascripts/moovinator.js
+vendor/assets/javascript/slider.js
+</plain>
+
+would be referenced in a manifest like this:
+
+<plain>
+//= require home
+//= require moovinator
+//= require slider
+</plain>
+
+Assets inside subdirectories can also be accessed.
+
+<plain>
+app/assets/javascripts/sub/something.js
+</plain>
+
+is referenced as:
+
+<plain>
+//= require sub/something
+</plain>
-All subdirectories that exist within these three locations are added to the search path for Sprockets (visible by calling +Rails.application.config.assets.paths+ in a console). When an asset is requested, these paths are traversed to see if they contain an asset matching the name specified. Once an asset has been found, it's processed by Sprockets and served.
+You can view the search path by inspecting +Rails.application.config.assets.paths+ in the Rails console.
-You can add additional (fully qualified) paths to the pipeline in +config/application.rb+. For example:
+Additional (fully qualified) paths can be added to the pipeline in +config/application.rb+. For example:
<ruby>
config.assets.paths << Rails.root.join("app", "assets", "flash")
</ruby>
+Paths are traversed in the order that they occur in the search path.
+
+It is important to note that files you want to reference outside a manifest must be added to the precompile array or they will not be available in the production environment.
+
+h5. Using index files
+
+Sprockets uses files named +index+ (with the relevant extensions) for a special purpose.
+
+For example, if you have a jQuery library with many modules, which is stored in +lib/assets/library_name+, the file +lib/assets/library_name/index.js+ serves as the manifest for all files in this library. This file could include a list of all the required files in order, or a simple <tt>require_tree</tt> directive.
+
+The library as a whole can be accessed in the site's application manifest like so:
+
+<plain>
+//= require library_name
+</plain>
+
+This simplifies maintenance and keeps things clean by allowing related code to be grouped before inclusion elsewhere.
+
h4. Coding Links to Assets
Sprockets does not add any new methods to access your assets - you still use the familiar +javascript_include_tag+ and +stylesheet_link_tag+.
@@ -138,7 +192,7 @@ In regular views you can access images in the +assets/images+ directory like thi
<%= image_tag "rails.png" %>
</erb>
-Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at +public/assets/rails.png+ it is served by the webserver.
+Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at +public/assets/rails.png+ it is served by the web server.
Alternatively, a request for a file with an MD5 hash such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ is treated the same way. How these hashes are generated is covered in the "In Production":#in-production section later on in this guide.
@@ -152,7 +206,7 @@ Images can also be organized into subdirectories if required, and they can be ac
h5. CSS and ERB
-If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+, then helpers like +asset_path+ are available in your CSS rules:
+The asset pipeline automatically evaluates ERB. This means that if you add an +erb+ extension to a CSS asset (for example, +application.css.erb+), then helpers like +asset_path+ are available in your CSS rules:
<plain>
.class { background-image: url(<%= asset_path 'image.png' %>) }
@@ -194,7 +248,7 @@ $('#logo').attr({
This writes the path to the particular asset being referenced.
-Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (eg. +application.js.coffee.erb+):
+Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (e.g., +application.js.coffee.erb+):
<plain>
$('#logo').attr src: "<%= asset_path('logo.png') %>"
@@ -202,9 +256,9 @@ $('#logo').attr src: "<%= asset_path('logo.png') %>"
h4. Manifest Files and Directives
-Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ -- instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets loads the files specified, processes them if necessary, concatenates them into one single file and then compresses them (if +Rails.application.config.assets.compress+ is true). By serving one file rather than many, the load time of pages are greatly reduced as there are fewer requests to make.
+Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ -- instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets loads the files specified, processes them if necessary, concatenates them into one single file and then compresses them (if +Rails.application.config.assets.compress+ is true). By serving one file rather than many, the load time of pages can be greatly reduced because the browser makes fewer requests.
-For example, in the default Rails application there's a +app/assets/javascripts/application.js+ file which contains the following lines:
+For example, a new Rails application includes a default +app/assets/javascripts/application.js+ file which contains the following lines:
<plain>
// ...
@@ -217,11 +271,11 @@ In JavaScript files, the directives begin with +//=+. In this case, the file is
NOTE. In Rails 3.1 the +jquery-rails+ gem provides the +jquery.js+ and +jquery_ujs.js+ files via the asset pipeline. You won't see them in the application tree.
-The +require_tree+ directive tells Sprockets to recursively include _all_ JavaScript files in this directory into the output. Only a path relative to the manifest file can be specified. There is also a +require_directory+ directive which includes all JavaScript files only in the directory specified (no nesting).
+The +require_tree+ directive tells Sprockets to recursively include _all_ JavaScript files in the specified directory into the output. These paths must be specified relative to the manifest file. You can also use the +require_directory+ directive which includes all JavaScript files only in the directory specified, without recursion.
-Directives are processed top to bottom, but the order in which files are included by +require_tree+ is unspecified. You should not rely on any particular order among those. If you need to ensure some particular JavaScript ends up above some other, require it before in the manifest. Note that the family of +require+ directives prevents files from being included twice in the output.
+Directives are processed top to bottom, but the order in which files are included by +require_tree+ is unspecified. You should not rely on any particular order among those. If you need to ensure some particular JavaScript ends up above some other in the concatenated file, require the prerequisite file first in the manifest. Note that the family of +require+ directives prevents files from being included twice in the output.
-There's also a default +app/assets/stylesheets/application.css+ file which contains these lines:
+Rails also creates a default +app/assets/stylesheets/application.css+ file which contains these lines:
<plain>
/* ...
@@ -230,15 +284,15 @@ There's also a default +app/assets/stylesheets/application.css+ file which conta
*/
</plain>
-The directives that work in the JavaScript files also work in stylesheets, obviously including stylesheets rather than JavaScript files. The +require_tree+ directive here works the same way as the JavaScript one, requiring all stylesheets from the current directory.
+The directives that work in the JavaScript files also work in stylesheets (though obviously including stylesheets rather than JavaScript files). The +require_tree+ directive in a CSS manifest works the same way as the JavaScript one, requiring all stylesheets from the current directory.
In this example +require_self+ is used. This puts the CSS contained within the file (if any) at the precise location of the +require_self+ call. If +require_self+ is called more than once, only the last call is respected.
-NOTE. If you want to use multiple Sass files, use the "Sass +@import+ rule":http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import instead of the Sprockets directives. Using Sprockets directives all Sass files exist within their own scope, making variables or mixins only available within the document they were defined in.
+NOTE. If you want to use multiple Sass files, you should generally use the "Sass +@import+ rule":http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import instead of these Sprockets directives. Using Sprockets directives all Sass files exist within their own scope, making variables or mixins only available within the document they were defined in.
You can have as many manifest files as you need. For example the +admin.css+ and +admin.js+ manifest could contain the JS and CSS files that are used for the admin section of an application.
-The same remarks about ordering made above apply. In particular, you can specify individual files and they are compiled in the order specified:
+The same remarks about ordering made above apply. In particular, you can specify individual files and they are compiled in the order specified. For example, you might concatenate three CSS files together this way:
<plain>
/* ...
@@ -253,15 +307,15 @@ h4. Preprocessing
The file extensions used on an asset determine what preprocessing is applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file are generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and an +app/assets/stylesheets/projects.css.scss+ file.
-When these files are requested, they are processed by the processors provided by the +coffee-script+ and +sass-rails+ gems and then sent back to the browser as JavaScript and CSS respectively.
+When these files are requested, they are processed by the processors provided by the +coffee-script+ and +sass+ gems and then sent back to the browser as JavaScript and CSS respectively.
-Additional layers of preprocessing can be requested by adding other extensions, where each extension is processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ is first processed as ERB, then SCSS and finally served as CSS. The same applies to a JavaScript file -- +app/assets/javascripts/projects.js.coffee.erb+ is processed as ERB, CoffeeScript, and served as JavaScript.
+Additional layers of preprocessing can be requested by adding other extensions, where each extension is processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ is first processed as ERB, then SCSS, and finally served as CSS. The same applies to a JavaScript file -- +app/assets/javascripts/projects.js.coffee.erb+ is processed as ERB, then CoffeeScript, and served as JavaScript.
Keep in mind that the order of these preprocessors is important. For example, if you called your JavaScript file +app/assets/javascripts/projects.js.erb.coffee+ then it would be processed with the CoffeeScript interpreter first, which wouldn't understand ERB and therefore you would run into problems.
h3. In Development
-In development mode assets are served as separate files in the order they are specified in the manifest file.
+In development mode, assets are served as separate files in the order they are specified in the manifest file.
This manifest +app/assets/javascripts/application.js+:
@@ -289,7 +343,7 @@ You can turn off debug mode by updating +config/environments/development.rb+ to
config.assets.debug = false
</ruby>
-When debug mode is off Sprockets concatenates and runs the necessary preprocessors on all files. With debug mode turned off the manifest above would generate instead:
+When debug mode is off, Sprockets concatenates and runs the necessary preprocessors on all files. With debug mode turned off the manifest above would generate instead:
<html>
<script src="/assets/application.js" type="text/javascript"></script>
@@ -312,7 +366,7 @@ You could potentially also enable compression in development mode as a sanity ch
h3. In Production
-In the production environment Rails uses the fingerprinting scheme outlined above. By default it is assumed that assets have been precompiled and will be served as static assets by your web server.
+In the production environment Rails uses the fingerprinting scheme outlined above. By default Rails assumes that assets have been precompiled and will be served as static assets by your web server.
During the precompilation phase an MD5 is generated from the contents of the compiled files, and inserted into the filenames as they are written to disc. These fingerprinted names are used by the Rails helpers in place of the manifest name.
@@ -330,7 +384,7 @@ generates something like this:
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" type="text/css" />
</html>
-The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which is +true+ for production, +false+ for everything else).
+The fingerprinting behavior is controlled by the setting of +config.assets.digest+ setting in Rails (which defaults to +true+ for production and +false+ for everything else).
NOTE: Under normal circumstances the default option should not be changed. If there are no digests in the filenames, and far-future headers are set, remote clients will never know to refetch the files when their content changes.
@@ -338,9 +392,9 @@ h4. Precompiling Assets
Rails comes bundled with a rake task to compile the asset manifests and other files in the pipeline to the disk.
-Compiled assets are written to the location specified in +config.assets.prefix+. The default setting will use the +public/assets+ directory.
+Compiled assets are written to the location specified in +config.assets.prefix+. By default, this is the +public/assets+ directory.
-You must use this task either during deployment or locally if you do not have write access to your production filesystem.
+You can call this task on the server during deployment to create compiled versions of your assets directly on the server. If you do not have write access to your production file system, you can call this task locally and then deploy the compiled assets.
The rake task is:
@@ -357,7 +411,7 @@ test +rake assets:precompile+ locally before deploying. It may expose bugs where
your assets reference application objects or methods, since those are still
in scope in development mode regardless of the value of this flag.
-Capistrano (v2.8.0 and above) has a recipe to handle this in deployment. Add the following line to +Capfile+:
+Capistrano (v2.8.0 and above) includes a recipe to handle this in deployment. Add the following line to +Capfile+:
<erb>
load 'deploy/assets'
@@ -369,10 +423,10 @@ It is important that this folder is shared between deployments so that remotely
NOTE. If you are precompiling your assets locally, you can use +bundle install --without assets+ on the server to avoid installing the assets gems (the gems in the assets group in the Gemfile).
-The default matcher for compiling files includes +application.js+, +application.css+ and all files that do not end in +js+ or +css+:
+The default matcher for compiling files includes +application.js+, +application.css+ and all non-JS/CSS files (i.e., +.coffee+ and +.scss+ files are *not* automatically included as they compile to JS/CSS):
<ruby>
-[ /\w<plus>\.(?!js|css).<plus>/, /application.(css|js)$/ ]
+[ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, /application.(css|js)$/ ]
</ruby>
If you have other manifests or individual stylesheets and JavaScript files to include, you can add them to the +precompile+ array:
@@ -381,7 +435,7 @@ If you have other manifests or individual stylesheets and JavaScript files to in
config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']
</erb>
-The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods and avoids handing the mapping requests back to Sprockets. A typical manifest file looks like:
+The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets. A typical manifest file looks like:
<plain>
---
@@ -404,16 +458,12 @@ NOTE: If there are missing precompiled files in production you will get an <tt>S
h5. Server Configuration
-Precompiled assets exist on the filesystem and are served directly by your webserver. They do not have far-future headers by default, so to get the benefit of fingerprinting you'll have to update your server configuration to add them.
+Precompiled assets exist on the filesystem and are served directly by your web server. They do not have far-future headers by default, so to get the benefit of fingerprinting you'll have to update your server configuration to add them.
For Apache:
<plain>
<LocationMatch "^/assets/.*$">
- # Some browsers still send conditional-GET requests if there's a
- # Last-Modified header or an ETag header even if they haven't
- # reached the expiry date sent in the Expires header.
- Header unset Last-Modified
Header unset ETag
FileETag None
# RFC says only cache for 1 year
@@ -429,16 +479,12 @@ location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
- # Some browsers still send conditional-GET requests if there's a
- # Last-Modified header or an ETag header even if they haven't
- # reached the expiry date sent in the Expires header.
- add_header Last-Modified "";
add_header ETag "";
break;
}
</plain>
-When files are precompiled, Sprockets also creates a "gzipped":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves.
+When files are precompiled, Sprockets also creates a "gzipped":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves.
Nginx is able to do this automatically enabling +gzip_static+:
@@ -459,7 +505,7 @@ This directive is available if the core module that provides this feature was co
If you're compiling nginx with Phusion Passenger you'll need to pass that option when prompted.
-Unfortunately, a robust configuration for Apache is possible but tricky, please Google around.
+A robust configuration for Apache is possible but tricky; please Google around. (Or help update this Guide if you have a good example configuration for Apache.)
h4. Live Compilation
@@ -475,9 +521,9 @@ On the first request the assets are compiled and cached as outlined in developme
Sprockets also sets the +Cache-Control+ HTTP header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
-This mode uses more memory, performs poorer than the default and is not recommended.
+This mode uses more memory, performs more poorly than the default and is not recommended.
-When deploying a production application to a system without any pre-existing JavaScript runtimes, you may want to add one to your Gemfile:
+If you are deploying a production application to a system without any pre-existing JavaScript runtimes, you may want to add one to your Gemfile:
<plain>
group :production do
@@ -501,9 +547,9 @@ The +config.assets.compress+ must be set to +true+ to enable CSS compression.
h4. JavaScript Compression
-Possible options for JavaScript compression are +:closure+, +:uglifier+ and +:yui+. These require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems respectively.
+Possible options for JavaScript compression are +:closure+, +:uglifier+ and +:yui+. These require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems, respectively.
-The default Gemfile includes "uglifier":https://github.com/lautis/uglifier. This gem wraps "UglifierJS":https://github.com/mishoo/UglifyJS (written for NodeJS) in Ruby. It compresses your code by removing white space and other magical things like changing your +if+ and +else+ statements to ternary operators where possible.
+The default Gemfile includes "uglifier":https://github.com/lautis/uglifier. This gem wraps "UglifierJS":https://github.com/mishoo/UglifyJS (written for NodeJS) in Ruby. It compresses your code by removing white space. It also includes other optimizations such as changing your +if+ and +else+ statements to ternary operators where possible.
The following line invokes +uglifier+ for JavaScript compression.
@@ -511,13 +557,13 @@ The following line invokes +uglifier+ for JavaScript compression.
config.assets.js_compressor = :uglifier
</erb>
-The +config.assets.compress+ must be set to +true+ to enable JavaScript compression
+Note that +config.assets.compress+ must be set to +true+ to enable JavaScript compression
-NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have installed a JavaScript runtime in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes.
+NOTE: You will need an "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check the "ExecJS":https://github.com/sstephenson/execjs#readme documentation for information on all of the supported JavaScript runtimes.
h4. Using Your Own Compressor
-The compressor config settings for CSS and JavaScript also take any Object. This object must have a +compress+ method that takes a string as the sole argument and it must return a string.
+The compressor config settings for CSS and JavaScript also take any object. This object must have a +compress+ method that takes a string as the sole argument and it must return a string.
<erb>
class Transformer
@@ -527,7 +573,7 @@ class Transformer
end
</erb>
-To enable this, pass a +new+ Object to the config option in +application.rb+:
+To enable this, pass a +new+ object to the config option in +application.rb+:
<erb>
config.assets.css_compressor = Transformer.new
@@ -544,20 +590,20 @@ This can be changed to something else:
config.assets.prefix = "/some_other_path"
</erb>
-This is a handy option if you have any existing project (pre Rails 3.1) that already uses this path or you wish to use this path for a new resource.
+This is a handy option if you are updating an existing project (pre Rails 3.1) that already uses this path or you wish to use this path for a new resource.
h4. X-Sendfile Headers
-The X-Sendfile header is a directive to the server to ignore the response from the application, and instead serve the file specified in the headers. This option is off by default, but can be enabled if your server supports it. When enabled, this passes responsibility for serving the file to the web server, which is faster.
+The X-Sendfile header is a directive to the web server to ignore the response from the application, and instead serve a specified file from disk. This option is off by default, but can be enabled if your server supports it. When enabled, this passes responsibility for serving the file to the web server, which is faster.
-Apache and nginx support this option which is enabled in <tt>config/environments/production.rb</tt>.
+Apache and nginx support this option, which can be enabled in <tt>config/environments/production.rb</tt>.
<erb>
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
</erb>
-WARNING: If you are upgrading an existing application and intend to use this option, take care to paste this configuration option only into +production.rb+ (and not +application.rb+) and any other environment you define with production behavior.
+WARNING: If you are upgrading an existing application and intend to use this option, take care to paste this configuration option only into +production.rb+ and any other environments you define with production behavior (not +application.rb+).
h3. How Caching Works
@@ -577,7 +623,7 @@ TODO: Registering gems on "Tilt":https://github.com/rtomayko/tilt enabling Sproc
h3. Upgrading from Old Versions of Rails
-There are two issues when upgrading. The first is moving the files to the new locations. See the section above for guidance on the correct locations for different file types.
+There are two issues when upgrading. The first is moving the files from +public/+ to the new locations. See "Asset Organization":#asset-organization above for guidance on the correct locations for different file types.
The second is updating the various environment files with the correct default options. The following changes reflect the defaults in version 3.1.0.
@@ -627,7 +673,7 @@ config.assets.digest = true
# config.assets.precompile += %w( search.js )
</erb>
-There are no changes to +test.rb+. The defaults in the test environment are: +config.assets.compile+ is true and +config.assets.compress+, +config.assets.debug+ and +config.assets.digest+ are false.
+You should not need to change +test.rb+. The defaults in the test environment are: +config.assets.compile+ is true and +config.assets.compress+, +config.assets.debug+ and +config.assets.digest+ are false.
The following should also be added to +Gemfile+:
@@ -641,7 +687,7 @@ group :assets do
end
</plain>
-If you use the +assets+ group with Bundler, please make sure that your +config/application.rb+ has the following Bundler require statement.
+If you use the +assets+ group with Bundler, please make sure that your +config/application.rb+ has the following Bundler require statement:
<ruby>
if defined?(Bundler)
@@ -652,7 +698,7 @@ if defined?(Bundler)
end
</ruby>
-Instead of the old Rails 3.0 one
+Instead of the old Rails 3.0 version:
<ruby>
# If you have a Gemfile, require the gems listed there, including any gems
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 6829eb8ef4..a55ed38d1b 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -1120,7 +1120,7 @@ h6(#has_many-collection-find). <tt><em>collection</em>.find(...)</tt>
The <tt><em>collection</em>.find</tt> method finds objects within the collection. It uses the same syntax and options as +ActiveRecord::Base.find+.
<ruby>
-@open_orders = @customer.orders.where(:open => 1)
+@open_orders = @customer.orders.find(1)
</ruby>
h6(#has_many-collection-where). <tt><em>collection</em>.where(...)</tt>
@@ -1234,7 +1234,7 @@ If you need to evaluate conditions dynamically at runtime, use a proc:
<ruby>
class Customer < ActiveRecord::Base
has_many :latest_orders, :class_name => "Order",
- :conditions => proc { "orders.created_at > #{10.hours.ago.to_s(:db).inspect}" }
+ :conditions => proc { ["orders.created_at > ?, 10.hours.ago] }
end
</ruby>
@@ -1242,7 +1242,7 @@ h6(#has_many-counter_sql). +:counter_sql+
Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
-NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting +SELECT COUNT(*) FROM+ for the +SELECT ... FROM+ clause of your +:finder_sql+ statement.
+NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+.
h6(#has_many-dependent). +:dependent+
@@ -1545,12 +1545,9 @@ h6(#has_and_belongs_to_many-collection-find). <tt><em>collection</em>.find(...)<
The <tt><em>collection</em>.find</tt> method finds objects within the collection. It uses the same syntax and options as +ActiveRecord::Base.find+. It also adds the additional condition that the object must be in the collection.
<ruby>
-@new_assemblies = @part.assemblies.all(
- :conditions => ["created_at > ?", 2.days.ago])
+@assembly = @part.assemblies.find(1)
</ruby>
-NOTE: Beginning with Rails 3, supplying options to the +ActiveRecord::Base.find+ method is discouraged. Use <tt><em>collection</em>.where</tt> instead when you need to pass conditions.
-
h6(#has_and_belongs_to_many-collection-where). <tt><em>collection</em>.where(...)</tt>
The <tt><em>collection</em>.where</tt> method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. It also adds the additional condition that the object must be in the collection.
@@ -1669,7 +1666,7 @@ h6(#has_and_belongs_to_many-counter_sql). +:counter_sql+
Normally Rails automatically generates the proper SQL to count the association members. With the +:counter_sql+ option, you can specify a complete SQL statement to count them yourself.
-NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting +SELECT COUNT(*) FROM+ for the +SELECT ... FROM+ clause of your +:finder_sql+ statement.
+NOTE: If you specify +:finder_sql+ but not +:counter_sql+, then the counter SQL will be generated by substituting the +SELECT ... FROM+ clause of your +:finder_sql+ statement by +SELECT COUNT(*) FROM+.
h6(#has_and_belongs_to_many-delete_sql). +:delete_sql+
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 4273d0dd64..6419d32c13 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -64,7 +64,29 @@ end
If you want a more complicated expiration scheme, you can use cache sweepers to expire cached objects when things change. This is covered in the section on Sweepers.
-NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL!
+By default, page caching automatically gzips files (for example, to +products.html.gz+ if user requests +/products+) to reduce the size of data transmitted (web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, compression ratio is maximum).
+
+Nginx is able to serve compressed content directly from disk by enabling +gzip_static+:
+
+<plain>
+location / {
+ gzip_static on; # to serve pre-gzipped version
+}
+</plain>
+
+You can disable gzipping by setting +:gzip+ option to false (for example, if action returns image):
+
+<ruby>
+caches_page :image, :gzip => false
+</ruby>
+
+Or, you can set custom gzip compression level (level names are taken from +Zlib+ constants):
+
+<ruby>
+caches_page :image, :gzip => :best_speed
+</ruby>
+
+NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. A workaround for this limitation is to include the parameters in the page's path, e.g. +/productions/page/1+.
INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
@@ -72,7 +94,7 @@ h4. Action Caching
One of the issues with Page Caching is that you cannot use it for pages that require to restrict access somehow. This is where Action Caching comes in. Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.
-Clearing the cache works in the exact same way as with Page Caching.
+Clearing the cache works in a similar way to Page Caching, except you use +expire_action+ instead of +expire_page+.
Let's say you only wanted authenticated users to call actions on +ProductsController+.
@@ -293,7 +315,7 @@ Note that the cache will grow until the disk is full unless you periodically cle
h4. ActiveSupport::Cache::MemCacheStore
-This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy.
+This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcache-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy.
When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites.
@@ -332,6 +354,14 @@ caches_action :index, :expires_in => 60.seconds, :unless_exist => true
For more information about Ehcache, see "http://ehcache.org/":http://ehcache.org/ .
For more information about Ehcache for JRuby and Rails, see "http://ehcache.org/documentation/jruby.html":http://ehcache.org/documentation/jruby.html
+h4. ActiveSupport::Cache::NullStore
+
+This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with +Rails.cache+, but caching may interfere with being able to see the results of code changes. With this cache store, all +fetch+ and +read+ operations will result in a miss.
+
+<ruby>
+ActionController::Base.cache_store = :null
+</ruby>
+
h4. Custom Cache Stores
You can create your own custom cache store by simply extending +ActiveSupport::Cache::Store+ and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application.
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index 3f8643eca3..fe4a84dae9 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -36,7 +36,7 @@ WARNING: You can install the rails gem by typing +gem install rails+, if you don
<shell>
$ rails new commandsapp
create
- create README
+ create README.rdoc
create .gitignore
create Rakefile
create config.ru
@@ -45,8 +45,6 @@ $ rails new commandsapp
...
create tmp/cache
create tmp/pids
- create vendor/plugins
- create vendor/plugins/.gitkeep
</shell>
Rails will set you up with what seems like a huge amount of stuff for such a tiny command! You've got the entire Rails directory structure now with all the code you need to run our simple application right out of the box.
@@ -81,6 +79,8 @@ The server can be run on a different port using the +-p+ option. The default dev
$ rails server -e production -p 4000
</shell>
+The +-b+ option binds Rails to the specified ip, by default it is 0.0.0.0. You can run a server as a daemon by passing a +-d+ option.
+
h4. +rails generate+
The +rails generate+ command uses templates to create a whole lot of things. Running +rails generate+ by itself gives a list of available generators:
@@ -293,18 +293,6 @@ h4. +rails dbconsole+
You can also use the alias "db" to invoke the dbconsole: <tt>rails db</tt>.
-h4. +rails plugin+
-
-The +rails plugin+ command simplifies plugin management. Plugins can be installed by name or their repository URLs. You need to have Git installed if you want to install a plugin from a Git repo. The same holds for Subversion too.
-
-<shell>
-$ rails plugin install https://github.com/technoweenie/acts_as_paranoid.git
-+ ./CHANGELOG
-+ ./MIT-LICENSE
-...
-...
-</shell>
-
h4. +rails runner+
<tt>runner</tt> runs Ruby code in the context of Rails non-interactively. For instance:
@@ -379,17 +367,17 @@ h4. +about+
<shell>
$ rake about
About your application's environment
-Ruby version 1.8.7 (x86_64-linux)
+Ruby version 1.9.3 (x86_64-linux)
RubyGems version 1.3.6
Rack version 1.3
-Rails version 3.2.0.beta
+Rails version 4.0.0.beta
JavaScript Runtime Node.js (V8)
-Active Record version 3.2.0.beta
-Action Pack version 3.2.0.beta
-Active Resource version 3.2.0.beta
-Action Mailer version 3.2.0.beta
-Active Support version 3.2.0.beta
-Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, ActionDispatch::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport
+Active Record version 4.0.0.beta
+Action Pack version 4.0.0.beta
+Active Resource version 4.0.0.beta
+Action Mailer version 4.0.0.beta
+Active Support version 4.0.0.beta
+Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, ActionDispatch::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport
Application root /home/foobar/commandsapp
Environment development
Database adapter sqlite3
@@ -413,12 +401,10 @@ The +doc:+ namespace has the tools to generate documentation for your app, API d
* +rake doc:app+ generates documentation for your application in +doc/app+.
* +rake doc:guides+ generates Rails guides in +doc/guides+.
* +rake doc:rails+ generates API documentation for Rails in +doc/api+.
-* +rake doc:plugins+ generates API documentation for all the plugins installed in the application in +doc/plugins+.
-* +rake doc:clobber_plugins+ removes the generated documentation for all plugins.
h4. +notes+
-+rake notes+ will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is only done in files with extension +.builder+, +.rb+, +.rxml+, +.rhtml+ and +.erb+ for both default and custom annotations.
++rake notes+ will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is done in files with extension +.builder+, +.rb+, +.erb+, +.haml+ and +.slim+ for both default and custom annotations.
<shell>
$ rake notes
@@ -507,8 +493,8 @@ $ rails new . --git --database=postgresql
create tmp/pids
create Rakefile
add 'Rakefile'
- create README
-add 'README'
+ create README.rdoc
+add 'README.rdoc'
create app/controllers/application_controller.rb
add 'app/controllers/application_controller.rb'
create app/helpers/application_helper.rb
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index bb494fbd33..451235d41d 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -40,7 +40,9 @@ Rails will use that particular setting to configure Active Record.
h4. Rails General Configuration
-* +config.after_initialize+ takes a block which will be run _after_ Rails has finished initializing the application. That includes the initialization of the framework itself, plugins, engines, and all the application's initializers in +config/initializers+. Note that this block _will_ be run for rake tasks. Useful for configuring values set up by other initializers:
+These configuration methods are to be called on a +Rails::Railtie+ object, such as a subclass of +Rails::Engine+ or +Rails::Application+.
+
+* +config.after_initialize+ takes a block which will be run _after_ Rails has finished initializing the application. That includes the initialization of the framework itself, engines, and all the application's initializers in +config/initializers+. Note that this block _will_ be run for rake tasks. Useful for configuring values set up by other initializers:
<ruby>
config.after_initialize do
@@ -80,21 +82,25 @@ NOTE. The +config.asset_path+ configuration is ignored if the asset pipeline is
* +config.encoding+ sets up the application-wide encoding. Defaults to UTF-8.
+* +config.exceptions_app+ sets the exceptions application invoked by the ShowException middleware when an exception happens. Defaults to +ActionDispatch::PublicExceptions.new(Rails.public_path)+.
+
+* +config.file_watcher+ the class used to detect file updates in the filesystem when +config.reload_classes_only_on_change+ is true. Must conform to +ActiveSupport::FileUpdateChecker+ API.
+
* +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers.
* +config.force_ssl+ forces all requests to be under HTTPS protocol by using +Rack::SSL+ middleware.
* +config.log_level+ defines the verbosity of the Rails logger. This option defaults to +:debug+ for all modes except production, where it defaults to +:info+.
+* +config.log_tags+ accepts a list of methods that respond to +request+ object. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications.
+
* +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby +Logger+ class. Defaults to an instance of +ActiveSupport::BufferedLogger+, with auto flushing off in production mode.
* +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware":#configuring-middleware section below.
-* +config.plugins+ accepts the list of plugins to load. The default is +nil+ in which case all plugins will be loaded. If this is set to +[]+, no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. This option lets you enforce some particular loading order, useful when dependencies between plugins require it. For that use case, put first the plugins you want to be loaded in a certain order, and then the special symbol +:all+ to have the rest loaded without the need to specify them.
-
* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Enabled by +config.threadsafe!+. Defaults to +nil+, so is disabled.
-* +config.reload_plugins+ enables or disables plugin reloading. Defaults to false.
+* +config.reload_classes_only_on_change+ enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If +config.cache_classes+ is true, this option is ignored.
* +config.secret_token+ used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get +config.secret_token+ initialized to a random key in +config/initializers/secret_token.rb+.
@@ -116,6 +122,17 @@ WARNING: Threadsafe operation is incompatible with the normal workings of develo
* +config.whiny_nils+ enables or disables warnings when a certain set of methods are invoked on +nil+ and it does not respond to them. Defaults to true in development and test environments.
+* +config.console+ allows you to set class that will be used as console you run +rails console+. It's best to run it in +console+ block:
+
+<ruby>
+console do
+ # this block is called only when running console,
+ # so we can safely require pry here
+ require "pry"
+ config.console = Pry
+end
+</ruby>
+
h4. Configuring Assets
Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets within an application. This gem concatenates and compresses assets in order to make serving them much less painful.
@@ -146,6 +163,7 @@ Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets wit
* +config.assets.compile+ is a boolean that can be used to turn on live Sprockets compilation in production.
+* +config.assets.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby +Logger+ class. Defaults to the same configured at +config.logger+. Setting +config.assets.logger+ to false will turn off served assets logging.
h4. Configuring Generators
@@ -186,6 +204,7 @@ Every Rails application comes with a standard set of middleware which it uses in
* +Rack::Runtime+ sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request.
* +Rails::Rack::Logger+ notifies the logs that the request has began. After request is complete, flushes all the logs.
* +ActionDispatch::ShowExceptions+ rescues any exception returned by the application and renders nice exception pages if the request is local or if +config.consider_all_requests_local+ is set to +true+. If +config.action_dispatch.show_exceptions+ is set to +false+, exceptions will be raised regardless.
+* +ActionDispatch::RequestId+ makes a unique X-Request-Id header available to the response and enables the +ActionDispatch::Request#uuid+ method.
* +ActionDispatch::RemoteIp+ checks for IP spoofing attacks. Configurable with the +config.action_dispatch.ip_spoofing_check+ and +config.action_dispatch.trusted_proxies+ settings.
* +Rack::Sendfile+ intercepts responses whose body is being served from a file and replaces it with a server specific X-Sendfile header. Configurable with +config.action_dispatch.x_sendfile_header+.
* +ActionDispatch::Callbacks+ runs the prepare callbacks before serving the request.
@@ -261,6 +280,12 @@ h4. Configuring Active Record
* +config.active_record.whitelist_attributes+ will create an empty whitelist of attributes available for mass-assignment security for all models in your app.
+* +config.active_record.identity_map+ controls whether the identity map is enabled, and is false by default.
+
+* +config.active_record.auto_explain_threshold_in_seconds+ configures the threshold for automatic EXPLAINs (+nil+ disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode.
+
+* +config.active_record.dependent_restrict_raises+ will control the behavior when an object with a <tt>:dependent => :restrict</tt> association is deleted. Setting this to false will prevent +DeleteRestrictionError+ from being raised and instead will add an error on the model object. Defaults to false in the development mode.
+
The MySQL adapter adds one additional configuration option:
* +ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans+ controls whether Active Record will consider all +tinyint(1)+ columns in a MySQL database to be booleans and is true by default.
@@ -331,7 +356,7 @@ Proc.new { |html_tag, instance| %Q(<div class="field_with_errors">#{html_tag}</d
* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
-* +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action Mailer. Set to +nil+ to disable logging.
+* +config.action_view.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action View. Set to +nil+ to disable logging.
* +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information.
@@ -447,7 +472,7 @@ Some parts of Rails can also be configured externally by supplying environment v
h3. Using Initializer Files
-After loading the framework and any gems and plugins in your application, Rails turns to loading initializers. An initializer is any Ruby file stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks, plugins and gems are loaded, such as options to configure settings for these parts.
+After loading the framework and any gems in your application, Rails turns to loading initializers. An initializer is any Ruby file stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and gems are loaded, such as options to configure settings for these parts.
NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the initializers folder on down.
@@ -522,7 +547,7 @@ Serves as a placeholder so that +:load_environment_config+ can be defined to run
*+initialize_logger+* Initializes the logger (an +ActiveSupport::BufferedLogger+ object) for the application and makes it accessible at +Rails.logger+, provided that no initializer inserted before this point has defined +Rails.logger+.
-*+initialize_cache+* If +RAILS_CACHE+ isn't set yet, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +RAILS_CACHE+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack.
+*+initialize_cache+* If +Rails.cache+ isn't set yet, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +Rails.cache+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack.
*+set_clear_dependencies_hook+* Provides a hook for +active_record.set_dispatch_hooks+ to use, which will run before this initializer. This initializer -- which runs only if +cache_classes+ is set to +false+ -- uses +ActionDispatch::Callbacks.after+ to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request.
@@ -560,7 +585,7 @@ The error occurred while evaluating nil.each
*+action_controller.logger+* Sets +ActionController::Base.logger+ -- if it's not already set -- to +Rails.logger+.
-*+action_controller.initialize_framework_caches+* Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +RAILS_CACHE+.
+*+action_controller.initialize_framework_caches+* Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +Rails.cache+.
*+action_controller.set_configs+* Sets up Action Controller by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through.
@@ -602,7 +627,7 @@ The error occurred while evaluating nil.each
*+prepend_helpers_path+* Adds the directory +app/helpers+ from the application, railties and engines to the lookup path for helpers for the application.
-*+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded.
+*+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded.
*+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran.
diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile
index 5848172510..aac5e13978 100644
--- a/railties/guides/source/contributing_to_ruby_on_rails.textile
+++ b/railties/guides/source/contributing_to_ruby_on_rails.textile
@@ -87,21 +87,21 @@ $ bundle install --without db
This command will install all dependencies except the MySQL and PostgreSQL Ruby drivers. We will come back at these soon. With dependencies installed, you can run the test suite with:
<shell>
-$ rake test
+$ bundle exec rake test
</shell>
You can also run tests for a specific framework, like Action Pack, by going into its directory and executing the same command:
<shell>
$ cd actionpack
-$ rake test
+$ bundle exec rake test
</shell>
If you want to run tests from the specific directory use the +TEST_DIR+ environment variable. For example, this will run tests inside +railties/test/generators+ directory only:
<shell>
$ cd railties
-$ TEST_DIR=generators rake test
+$ TEST_DIR=generators bundle exec rake test
</shell>
h4. Warnings
@@ -111,7 +111,7 @@ The test suite runs with warnings enabled. Ideally Ruby on Rails should issue no
As of this writing they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag:
<shell>
-$ RUBYOPT=-W0 rake test
+$ RUBYOPT=-W0 bundle exec rake test
</shell>
h4. Testing Active Record
@@ -130,7 +130,7 @@ The gem +sqlite3-ruby+ does not belong to the "db" group indeed, if you followed
<shell>
$ cd activerecord
-$ rake test_sqlite3
+$ bundle exec rake test_sqlite3
</shell>
h5. MySQL and PostgreSQL
@@ -195,12 +195,12 @@ test_postgresql
respectively. As we mentioned before
<shell>
-$ rake test
+$ bundle exec rake test
</shell>
will now run the four of them in turn.
-You can also invoke +test_jdbcmysql+, +test_jdbcsqlite3+ or +test_jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the continuous integration server runs.
+You can also invoke +test_jdbcmysql+, +test_jdbcsqlite3+ or +test_jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/travis.rb+ to see the test suite that the continuous integration server runs.
h4. Older versions of Ruby on Rails
@@ -215,7 +215,7 @@ TIP: You may want to "put your git branch name in your shell prompt":http://qugs
h3. Helping to Resolve Existing Issues
-As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "Everyone's Issues":https://github.com/rails/rails/issues?sort=created&direction=desc&state=open&page=1 list in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually:
+As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the "Everyone's Issues":https://github.com/rails/rails/issues list in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually:
h4. Verifying Bug Reports
@@ -271,6 +271,8 @@ When working with documentation, please take into account the "API Documentation
NOTE: As explained earlier, ordinary code patches should have proper documentation coverage. docrails is only used for isolated documentation improvements.
+NOTE: To help our CI servers you can add [ci skip] tag to your documentation commit message to skip build on that commit. Please remember to use it for commits containing only documentation changes.
+
WARNING: docrails has a very strict policy: no code can be touched whatsoever, no matter how trivial or small the change. Only RDoc and guides can be edited via docrails. Also, CHANGELOGs should never be edited in docrails.
h3. Contributing to the Rails Code
@@ -307,7 +309,7 @@ Rails follows a simple set of coding style conventions.
* Two spaces, no tabs.
* No trailing whitespace. Blank lines should not have any space.
-* Indent after private/protected.
+* Outdent private/protected from method definitions. Same indentation as the class/module.
* Prefer +&amp;&amp;+/+||+ over +and+/+or+.
* Prefer class << self block over self.method for class methods.
* +MyClass.my_method(my_arg)+ not +my_method( my_arg )+ or +my_method my_arg+.
@@ -336,7 +338,7 @@ It’s pretty likely that other changes to master have happened while you were w
<shell>
$ git checkout master
-$ git pull
+$ git pull --rebase
</shell>
Now reapply your patch on top of the latest changes:
@@ -380,9 +382,9 @@ Now you need to get other people to look at your patch, just as you've looked at
h4. Iterate as Necessary
-It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a plugin.
+It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a gem.
-And then...think about your next contribution!
+And then ... think about your next contribution!
h3. Rails Contributors
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index 3552c68418..57c7786636 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -465,7 +465,7 @@ Now you should know where you are in the running trace and be able to print the
Use +step+ (abbreviated +s+) to continue running your program until the next logical stopping point and return control to ruby-debug.
-TIP: You can also use +step+ _n_+ and +step- _n_+ to move forward or backward _n_ steps respectively.
+TIP: You can also use <tt>step<plus> n</tt> and <tt>step- n</tt> to move forward or backward +n+ steps respectively.
You may also use +next+ which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move _n_ steps.
diff --git a/railties/guides/source/documents.yaml b/railties/guides/source/documents.yaml
new file mode 100644
index 0000000000..6a47959c3d
--- /dev/null
+++ b/railties/guides/source/documents.yaml
@@ -0,0 +1,157 @@
+-
+ name: Start Here
+ documents:
+ -
+ name: Getting Started with Rails
+ url: getting_started.html
+ description: Everything you need to know to install Rails and create your first application.
+-
+ name: Models
+ documents:
+ -
+ name: Rails Database Migrations
+ url: migrations.html
+ description: This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.
+ -
+ name: Active Record Validations and Callbacks
+ url: active_record_validations_callbacks.html
+ description: This guide covers how you can use Active Record validations and callbacks.
+ -
+ name: Active Record Associations
+ url: association_basics.html
+ description: This guide covers all the associations provided by Active Record.
+ -
+ name: Active Record Query Interface
+ url: active_record_querying.html
+ description: This guide covers the database query interface provided by Active Record.
+-
+ name: Views
+ documents:
+ -
+ name: Layouts and Rendering in Rails
+ url: layouts_and_rendering.html
+ description: This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials.
+ -
+ name: Action View Form Helpers
+ url: form_helpers.html
+ description: Guide to using built-in Form helpers.
+-
+ name: Controllers
+ documents:
+ -
+ name: Action Controller Overview
+ url: action_controller_overview.html
+ description: This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.
+ -
+ name: Rails Routing from the Outside In
+ url: routing.html
+ description: This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here.
+-
+ name: Digging Deeper
+ documents:
+ -
+ name: Active Support Core Extensions
+ url: active_support_core_extensions.html
+ description: This guide documents the Ruby core extensions defined in Active Support.
+ -
+ name: Rails Internationalization API
+ url: i18n.html
+ description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.
+ -
+ name: Action Mailer Basics
+ url: action_mailer_basics.html
+ work_in_progress: true
+ description: This guide describes how to use Action Mailer to send and receive emails.
+ -
+ name: Testing Rails Applications
+ url: testing.html
+ work_in_progress: true
+ description: This is a rather comprehensive guide to doing both unit and functional tests in Rails. It covers everything from 'What is a test?' to the testing APIs. Enjoy.
+ -
+ name: Securing Rails Applications
+ url: security.html
+ description: This guide describes common security problems in web applications and how to avoid them with Rails.
+ -
+ name: Debugging Rails Applications
+ url: debugging_rails_applications.html
+ description: This guide describes how to debug Rails applications. It covers the different ways of achieving this and how to understand what is happening "behind the scenes" of your code.
+ -
+ name: Performance Testing Rails Applications
+ url: performance_testing.html
+ description: This guide covers the various ways of performance testing a Ruby on Rails application.
+ -
+ name: Configuring Rails Applications
+ url: configuring.html
+ description: This guide covers the basic configuration settings for a Rails application.
+ -
+ name: Rails Command Line Tools and Rake Tasks
+ url: command_line.html
+ description: This guide covers the command line tools and rake tasks provided by Rails.
+ -
+ name: Caching with Rails
+ work_in_progress: true
+ url: caching_with_rails.html
+ description: Various caching techniques provided by Rails.
+ -
+ name: Asset Pipeline
+ url: asset_pipeline.html
+ description: This guide documents the asset pipeline.
+ -
+ name: The Rails Initialization Process
+ work_in_progress: true
+ url: initialization.html
+ description: This guide explains the internals of the Rails initialization process as of Rails 3.1
+-
+ name: Extending Rails
+ documents:
+ -
+ name: The Basics of Creating Rails Plugins
+ work_in_progress: true
+ url: plugins.html
+ description: This guide covers how to build a plugin to extend the functionality of Rails.
+ -
+ name: Rails on Rack
+ url: rails_on_rack.html
+ description: This guide covers Rails integration with Rack and interfacing with other Rack components.
+ -
+ name: Creating and Customizing Rails Generators
+ url: generators.html
+ description: This guide covers the process of adding a brand new generator to your extension or providing an alternative to an element of a built-in Rails generator (such as providing alternative test stubs for the scaffold generator).
+-
+ name: Contributing to Ruby on Rails
+ documents:
+ -
+ name: Contributing to Ruby on Rails
+ url: contributing_to_ruby_on_rails.html
+ description: Rails is not 'somebody else's framework.' This guide covers a variety of ways that you can get involved in the ongoing development of Rails.
+ -
+ name: API Documentation Guidelines
+ url: api_documentation_guidelines.html
+ description: This guide documents the Ruby on Rails API documentation guidelines.
+ -
+ name: Ruby on Rails Guides Guidelines
+ url: ruby_on_rails_guides_guidelines.html
+ description: This guide documents the Ruby on Rails guides guidelines.
+-
+ name: Release Notes
+ documents:
+ -
+ name: Ruby on Rails 3.2 Release Notes
+ url: 3_2_release_notes.html
+ description: Release notes for Rails 3.2.
+ -
+ name: Ruby on Rails 3.1 Release Notes
+ url: 3_1_release_notes.html
+ description: Release notes for Rails 3.1.
+ -
+ name: Ruby on Rails 3.0 Release Notes
+ url: 3_0_release_notes.html
+ description: Release notes for Rails 3.0.
+ -
+ name: Ruby on Rails 2.3 Release Notes
+ url: 2_3_release_notes.html
+ description: Release notes for Rails 2.3.
+ -
+ name: Ruby on Rails 2.2 Release Notes
+ url: 2_2_release_notes.html
+ description: Release notes for Rails 2.2.
diff --git a/railties/guides/source/engines.textile b/railties/guides/source/engines.textile
index da56f3d0ed..694b36bea1 100644
--- a/railties/guides/source/engines.textile
+++ b/railties/guides/source/engines.textile
@@ -506,6 +506,10 @@ Now instead of the ugly Ruby object output the author's name will be displayed.
h4. Configuring an engine
+This section covers firstly how you can make the +user_class+ setting of the Blorgh engine configurable, followed by general configuration tips for the engine.
+
+h5. Setting configuration settings in the application
+
The next step is to make the class that represents a +User+ in the application customizable for the engine. This is because, as explained before, that class may not always be +User+. To make this customizable, the engine will have a configuration setting called +user_class+ that will be used to specify what the class representing users is inside the application.
To define this configuration setting, you should use a +mattr_accessor+ inside the +Blorgh+ module for the engine, located at +lib/blorgh.rb+ inside the engine. Inside this module, put this line:
@@ -542,6 +546,14 @@ Go ahead and try to create a new post. You will see that it works exactly in the
There are now no strict dependencies on what the class is, only what the class's API must be. The engine simply requires this class to define a +find_or_create_by_name+ method which returns an object of that class to be associated with a post when it's created.
+h5. General engine configuration
+
+Within an engine, there may come a time where you wish to use things such as initializers, internationalization or other configuration options. The great news is that these things are entirely possible because a Rails engine shares much the same functionality as a Rails application. In fact, a Rails application's functionality is actually a superset of what is provided by engines!
+
+If you wish to use initializers (code that should run before the engine is loaded), the best place for them is the +config/initializers+ folder. This directory's functionality is explained in the "Initializers section":http://guides.rubyonrails.org/configuring.html#initializers of the Configuring guide.
+
+For locales, simply place the locale files in the +config/locales+ directory, just like you would in an application.
+
h3. Extending engine functionality
This section looks at overriding or adding functionality to the views, controllers and models provided by an engine.
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 821bb305f6..2de4d49cf2 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -150,7 +150,7 @@ NOTE: Always use labels for checkbox and radio buttons. They associate text with
h4. Other Helpers of Interest
-Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, URL fields and email fields:
+Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, date fields, URL fields and email fields:
<erb>
<%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %>
@@ -158,6 +158,7 @@ Other form controls worth mentioning are textareas, password fields, hidden fiel
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
+<%= date_field(:user, :born_on) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
</erb>
@@ -170,13 +171,14 @@ Output:
<input id="parent_id" name="parent_id" type="hidden" value="5" />
<input id="user_name" name="user[name]" size="30" type="search" />
<input id="user_phone" name="user[phone]" size="30" type="tel" />
+<input id="user_born_on" name="user[born_on]" type="date" />
<input id="user_homepage" size="30" name="user[homepage]" type="url" />
<input id="user_address" size="30" name="user[address]" type="email" />
</html>
Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript.
-IMPORTANT: The search, telephone, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely "no shortage of solutions for this":https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills, although a couple of popular tools at the moment are "Modernizr":http://www.modernizr.com/ and "yepnope":http://yepnopejs.com/, which provide a simple way to add functionality based on the presence of detected HTML5 features.
+IMPORTANT: The search, telephone, date, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely "no shortage of solutions for this":https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills, although a couple of popular tools at the moment are "Modernizr":http://www.modernizr.com/ and "yepnope":http://yepnopejs.com/, which provide a simple way to add functionality based on the presence of detected HTML5 features.
TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the "Security Guide":security.html#logging.
@@ -229,7 +231,7 @@ The corresponding view +app/views/articles/new.html.erb+ using +form_for+ looks
There are a few things to note here:
# +@article+ is the actual object being edited.
-# There is a single hash of options. Routing options are passed in the +:url+ hash, HTML options are passed in the +:html+ hash.
+# There is a single hash of options. Routing options are passed in the +:url+ hash, HTML options are passed in the +:html+ hash. Also you can provide a +:namespace+ option for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generated HTML id.
# The +form_for+ method yields a *form builder* object (the +f+ variable).
# Methods to create form controls are called *on* the form builder object +f+
@@ -467,7 +469,7 @@ Rails _used_ to have a +country_select+ helper for choosing countries, but this
h3. Using Date and Time Form Helpers
-The date and time helpers differ from all the other form helpers in two important respects:
+You can choose not to use the form helpers generating HTML5 date input fields and use the alternative date and time helpers. These date and time helpers differ from all the other form helpers in two important respects:
# Dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc.) and so there is no single value in your +params+ hash with your date or time.
# Other helpers use the +_tag+ suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, +select_date+, +select_time+ and +select_datetime+ are the barebones helpers, +date_select+, +time_select+ and +datetime_select+ are the equivalent model object helpers.
@@ -630,10 +632,10 @@ action for a Person model, +params[:model]+ would usually be a hash of all the a
Fundamentally HTML forms don't know about any sort of structured data, all they generate is name–value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.
-TIP: You may find you can try out examples in this section faster by using the console to directly invoke Rails' parameter parser. For example,
+TIP: You may find you can try out examples in this section faster by using the console to directly invoke Racks' parameter parser. For example,
<ruby>
-ActionController::UrlEncodedPairParser.parse_query_parameters "name=fred&phone=0123456789"
+Rack::Utils.parse_query "name=fred&phone=0123456789"
# => {"name"=>"fred", "phone"=>"0123456789"}
</ruby>
@@ -754,7 +756,7 @@ produces exactly the same output as the previous example.
h3. Forms to external resources
-If you need to post some data to an external resource it is still great to build your from using rails form helpers. But sometimes you need to set an +authenticity_token+ for this resource. You can do it by passing an +:authenticity_token => 'your_external_token'+ parameter to the +form_tag+ options:
+If you need to post some data to an external resource it is still great to build your form using rails form helpers. But sometimes you need to set an +authenticity_token+ for this resource. You can do it by passing an +:authenticity_token => 'your_external_token'+ parameter to the +form_tag+ options:
<erb>
<%= form_tag 'http://farfar.away/form', :authenticity_token => 'external_token') do %>
diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile
index 7a863ccbc7..920ff997ae 100644
--- a/railties/guides/source/generators.textile
+++ b/railties/guides/source/generators.textile
@@ -48,7 +48,7 @@ end
NOTE: +create_file+ is a method provided by +Thor::Actions+. Documentation for +create_file+ and other Thor methods can be found in "Thor's documentation":http://rdoc.info/github/wycats/thor/master/Thor/Actions.html
-Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and has one method definition. Each public method in the generator is executed when a generator is invoked. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with the Rails Application Templates API, you'll feel right at home with the new generators API.
+Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and has one method definition. When a generator is invoked, each public method in the generator is executed sequentially in the order that it is defined. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with the Rails Application Templates API, you'll feel right at home with the new generators API.
To invoke our new generator, we just need to do:
@@ -406,22 +406,6 @@ The following are methods available for both generators and templates for Rails.
NOTE: Methods provided by Thor are not covered this guide and can be found in "Thor's documentation":http://rdoc.info/github/wycats/thor/master/Thor/Actions.html
-h4. +plugin+
-
-+plugin+ will install a plugin into the current application.
-
-<ruby>
-plugin("dynamic-form", :git => "git://github.com/rails/dynamic-form.git")
-</ruby>
-
-Available options are:
-
-* +:git+ - Takes the path to the git repository where this plugin can be found.
-* +:branch+ - The name of the branch of the git repository where the plugin is found.
-* +:submodule+ - Set to +true+ for the plugin to be installed as a submodule. Defaults to +false+.
-* +:svn+ - Takes the path to the svn repository where this plugin can be found.
-* +:revision+ - The revision of the plugin in an SVN repository.
-
h4. +gem+
Specifies a gem dependency of the application.
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index bf6104b96b..bed14ef6a8 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -41,11 +41,12 @@ internet for learning Ruby, including:
* "Programming Ruby":http://www.ruby-doc.org/docs/ProgrammingRuby/
* "Why's (Poignant) Guide to Ruby":http://mislav.uniqpath.com/poignant-guide/
-Also, the example code for this guide is available in the rails github:https://github.com/rails/rails repository
-in rails/railties/guides/code/getting_started.
-
h3. What is Rails?
+TIP: This section goes into the background and philosophy of the Rails framework
+in detail. You can safely skip this section and come back to it at a later time.
+Section 3 starts you on the path to creating your first Rails application.
+
Rails is a web application development framework written in the Ruby language.
It is designed to make programming web applications easier by making assumptions
about what every developer needs to get started. It allows you to write less
@@ -215,7 +216,11 @@ Ian Robinson
h3. Creating a New Rails Project
-If you follow this guide, you'll create a Rails project called <tt>blog</tt>, a
+The best way to use this guide is to follow each step as it happens, no code or
+step needed to make this example application has been left out, so you can
+literally follow along step by step. You can get the complete code "here":https://github.com/lifo/docrails/tree/master/railties/guides/code/getting_started.
+
+By following along with this guide, you'll create a Rails project called <tt>blog</tt>, a
(very) simple weblog. Before you can start building the application, you need to
make sure that you have Rails itself installed.
@@ -233,13 +238,16 @@ Usually run this as the root user:
TIP. If you're working on Windows, you can quickly install Ruby and Rails with
"Rails Installer":http://railsinstaller.org.
-h4. Creating the Blog Application
+To verify that you have everything installed correctly, you should be able to run
+the following:
-The best way to use this guide is to follow each step as it happens, no code or
-step needed to make this example application has been left out, so you can
-literally follow along step by step. If you need to see the completed code, you
-can download it from "Getting Started
-Code":https://github.com/mikel/getting-started-code.
+<shell>
+$ rails --version
+</shell>
+
+If it says something like "Rails 3.1.3" you are ready to continue.
+
+h4. Creating the Blog Application
To begin, open a terminal, navigate to a folder where you have rights to create
files, and type:
@@ -261,41 +269,50 @@ directly in that application:
$ cd blog
</shell>
-In any case, Rails will create a folder in your working directory called
-<tt>blog</tt>. Open up that folder and explore its contents. Most of the work in
+The 'rails new blog' command we ran above created a folder in your working directory
+called <tt>blog</tt>. The <tt>blog</tt> folder has a number of auto-generated folders
+that make up the structure of a Rails application. Most of the work in
this tutorial will happen in the <tt>app/</tt> folder, but here's a basic
-rundown on the function of each folder that Rails creates in a new application
-by default:
+rundown on the function of each of the files and folders that Rails created by default:
|_.File/Folder|_.Purpose|
-|Gemfile|This file allows you to specify what gem dependencies are needed for your Rails application. See section on Bundler, below.|
-|README|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.|
-|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.|
|app/|Contains the controllers, models, views and assets for your application. You'll focus on this folder for the remainder of this guide.|
-|config/|Configure your application's runtime rules, routes, database, and more.|
+|config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in "Configuring Rails Applications":configuring.html|
|config.ru|Rack configuration for Rack based servers used to start the application.|
-|db/|Shows your current database schema, as well as the database migrations. You'll learn about migrations shortly.|
+|db/|Contains your current database schema, as well as the database migrations.|
|doc/|In-depth documentation for your application.|
-|lib/|Extended modules for your application (not covered in this guide).|
+|Gemfile<BR />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application.|
+|lib/|Extended modules for your application.|
|log/|Application log files.|
|public/|The only folder seen to the world as-is. Contains the static files and compiled assets.|
+|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.|
+|README.rdoc|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.|
|script/|Contains the rails script that starts your app and can contain other scripts you use to deploy or run your application.|
|test/|Unit tests, fixtures, and other test apparatus. These are covered in "Testing Rails Applications":testing.html|
|tmp/|Temporary files|
-|vendor/|A place for all third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.|
+|vendor/|A place for all third-party code. In a typical Rails application, this includes Ruby Gems and the Rails source code (if you optionally install it into your project).|
h4. Configuring a Database
Just about every Rails application will interact with a database. The database
to use is specified in a configuration file, +config/database.yml+. If you open
this file in a new Rails application, you'll see a default database
-configuration using SQLite3. The file contains sections for three different
+configured to use SQLite3. The file contains sections for three different
environments in which Rails can run by default:
-* The +development+ environment is used on your development computer as you interact manually with the application.
-* The +test+ environment is used to run automated tests.
+* The +development+ environment is used on your development/local computer as you interact
+manually with the application.
+* The +test+ environment is used when running automated tests.
* The +production+ environment is used when you deploy your application for the world to use.
+TIP: You don't have to update the database configurations manually. If you look at the
+options of the application generator, you will see that one of the options
+is named <tt>--database</tt>. This option allows you to choose an adapter from a
+list of the most used relational databases. You can even run the generator
+repeatedly: <tt>cd .. && rails new blog --database=mysql</tt>. When you confirm the overwriting
+ of the +config/database.yml+ file, your application will be configured for MySQL
+instead of SQLite. Detailed examples of the common database connections are below.
+
h5. Configuring an SQLite3 Database
Rails comes with built-in support for "SQLite3":http://www.sqlite.org, which is
@@ -399,14 +416,6 @@ development:
Change the username and password in the +development+ section as appropriate.
-TIP: You don't have to update the database configurations manually. If you look at the
-options of the application generator, you will see that one of the options
-is named <tt>--database</tt>. This option allows you to choose an adapter from a
-list of the most used relational databases. You can even run the generator
-repeatedly: <tt>cd .. && rails new blog --database=mysql</tt>. When you confirm the overwriting
- of the +config/database.yml+ file, your application will be configured for MySQL
-instead of SQLite.
-
h4. Creating the Database
Now that you have your database configured, it's time to have Rails create an
@@ -438,6 +447,14 @@ start a web server on your development machine. You can do this by running:
$ rails server
</shell>
+TIP: Compiling CoffeeScript to JavaScript requires a JavaScript runtime and
+the absence of a runtime will give you an +execjs+ error. Usually Mac OS X
+and Windows come with a JavaScript runtime installed. Rails adds the +therubyracer+ gem
+to Gemfile in a commented line for new apps and you can uncomment if you need it.
++therubyrhino+ is the recommended runtime for JRuby users and is added by default
+to Gemfile in apps generated under JRuby. You can investigate about all the
+supported runtimes at "ExecJS":https://github.com/sstephenson/execjs#readme.
+
This will fire up an instance of the WEBrick web server by default (Rails can
also use several other web servers). To see your application in action, open a
browser window and navigate to "http://localhost:3000":http://localhost:3000.
@@ -480,7 +497,7 @@ Open this file in your text editor and edit it to contain a single line of code:
h4. Setting the Application Home Page
Now that we have made the controller and view, we need to tell Rails when we
-want "Hello Rails" to show up. In our case, we want it to show up when we
+want "Hello Rails!" to show up. In our case, we want it to show up when we
navigate to the root URL of our site,
"http://localhost:3000":http://localhost:3000, instead of the "Welcome Aboard"
smoke test.
@@ -501,8 +518,7 @@ file_ which holds entries in a special DSL (domain-specific language) that tells
Rails how to connect incoming requests to controllers and actions. This file
contains many sample routes on commented lines, and one of them actually shows
you how to connect the root of your site to a specific controller and action.
-Find the line beginning with +root :to+, uncomment it and change it like the
-following:
+Find the line beginning with +root :to+ and uncomment it. It should look something like the following:
<ruby>
Blog::Application.routes.draw do
@@ -530,7 +546,7 @@ resource in a single operation, scaffolding is the tool for the job.
h3. Creating a Resource
-In the case of the blog application, you can start by generating a scaffolded
+In the case of the blog application, you can start by generating a scaffold for the
Post resource: this will represent a single blog posting. To do this, enter this
command in your terminal:
@@ -544,21 +560,21 @@ folders, and edit <tt>config/routes.rb</tt>. Here's a quick overview of what it
|_.File |_.Purpose|
|db/migrate/20100207214725_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)|
|app/models/post.rb |The Post model|
-|test/fixtures/posts.yml |Dummy posts for use in testing|
+|test/unit/post_test.rb |Unit testing harness for the posts model|
+|test/fixtures/posts.yml |Sample posts for use in testing|
+|config/routes.rb |Edited to include routing information for posts|
|app/controllers/posts_controller.rb |The Posts controller|
|app/views/posts/index.html.erb |A view to display an index of all posts |
|app/views/posts/edit.html.erb |A view to edit an existing post|
|app/views/posts/show.html.erb |A view to display a single post|
|app/views/posts/new.html.erb |A view to create a new post|
|app/views/posts/_form.html.erb |A partial to control the overall look and feel of the form used in edit and new views|
-|app/helpers/posts_helper.rb |Helper functions to be used from the post views|
-|app/assets/stylesheets/scaffolds.css.scss |Cascading style sheet to make the scaffolded views look better|
-|app/assets/stylesheets/posts.css.scss |Cascading style sheet for the posts controller|
-|app/assets/javascripts/posts.js.coffee |CoffeeScript for the posts controller|
-|test/unit/post_test.rb |Unit testing harness for the posts model|
|test/functional/posts_controller_test.rb |Functional testing harness for the posts controller|
+|app/helpers/posts_helper.rb |Helper functions to be used from the post views|
|test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper|
-|config/routes.rb |Edited to include routing information for posts|
+|app/assets/javascripts/posts.js.coffee |CoffeeScript for the posts controller|
+|app/assets/stylesheets/posts.css.scss |Cascading style sheet for the posts controller|
+|app/assets/stylesheets/scaffolds.css.scss |Cascading style sheet to make the scaffolded views look better|
NOTE. While scaffolding will get you up and running quickly, the code it
generates is unlikely to be a perfect fit for your application. You'll most
@@ -596,11 +612,11 @@ end
</ruby>
The above migration creates a method named +change+ which will be called when you
-run this migration. The action defined in that method is also reversible, which
+run this migration. The action defined in this method is also reversible, which
means Rails knows how to reverse the change made by this migration, in case you
-want to reverse it at later date. By default, when you run this migration it
-creates a +posts+ table with two string columns and a text column. It also
-creates two timestamp fields to track record creation and updating. More
+want to reverse it later. When you run this migration it will create a
++posts+ table with two string columns and a text column. It also creates two
+timestamp fields to allow Rails to track post creation and update times. More
information about Rails migrations can be found in the "Rails Database
Migrations":migrations.html guide.
@@ -620,7 +636,7 @@ table.
== CreatePosts: migrated (0.0020s) ===========================================
</shell>
-NOTE. Because by default you're working in the development environment, this
+NOTE. Because you're working in the development environment by default, this
command will apply to the database defined in the +development+ section of your
+config/database.yml+ file. If you would like to execute migrations in another
environment, for instance in production, you must explicitly pass it when
@@ -691,7 +707,8 @@ end
These changes will ensure that all posts have a name and a title, and that the
title is at least five characters long. Rails can validate a variety of
conditions in a model, including the presence or uniqueness of columns, their
-format, and the existence of associated objects.
+format, and the existence of associated objects. Validations are covered in detail
+in "Active Record Validations and Callbacks":active_record_validations_callbacks.html#validations-overview
h4. Using the Console
@@ -716,10 +733,8 @@ After the console loads, you can use it to work with your application's models:
updated_at: nil>
>> p.save
=> false
->> p.errors
-=> #<OrderedHash { :title=>["can't be blank",
- "is too short (minimum is 5 characters)"],
- :name=>["can't be blank"] }>
+>> p.errors.full_messages
+=> ["Name can't be blank", "Title can't be blank", "Title is too short (minimum is 5 characters)"]
</shell>
This code shows creating a new +Post+ instance, attempting to save it and
@@ -729,13 +744,14 @@ inspecting the +errors+ of the post.
When you're finished, type +exit+ and hit +return+ to exit the console.
TIP: Unlike the development web server, the console does not automatically load
-your code afresh for each line. If you make changes to your models while the
-console is open, type +reload!+ at the console prompt to load them.
+your code afresh for each line. If you make changes to your models (in your editor)
+while the console is open, type +reload!+ at the console prompt to load them.
h4. Listing All Posts
-The easiest place to start looking at functionality is with the code that lists
-all posts. Open the file +app/controllers/posts_controller.rb+ and look at the
+Let's dive into the Rails code a little deeper to see how the application is
+showing us the list of Posts. Open the file
++app/controllers/posts_controller.rb+ and look at the
+index+ action:
<ruby>
@@ -749,9 +765,8 @@ def index
end
</ruby>
-+Post.all+ calls the +Post+ model to return all of the posts currently in the
-database. The result of this call is an array of posts that we store in an
-instance variable called +@posts+.
++Post.all+ returns all of the posts currently in the database as an array
+of +Post+ records that we store in an instance variable called +@posts+.
TIP: For more information on finding records with Active Record, see "Active
Record Query Interface":active_record_querying.html.
@@ -802,8 +817,7 @@ and links. A few things to note in the view:
NOTE. In previous versions of Rails, you had to use +&lt;%=h post.name %&gt;+ so
that any HTML would be escaped before being inserted into the page. In Rails
-3.0, this is now the default. To get unescaped HTML, you now use +&lt;%= raw
-post.name %&gt;+.
+3 and above, this is now the default. To get unescaped HTML, you now use <tt>&lt;%= raw post.name %&gt;</tt>.
TIP: For more details on the rendering process, see "Layouts and Rendering in
Rails":layouts_and_rendering.html.
@@ -816,9 +830,10 @@ Rails renders a view to the browser, it does so by putting the view's HTML into
a layout's HTML. In previous versions of Rails, the +rails generate scaffold+
command would automatically create a controller specific layout, like
+app/views/layouts/posts.html.erb+, for the posts controller. However this has
-been changed in Rails 3.0. An application specific +layout+ is used for all the
+been changed in Rails 3. An application specific +layout+ is used for all the
controllers and can be found in +app/views/layouts/application.html.erb+. Open
-this layout in your editor and modify the +body+ tag:
+this layout in your editor and modify the +body+ tag to include the style directive
+below:
<erb>
<!DOCTYPE html>
@@ -829,7 +844,7 @@ this layout in your editor and modify the +body+ tag:
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
-<body style="background: #EEEEEE;">
+<body style="background-color: #EEEEEE;">
<%= yield %>
@@ -838,7 +853,7 @@ this layout in your editor and modify the +body+ tag:
</erb>
Now when you refresh the +/posts+ page, you'll see a gray background to the
-page. This same gray background will be used throughout all the views for posts.
+page. This same gray background will be used throughout all the views.
h4. Creating New Posts
@@ -996,10 +1011,10 @@ end
The +show+ action uses +Post.find+ to search for a single record in the database
by its id value. After finding the record, Rails displays it by using
-+show.html.erb+:
++app/views/posts/show.html.erb+:
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1060,7 +1075,7 @@ def update
if @post.update_attributes(params[:post])
format.html { redirect_to(@post,
:notice => 'Post was successfully updated.') }
- format.json { render :json => {}, :status => :ok }
+ format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => @post.errors,
@@ -1089,7 +1104,7 @@ def destroy
respond_to do |format|
format.html { redirect_to posts_url }
- format.json { head :ok }
+ format.json { head :no_content }
end
end
</ruby>
@@ -1101,7 +1116,7 @@ the controller.
h3. Adding a Second Model
-Now that you've seen how a model built with scaffolding looks like, it's time to
+Now that you've seen what a model built with scaffolding looks like, it's time to
add a second model to the application. The second model will handle comments on
blog posts.
@@ -1120,9 +1135,11 @@ $ rails generate model Comment commenter:string body:text post:references
This command will generate four files:
-* +app/models/comment.rb+ - The model.
-* +db/migrate/20100207235629_create_comments.rb+ - The migration.
-* +test/unit/comment_test.rb+ and +test/fixtures/comments.yml+ - The test harness.
+|_.File |_.Purpose|
+|db/migrate/20100207235629_create_comments.rb | Migration to create the comments table in your database (your name will include a different timestamp) |
+| app/models/comment.rb | The Comment model |
+| test/unit/comment_test.rb | Unit testing harness for the comments model |
+| test/fixtures/comments.yml | Sample comments for use in testing |
First, take a look at +comment.rb+:
@@ -1169,8 +1186,10 @@ run against the current database, so in this case you will just see:
<shell>
== CreateComments: migrating =================================================
-- create_table(:comments)
- -> 0.0017s
-== CreateComments: migrated (0.0018s) ========================================
+ -> 0.0008s
+-- add_index(:comments, :post_id)
+ -> 0.0003s
+== CreateComments: migrated (0.0012s) ========================================
</shell>
h4. Associating Models
@@ -1243,13 +1262,14 @@ $ rails generate controller Comments
This creates six files and one empty directory:
-* +app/controllers/comments_controller.rb+ - The controller.
-* +app/helpers/comments_helper.rb+ - A view helper file.
-* +test/functional/comments_controller_test.rb+ - The functional tests for the controller.
-* +test/unit/helpers/comments_helper_test.rb+ - The unit tests for the helper.
-* +app/views/comments/+ - Views of the controller are stored here.
-* +app/assets/stylesheets/comment.css.scss+ - Cascading style sheet for the controller.
-* +app/assets/javascripts/comment.js.coffee+ - CoffeeScript for the controller.
+|_.File/Directory |_.Purpose |
+| app/controllers/comments_controller.rb | The Comments controller |
+| app/views/comments/ | Views of the controller are stored here |
+| test/functional/comments_controller_test.rb | The functional tests for the controller |
+| app/helpers/comments_helper.rb | A view helper file |
+| test/unit/helpers/comments_helper_test.rb | The unit tests for the helper |
+| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller |
+| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller |
Like with any blog, our readers will create their comments directly after
reading the post, and once they have added their comment, will be sent back to
@@ -1261,7 +1281,7 @@ So first, we'll wire up the Post show template
(+/app/views/posts/show.html.erb+) to let us make a new comment:
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1327,7 +1347,7 @@ template. This is where we want the comment to show, so let's add that to the
+app/views/posts/show.html.erb+.
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1409,7 +1429,7 @@ Then you can change +app/views/posts/show.html.erb+ to look like the
following:
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1480,7 +1500,7 @@ create a file +app/views/comments/_form.html.erb+ containing:
Then you make the +app/views/posts/show.html.erb+ look like the following:
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1648,7 +1668,7 @@ right in the form where you create the post. First, create a new model to hold
the tags:
<shell>
-$ rails generate model tag name:string post:references
+$ rails generate model Tag name:string post:references
</shell>
Again, run the migration to create the database table:
@@ -1675,10 +1695,10 @@ class Post < ActiveRecord::Base
end
</ruby>
-The +:allow_destroy+ option on the nested attribute declaration tells Rails to
-display a "remove" checkbox on the view that you'll build shortly. The
-+:reject_if+ option prevents saving new tags that do not have any attributes
-filled in.
+The +:allow_destroy+ option tells Rails to enable destroying tags through the
+nested attributes (you'll handle that by displaying a "remove" checkbox on the
+view that you'll build shortly). The +:reject_if+ option prevents saving new
+tags that do not have any attributes filled in.
We will modify +views/posts/_form.html.erb+ to render a partial to make a tag:
@@ -1751,7 +1771,7 @@ Finally, we will edit the <tt>app/views/posts/show.html.erb</tt> template to
show our tags.
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1811,7 +1831,7 @@ Now you can edit the view in <tt>app/views/posts/show.html.erb</tt> to look like
this:
<erb>
-<p class="notice"><%= notice %></p>
+<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
@@ -1855,7 +1875,6 @@ free to consult these support resources:
* The "Ruby on Rails Tutorial":http://railstutorial.org/book
* The "Ruby on Rails mailing list":http://groups.google.com/group/rubyonrails-talk
* The "#rubyonrails":irc://irc.freenode.net/#rubyonrails channel on irc.freenode.net
-* The "Rails Wiki":http://wiki.rubyonrails.org/
Rails also comes with built-in help that you can generate using the rake command-line utility:
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index 2d4cc13571..25201888e7 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -231,7 +231,7 @@ end
Now, when you call the +books_path+ method you should get +"/en/books"+ (for the default locale). An URL like +http://localhost:3001/nl/books+ should load the Netherlands locale, then, and following calls to +books_path+ should return +"/nl/books"+ (because the locale changed).
-If you don't want to force the use of a locale in your routes you can use an optional path scope (donated by the use brackets) like so:
+If you don't want to force the use of a locale in your routes you can use an optional path scope (denoted by the parentheses) like so:
<ruby>
# config/routes.rb
@@ -819,13 +819,13 @@ h5. Action View Helper Methods
* The +number_to_currency+, +number_with_precision+, +number_to_percentage+, +number_with_delimiter+, and +number_to_human_size+ helpers use the number format settings located in the "number":https://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L2 scope.
-h5. Active Record Methods
+h5. Active Model Methods
* +model_name.human+ and +human_attribute_name+ use translations for model names and attribute names if available in the "activerecord.models":https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#L29 scope. They also support translations for inherited class names (e.g. for use with STI) as explained above in "Error message scopes".
-* +ActiveRecord::Errors#generate_message+ (which is used by Active Record validations but may also be used manually) uses +model_name.human+ and +human_attribute_name+ (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes".
+* +ActiveModel::Errors#generate_message+ (which is used by Active Model validations but may also be used manually) uses +model_name.human+ and +human_attribute_name+ (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes".
-*+ ActiveRecord::Errors#full_messages+ prepends the attribute name to the error message using a separator that will be looked up from "activerecord.errors.format.separator":https://github.com/rails/rails/blob/master/actionpack/lib/action_view/locale/en.yml#L91 (and which defaults to +'&nbsp;'+).
+* +ActiveModel::Errors#full_messages+ prepends the attribute name to the error message using a separator that will be looked up from "errors.format":https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml#L4 (and which defaults to +"%{attribute} %{message}"+).
h5. Active Support Methods
diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb
index c9a8c4fa5c..5439459b42 100644
--- a/railties/guides/source/index.html.erb
+++ b/railties/guides/source/index.html.erb
@@ -3,189 +3,28 @@ Ruby on Rails Guides
<% end %>
<% content_for :header_section do %>
-<h2>Ruby on Rails Guides (<%= ENV['RAILS_VERSION'] || 'edge' %>)</h2>
-
-<% if @edge %>
-<p>
- These are <b>Edge Guides</b>, based on the current
- <a href="https://github.com/rails/rails/tree/master">master branch</a>.
-</p>
-<p>
- If you are looking for the ones for the stable version please check
- <a href="http://guides.rubyonrails.org">http://guides.rubyonrails.org</a> instead.
-</p>
-<% else %>
-<p>
- These are the new guides for Rails 3. The guides for Rails 2.3 are still available
- at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>.
-</p>
-<% end %>
-<p>
- These guides are designed to make you immediately productive with Rails,
- and to help you understand how all of the pieces fit together.
-</p>
-
+<%= render 'welcome' %>
<% end %>
<% content_for :index_section do %>
<div id="subCol">
<dl>
+ <dd class="kindle">Rails Guides are also available for the <%= link_to 'Kindle', 'https://kindle.amazon.com' %>
+and <%= link_to 'Free Kindle Reading Apps', 'http://www.amazon.com/gp/kindle/kcp' %> for the iPad,
+iPhone, Mac, Android, etc. Download them from <%= link_to 'here', @mobi %>.
+ </dd>
<dd class="work-in-progress">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections to the author.</dd>
</dl>
</div>
<% end %>
-<h3>Start Here</h3>
-
-<dl>
-<%= guide('Getting Started with Rails', 'getting_started.html') do %>
- <p>Everything you need to know to install Rails and create your first application.</p>
-<% end %>
-</dl>
-
-<h3>Models</h3>
-
-<dl>
-<%= guide("Rails Database Migrations", 'migrations.html') do %>
- <p>This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.</p>
-<% end %>
-
-<%= guide("Active Record Validations and Callbacks", 'active_record_validations_callbacks.html') do %>
- <p>This guide covers how you can use Active Record validations and callbacks.</p>
-<% end %>
-
-<%= guide("Active Record Associations", 'association_basics.html') do %>
- <p>This guide covers all the associations provided by Active Record.</p>
-<% end %>
-
-<%= guide("Active Record Query Interface", 'active_record_querying.html') do %>
- <p>This guide covers the database query interface provided by Active Record.</p>
-<% end %>
-</dl>
-
-<h3>Views</h3>
-
-<dl>
-<%= guide("Layouts and Rendering in Rails", 'layouts_and_rendering.html') do %>
- <p>This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials.</p>
-<% end %>
-
-<%= guide("Action View Form Helpers", 'form_helpers.html', :work_in_progress => true) do %>
- <p>Guide to using built-in Form helpers.</p>
-<% end %>
-</dl>
-
-<h3>Controllers</h3>
-
-<dl>
-<%= guide("Action Controller Overview", 'action_controller_overview.html') do %>
- <p>This guide covers how controllers work and how they fit into the request cycle in your application. It includes sessions, filters, and cookies, data streaming, and dealing with exceptions raised by a request, among other topics.</p>
-<% end %>
-
-<%= guide("Rails Routing from the Outside In", 'routing.html') do %>
- <p>This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here.</p>
-<% end %>
-</dl>
-
-<h3>Digging Deeper</h3>
-
-<dl>
-
-<%= guide("Active Support Core Extensions", 'active_support_core_extensions.html') do %>
- <p>This guide documents the Ruby core extensions defined in Active Support.</p>
-<% end %>
-
-<%= guide("Rails Internationalization API", 'i18n.html') do %>
- <p>This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.</p>
-<% end %>
-
-<%= guide("Action Mailer Basics", 'action_mailer_basics.html', :work_in_progress => true) do %>
- <p>This guide describes how to use Action Mailer to send and receive emails.</p>
-<% end %>
-
-<%= guide("Testing Rails Applications", 'testing.html', :work_in_progress => true) do %>
- <p>This is a rather comprehensive guide to doing both unit and functional tests in Rails. It covers everything from &quot;What is a test?&quot; to the testing APIs. Enjoy.</p>
-<% end %>
-
-<%= guide("Securing Rails Applications", 'security.html') do %>
- <p>This guide describes common security problems in web applications and how to avoid them with Rails.</p>
-<% end %>
-
-<%= guide("Debugging Rails Applications", 'debugging_rails_applications.html') do %>
- <p>This guide describes how to debug Rails applications. It covers the different ways of achieving this and how to understand what is happening "behind the scenes" of your code.</p>
-<% end %>
-
-<%= guide("Performance Testing Rails Applications", 'performance_testing.html') do %>
- <p>This guide covers the various ways of performance testing a Ruby on Rails application.</p>
-<% end %>
-
-<%= guide("Configuring Rails Applications", 'configuring.html') do %>
- <p>This guide covers the basic configuration settings for a Rails application.</p>
-<% end %>
-
-<%= guide("Rails Command Line Tools and Rake tasks", 'command_line.html') do %>
- <p>This guide covers the command line tools and rake tasks provided by Rails.</p>
-<% end %>
-
-<%= guide("Caching with Rails", 'caching_with_rails.html', :work_in_progress => true) do %>
- <p>Various caching techniques provided by Rails.</p>
-<% end %>
-
-<%= guide('Asset Pipeline', 'asset_pipeline.html') do %>
- <p>This guide documents the asset pipeline.</p>
-<% end %>
-</dl>
-
-<h3>Extending Rails</h3>
-
-<dl>
- <%= guide("The Basics of Creating Rails Plugins", 'plugins.html', :work_in_progress => true) do %>
- <p>This guide covers how to build a plugin to extend the functionality of Rails.</p>
- <% end %>
-
- <%= guide("Rails on Rack", 'rails_on_rack.html') do %>
- <p>This guide covers Rails integration with Rack and interfacing with other Rack components.</p>
- <% end %>
-
- <%= guide("Creating and Customizing Rails Generators", 'generators.html') do %>
- <p>This guide covers the process of adding a brand new generator to your extension
- or providing an alternative to an element of a built-in Rails generator (such as
- providing alternative test stubs for the scaffold generator).</p>
- <% end %>
-</dl>
-
-<h3>Contributing to Ruby on Rails</h3>
-
-<dl>
- <%= guide("Contributing to Ruby on Rails", 'contributing_to_ruby_on_rails.html') do %>
- <p>Rails is not &quot;somebody else's framework.&quot; This guide covers a variety of ways that you can get involved in the ongoing development of Rails.</p>
- <% end %>
-
- <%= guide('API Documentation Guidelines', 'api_documentation_guidelines.html') do %>
- <p>This guide documents the Ruby on Rails API documentation guidelines.</p>
- <% end %>
-
- <%= guide('Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html') do %>
- <p>This guide documents the Ruby on Rails guides guidelines.</p>
- <% end %>
-</dl>
-
-<h3>Release Notes</h3>
-
-<dl>
-<%= guide("Ruby on Rails 3.1 Release Notes", '3_1_release_notes.html') do %>
- <p>Release notes for Rails 3.1.</p>
-<% end %>
-
-<%= guide("Ruby on Rails 3.0 Release Notes", '3_0_release_notes.html') do %>
- <p>Release notes for Rails 3.0.</p>
-<% end %>
-
-<%= guide("Ruby on Rails 2.3 Release Notes", '2_3_release_notes.html') do %>
- <p>Release notes for Rails 2.3.</p>
-<% end %>
-
-<%= guide("Ruby on Rails 2.2 Release Notes", '2_2_release_notes.html') do %>
- <p>Release notes for Rails 2.2.</p>
+<% documents_by_section.each do |section| %>
+ <h3><%= section['name'] %></h3>
+ <dl>
+ <% section['documents'].each do |document| %>
+ <%= guide(document['name'], document['url'], :work_in_progress => document['work_in_progress']) do %>
+ <p><%= document['description'] %></p>
+ <% end %>
+ <% end %>
+ </dl>
<% end %>
-</dl>
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index f88405a2fd..5ae9cf0f2b 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -17,7 +17,7 @@ As of Rails 3, +script/server+ has become +rails server+. This was done to centr
h4. +bin/rails+
-The actual +rails+ command is kept in _bin/rails_ at the and goes like this:
+The actual +rails+ command is kept in _bin/rails_:
<ruby>
#!/usr/bin/env ruby
@@ -31,7 +31,7 @@ rescue LoadError
end
</ruby>
-This file will attempt to load +rails/cli+ and if it cannot find it then add the +railties/lib+ path to the load path (+$:+) and will then try to require it again.
+This file will attempt to load +rails/cli+. If it cannot find it then +railties/lib+ is added to the load path (+$:+) before retrying.
h4. +railties/lib/rails/cli.rb+
@@ -56,7 +56,7 @@ else
end
</ruby>
-The +rbconfig+ file here is out of Ruby's standard library and provides us with the +RbConfig+ class which contains useful information dependent on how Ruby was compiled. We'll see this in use in +railties/lib/rails/script_rails_loader+.
+The +rbconfig+ file from the Ruby standard library provides us with the +RbConfig+ class which contains detailed information about the Ruby environment, including how Ruby was compiled. We can see this in use in +railties/lib/rails/script_rails_loader+.
<ruby>
require 'pathname'
@@ -71,7 +71,7 @@ module Rails
end
</ruby>
-The +rails/script_rails_loader+ file uses +RbConfig::Config+ to gather up the +bin_dir+ and +ruby_install_name+ values for the configuration which will result in a path such as +/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby+, which is the default path on Mac OS X. If you're running Windows the path may be something such as +C:/Ruby192/bin/ruby+. Anyway, the path on your system may be different, but the point of this is that it will point at the known ruby executable location for your install. The +RbConfig::CONFIG["EXEEXT"]+ will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in +exec_script_rails!+. As for the +SCRIPT_RAILS+ constant, we'll see that when we get to the +in_rails_application?+ method.
+The +rails/script_rails_loader+ file uses +RbConfig::Config+ to obtain the +bin_dir+ and +ruby_install_name+ values for the configuration which together form the path to the Ruby interpreter. The +RbConfig::CONFIG["EXEEXT"]+ will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in +exec_script_rails!+. As for the +SCRIPT_RAILS+ constant, we'll see that when we get to the +in_rails_application?+ method.
Back in +rails/cli+, the next line is this:
@@ -79,7 +79,7 @@ Back in +rails/cli+, the next line is this:
Rails::ScriptRailsLoader.exec_script_rails!
</ruby>
-This method is defined in +rails/script_rails_loader+ like this:
+This method is defined in +rails/script_rails_loader+:
<ruby>
def self.exec_script_rails!
@@ -96,7 +96,7 @@ rescue SystemCallError
end
</ruby>
-This method will first check if the current working directory (+cwd+) is a Rails application or is a subdirectory of one. The way to determine this is defined in the +in_rails_application?+ method like this:
+This method will first check if the current working directory (+cwd+) is a Rails application or a subdirectory of one. This is determined by the +in_rails_application?+ method:
<ruby>
def self.in_rails_application?
@@ -104,7 +104,7 @@ def self.in_rails_application?
end
</ruby>
-The +SCRIPT_RAILS+ constant defined earlier is used here, with +File.exists?+ checking for its presence in the current directory. If this method returns +false+, then +in_rails_application_subdirectory?+ will be used:
+The +SCRIPT_RAILS+ constant defined earlier is used here, with +File.exists?+ checking for its presence in the current directory. If this method returns +false+ then +in_rails_application_subdirectory?+ will be used:
<ruby>
def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
@@ -112,17 +112,17 @@ def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
end
</ruby>
-This climbs the directory tree until it reaches a path which contains a +script/rails+ file. If a directory is reached which contains this file then this line will run:
+This climbs the directory tree until it reaches a path which contains a +script/rails+ file. If a directory containing this file is reached then this line will run:
<ruby>
exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
</ruby>
-This is effectively the same as doing +ruby script/rails [arguments]+. Where +[arguments]+ at this point in time is simply "server".
+This is effectively the same as running +ruby script/rails [arguments]+, where +[arguments]+ at this point in time is simply "server".
h4. +script/rails+
-This file looks like this:
+This file is as follows:
<ruby>
APP_PATH = File.expand_path('../../config/application', __FILE__)
@@ -130,7 +130,7 @@ require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
</ruby>
-The +APP_PATH+ constant here will be used later in +rails/commands+. The +config/boot+ file that +script/rails+ references is the +config/boot.rb+ file in our application which is responsible for loading Bundler and setting it up.
+The +APP_PATH+ constant will be used later in +rails/commands+. The +config/boot+ file referenced here is the +config/boot.rb+ file in our application which is responsible for loading Bundler and setting it up.
h4. +config/boot.rb+
@@ -525,19 +525,19 @@ silence_warnings do
end
</ruby>
-These methods can be used to silence STDERR responses and the +silence_stream+ allows you to also silence other streams. Additionally, this mixin allows you to suppress exceptions and capture streams. For more information see the "Silencing Warnings, Streams, and Exceptions":http://guides.rubyonrails.org/active_support_core_extensions.html#silencing-warnings-streams-and-exceptions section from the Active Support Core Extensions Guide.
+These methods can be used to silence STDERR responses and the +silence_stream+ allows you to also silence other streams. Additionally, this mixin allows you to suppress exceptions and capture streams. For more information see the "Silencing Warnings, Streams, and Exceptions":active_support_core_extensions.html#silencing-warnings-streams-and-exceptions section from the Active Support Core Extensions Guide.
h4. +active_support/core_ext/logger.rb+
The next file that is required is another Active Support core extension, this time to the +Logger+ class. This begins by defining the +around_[level]+ helpers for the +Logger+ class as well as other methods such as a +datetime_format+ getter and setter for the +formatter+ object tied to a +Logger+ object.
-For more information see the "Extensions to Logger":http://guides.rubyonrails.org/active_support_core_extensions.html#extensions-to-logger section from the Active Support Core Extensions Guide.
+For more information see the "Extensions to Logger":active_support_core_extensions.html#extensions-to-logger section from the Active Support Core Extensions Guide.
h4. +railties/lib/rails/application.rb+
The next file required by +railties/lib/rails.rb+ is +application.rb+. This file defines the +Rails::Application+ constant which the application's class defined in +config/application.rb+ in a standard Rails application depends on. Before the +Rails::Application+ class is defined however, there's some other files that get required first.
-The first of these is +active_support/core_ext/hash/reverse_merge+ which can be "read about in the Active Support Core Extensions guide":http://guides.rubyonrails.org/active_support_core_extensions.html#merging under the "Merging" section.
+The first of these is +active_support/core_ext/hash/reverse_merge+ which can be "read about in the Active Support Core Extensions guide":active_support_core_extensions.html#merging under the "Merging" section.
h4. +active_support/file_update_checker.rb+
@@ -575,7 +575,7 @@ Now that +rails/initializable.rb+ has finished being required from +rails/railti
h4. +railties/lib/rails/configuration.rb+
-This file defines the +Rails::Configuration+ module, containing the +MiddlewareStackProxy+ class as well as the +Generators+ class. The +MiddlewareStackProxy+ class is used for managing the middleware stack for an application, which we'll see later on. The +Generators+ class provides the functionality used for configuring what generators an application uses through the "+config.generators+ option":http://guides.rubyonrails.org/configuring.html#configuring-generators.
+This file defines the +Rails::Configuration+ module, containing the +MiddlewareStackProxy+ class as well as the +Generators+ class. The +MiddlewareStackProxy+ class is used for managing the middleware stack for an application, which we'll see later on. The +Generators+ class provides the functionality used for configuring what generators an application uses through the "+config.generators+ option":configuring.html#configuring-generators.
The first file required in this file is +activesupport/deprecation+.
@@ -598,11 +598,11 @@ This file defines the +ActiveSupport::Notifications+ module. Notifications provi
The "API documentation":http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html for +ActiveSupport::Notifications+ explains the usage of this module, including the methods that it defines.
-The file required in +active_support/notifications.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":http://guides.rubyonrails.org/active_support_core_extensions.html#method-delegation.
+The file required in +active_support/notifications.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":active_support_core_extensions.html#method-delegation.
h4. +activesupport/core_ext/array/wrap+
-As this file comprises of a core extension, it is covered exclusively in "the Active Support Core Extensions guide":http://guides.rubyonrails.org/active_support_core_extensions.html#wrapping
+As this file comprises of a core extension, it is covered exclusively in "the Active Support Core Extensions guide":active_support_core_extensions.html#wrapping
h4. +activesupport/lib/active_support/deprecation/reporting.rb+
@@ -624,7 +624,7 @@ h4. +active_support/ordered_options+
This file is the next file required from +rails/configuration.rb+ is the file that defines +ActiveSupport::OrderedOptions+ which is used for configuration options such as +config.active_support+ and the like.
-The next file required is +active_support/core_ext/hash/deep_dup+ which is covered in "Active Support Core Extensions guide":http://guides.rubyonrails.org/active_support_core_extensions.html#deep_dup
+The next file required is +active_support/core_ext/hash/deep_dup+ which is covered in "Active Support Core Extensions guide":active_support_core_extensions.html#deep_dup
The file that is required next from is +rails/paths+
@@ -682,7 +682,7 @@ When the module from this file (+Rails::Initializable+) is included, it extends
h4. +railties/lib/rails/engine.rb+
-The next file required in +rails/engine.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":http://guides.rubyonrails.org/active_support_core_extensions.html#method-delegation.
+The next file required in +rails/engine.rb+ is +active_support/core_ext/module/delegation+ which is documented in the "Active Support Core Extensions Guide":active_support_core_extensions.html#method-delegation.
The next two files after this are Ruby standard library files: +pathname+ and +rbconfig+. The file after these is +rails/engine/railties+.
@@ -698,7 +698,7 @@ Once this file has finished loading we jump back to +railties/lib/rails/plugin.r
h4. Back to +railties/lib/rails/plugin.rb+
-The next file required in this is a core extension from Active Support called +array/conversions+ which is covered in "this section":http://guides.rubyonrails.org/active_support_core_extensions.html#array-conversions of the Active Support Core Extensions Guide.
+The next file required in this is a core extension from Active Support called +array/conversions+ which is covered in "this section":active_support_core_extensions.html#array-conversions of the Active Support Core Extensions Guide.
Once that file has finished loading, the +Rails::Plugin+ class is defined.
@@ -817,7 +817,7 @@ def initializer(name, opts = {}, &blk)
end
</ruby>
-An initializer can be configured to run before or after another initializer, which we'll see a couple of times throughout this initialization process. Anything that inherits from +Rails::Railtie+ may also make use of the +initializer+ method, something which is covered in the "Configuration guide":[http://ryanbigg.com/guides/configuring.html#rails-railtie-initializer].
+An initializer can be configured to run before or after another initializer, which we'll see a couple of times throughout this initialization process. Anything that inherits from +Rails::Railtie+ may also make use of the +initializer+ method, something which is covered in the "Configuration guide":configuring.html#rails-railtie-initializer.
The +Initializer+ class here is defined within the +Rails::Initializable+ module and its +initialize+ method is defined to just set up a couple of variables:
diff --git a/railties/guides/source/kindle/KINDLE.md b/railties/guides/source/kindle/KINDLE.md
new file mode 100644
index 0000000000..a7d9a4e4cf
--- /dev/null
+++ b/railties/guides/source/kindle/KINDLE.md
@@ -0,0 +1,26 @@
+# Rails Guides on the Kindle
+
+
+## Synopsis
+
+ 1. Obtain `kindlegen` from the link below and put the binary in your path
+ 2. Run `KINDLE=1 rake generate_guides` to generate the guides and compile the `.mobi` file
+ 3. Copy `output/kindle/rails_guides.mobi` to your Kindle
+
+## Resources
+
+ * [StackOverflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format)
+ * Example Periodical [.ncx](https://gist.github.com/808c971ed087b839d462) and [.opf](https://gist.github.com/d6349aa8488eca2ee6d0)
+ * [Kindle Publishing guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf)
+ * [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621)
+
+## TODO
+
+### Post release
+
+ * Integrate generated Kindle document in to published HTML guides
+ * Tweak heading styles (most docs use h3/h4/h5, which end up being smaller than the text under it)
+ * Tweak table styles (smaller text? Many of the tables are unusable on a Kindle in portrait mode)
+ * Have the HTML/XML TOC 'drill down' into the TOCs of the individual guides
+ * `.epub` generation.
+
diff --git a/railties/guides/source/kindle/copyright.html.erb b/railties/guides/source/kindle/copyright.html.erb
new file mode 100644
index 0000000000..bd51d87383
--- /dev/null
+++ b/railties/guides/source/kindle/copyright.html.erb
@@ -0,0 +1 @@
+<%= render 'license' %> \ No newline at end of file
diff --git a/railties/guides/source/kindle/layout.html.erb b/railties/guides/source/kindle/layout.html.erb
new file mode 100644
index 0000000000..f0a286210b
--- /dev/null
+++ b/railties/guides/source/kindle/layout.html.erb
@@ -0,0 +1,27 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
+
+<link rel="stylesheet" type="text/css" href="stylesheets/kindle.css" />
+
+</head>
+<body class="guide">
+
+ <% if content_for? :header_section %>
+ <%= yield :header_section %>
+ <div class="pagebreak">
+ <% end %>
+
+ <% if content_for? :index_section %>
+ <%= yield :index_section %>
+ <div class="pagebreak">
+ <% end %>
+
+ <%= yield.html_safe %>
+</body>
+</html>
diff --git a/railties/guides/source/kindle/rails_guides.opf.erb b/railties/guides/source/kindle/rails_guides.opf.erb
new file mode 100644
index 0000000000..4e07664fd0
--- /dev/null
+++ b/railties/guides/source/kindle/rails_guides.opf.erb
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="RailsGuides">
+<metadata>
+ <meta name="cover" content="cover" />
+ <dc-metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
+
+ <dc:title>Ruby on Rails Guides (<%= @version %>)</dc:title>
+
+ <dc:language>en-us</dc:language>
+ <dc:creator>Ruby on Rails</dc:creator>
+ <dc:publisher>Ruby on Rails</dc:publisher>
+ <dc:subject>Reference</dc:subject>
+ <dc:date><%= Time.now.strftime('%Y-%m-%d') %></dc:date>
+
+ <dc:description>These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.</dc:description>
+ </dc-metadata>
+ <x-metadata>
+ <output content-type="application/x-mobipocket-subscription-magazine" encoding="utf-8"/>
+ </x-metadata>
+</metadata>
+
+<manifest>
+ <!-- HTML content files [mandatory] -->
+ <% documents_flat.each do |document| %>
+ <item id="<%= document['url'] %>" media-type="text/html" href="<%= document['url'] %>" />
+ <% end %>
+
+ <% %w{toc.html credits.html welcome.html copyright.html}.each do |url| %>
+ <item id="<%= url %>" media-type="text/html" href="<%= url %>" />
+ <% end %>
+
+ <item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx" />
+
+ <item id="cover" media-type="image/jpeg" href="images/rails_guides_kindle_cover.jpg"/>
+</manifest>
+
+<spine toc="toc">
+ <itemref idref="toc.html" />
+ <itemref idref="welcome.html" />
+ <itemref idref="credits.html" />
+ <itemref idref="copyright.html" />
+ <% documents_flat.each do |document| %>
+ <itemref idref="<%= document['url'] %>" />
+ <% end %>
+</spine>
+
+<guide>
+ <reference type="toc" title="Table of Contents" href="toc.html"></reference>
+</guide>
+
+</package>
diff --git a/railties/guides/source/kindle/toc.html.erb b/railties/guides/source/kindle/toc.html.erb
new file mode 100644
index 0000000000..e013797dee
--- /dev/null
+++ b/railties/guides/source/kindle/toc.html.erb
@@ -0,0 +1,24 @@
+<% content_for :page_title do %>
+Ruby on Rails Guides
+<% end %>
+
+<h1>Table of Contents</h1>
+<div id="toc">
+ <ul><li><a href="welcome.html">Welcome</a></li></ul>
+<% documents_by_section.each_with_index do |section, i| %>
+ <h3><%= "#{i + 1}." %> <%= section['name'] %></h3>
+ <ul>
+ <% section['documents'].each do |document| %>
+ <li>
+ <a href="<%= document['url'] %>"><%= document['name'] %></a>
+ <% if document['work_in_progress']%>(WIP)<% end %>
+ </li>
+ <% end %>
+ </ul>
+<% end %>
+<hr />
+<ul>
+ <li><a href="credits.html">Credits</a></li>
+ <li><a href="copyright.html">Copyright &amp; License</a></li>
+<ul>
+</div>
diff --git a/railties/guides/source/kindle/toc.ncx.erb b/railties/guides/source/kindle/toc.ncx.erb
new file mode 100644
index 0000000000..2c6d8e3bdf
--- /dev/null
+++ b/railties/guides/source/kindle/toc.ncx.erb
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN"
+ "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
+
+<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="en-US">
+<head>
+ <meta name="dtb:uid" content="RailsGuides"/>
+ <meta name="dtb:depth" content="2"/>
+ <meta name="dtb:totalPageCount" content="0"/>
+ <meta name="dtb:maxPageNumber" content="0"/>
+</head>
+<docTitle><text>Ruby on Rails Guides</text></docTitle>
+<docAuthor><text>docrails</text></docAuthor>
+<navMap>
+ <navPoint playOrder="0" class="periodical" id="periodical">
+ <navLabel>
+ <text>Table of Contents</text>
+ </navLabel>
+ <content src="toc.html"/>
+
+ <navPoint class="section" id="welcome" playOrder="1">
+ <navLabel>
+ <text>Introduction</text>
+ </navLabel>
+ <content src="welcome.html"/>
+
+ <navPoint class="article" id="welcome" playOrder="2">
+ <navLabel>
+ <text>Welcome</text>
+ </navLabel>
+ <content src="welcome.html"/>
+ </navPoint>
+ <navPoint class="article" id="credits" playOrder="3">
+ <navLabel><text>Credits</text></navLabel>
+ <content src="credits.html">
+ </navPoint>
+ <navPoint class="article" id="copyright" playOrder="4">
+ <navLabel><text>Copyright &amp; License</text></navLabel>
+ <content src="copyright.html">
+ </navPoint>
+ </navPoint>
+
+ <% play_order = 4 %>
+ <% documents_by_section.each_with_index do |section, section_no| %>
+ <navPoint class="section" id="chapter_<%= section_no + 1 %>" playOrder="<% play_order +=1 %>">
+ <navLabel>
+ <text><%= section['name'] %></text>
+ </navLabel>
+ <content src="<%=section['documents'].first['url'] %>"/>
+
+ <% section['documents'].each_with_index do |document, document_no| %>
+ <navPoint class="article" id="_<%=section_no+1%>.<%=document_no+1%>" playOrder="<%=play_order +=1 %>">
+ <navLabel>
+ <text><%= document['name'] %></text>
+ </navLabel>
+ <content src="<%=document['url'] %>"/>
+ </navPoint>
+ <% end %>
+ </navPoint>
+ <% end %>
+
+ </navPoint>
+</navMap>
+</ncx>
diff --git a/railties/guides/source/kindle/welcome.html.erb b/railties/guides/source/kindle/welcome.html.erb
new file mode 100644
index 0000000000..e30704c4e6
--- /dev/null
+++ b/railties/guides/source/kindle/welcome.html.erb
@@ -0,0 +1,5 @@
+<%= render 'welcome' %>
+
+<h3>Kindle Edition</h3>
+
+The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome, please see the "Feedback" section at the end of each guide for instructions.
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index 4c979888b7..35b6fc7014 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -38,57 +38,21 @@
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="Return to home page">Guides.rubyonrails.org</a></h1>
- <p class="hide"><a href="#mainCol">Skip navigation</a>.</p>
<ul class="nav">
<li><a href="index.html">Home</a></li>
<li class="index"><a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu">Guides Index</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
- <dl class="L">
- <dt>Start Here</dt>
- <dd><a href="getting_started.html">Getting Started with Rails</a></dd>
- <dt>Models</dt>
- <dd><a href="migrations.html">Rails Database Migrations</a></dd>
- <dd><a href="active_record_validations_callbacks.html">Active Record Validations and Callbacks</a></dd>
- <dd><a href="association_basics.html">Active Record Associations</a></dd>
- <dd><a href="active_record_querying.html">Active Record Query Interface</a></dd>
- <dt>Views</dt>
- <dd><a href="layouts_and_rendering.html">Layouts and Rendering in Rails</a></dd>
- <dd><a href="form_helpers.html">Action View Form Helpers</a></dd>
- <dt>Controllers</dt>
- <dd><a href="action_controller_overview.html">Action Controller Overview</a></dd>
- <dd><a href="routing.html">Rails Routing from the Outside In</a></dd>
- </dl>
- <dl class="R">
- <dt>Digging Deeper</dt>
- <dd><a href="active_support_core_extensions.html">Active Support Core Extensions</a></dd>
- <dd><a href="i18n.html">Rails Internationalization API</a></dd>
- <dd><a href="action_mailer_basics.html">Action Mailer Basics</a></dd>
- <dd><a href="testing.html">Testing Rails Applications</a></dd>
- <dd><a href="security.html">Securing Rails Applications</a></dd>
- <dd><a href="debugging_rails_applications.html">Debugging Rails Applications</a></dd>
- <dd><a href="performance_testing.html">Performance Testing Rails Applications</a></dd>
- <dd><a href="configuring.html">Configuring Rails Applications</a></dd>
- <dd><a href="command_line.html">Rails Command Line Tools and Rake Tasks</a></dd>
- <dd><a href="caching_with_rails.html">Caching with Rails</a></dd>
- <dd><a href="asset_pipeline.html">Asset Pipeline</a></dd>
-
- <dt>Extending Rails</dt>
- <dd><a href="plugins.html">The Basics of Creating Rails Plugins</a></dd>
- <dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
- <dd><a href="generators.html">Creating and Customizing Rails Generators</a></dd>
-
- <dt>Contributing to Ruby on Rails</dt>
- <dd><a href="contributing_to_ruby_on_rails.html">Contributing to Ruby on Rails</a></dd>
- <dd><a href="api_documentation_guidelines.html">API Documentation Guidelines</a></dd>
- <dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails Guides Guidelines</a></dd>
-
- <dt>Release Notes</dt>
- <dd><a href="3_1_release_notes.html">Ruby on Rails 3.1 Release Notes</a></dd>
- <dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 Release Notes</a></dd>
- <dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 Release Notes</a></dd>
- <dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 Release Notes</a></dd>
- </dl>
+ <% ['L', 'R'].each do |position| %>
+ <dl class="<%= position %>">
+ <% docs_for_menu(position).each do |section| %>
+ <dt><%= section['name'] %></dt>
+ <% finished_documents(section['documents']).each do |document| %>
+ <dd><a href="<%= document['url'] %>"><%= document['name'] %></a></dd>
+ <% end %>
+ <% end %>
+ </dl>
+ <% end %>
</div>
</li>
<li><a href="contributing_to_ruby_on_rails.html">Contribute</a></li>
@@ -143,8 +107,7 @@
<hr class="hide" />
<div id="footer">
<div class="wrapper">
- <p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0</a> License</p>
- <p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p>
+ <%= render 'license' %>
</div>
</div>
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index df7b9b364c..6ac9645917 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -495,7 +495,7 @@ def show
end
</ruby>
-Make sure to use +and return+ and not +&amp;&amp; return+, since +&amp;&amp; return+ will not work due to the operator precedence in the Ruby Language.
+Make sure to use +and return+ instead of +&amp;&amp; return+ because +&amp;&amp; return+ will not work due to the operator precedence in the Ruby Language.
Note that the implicit render done by ActionController detects if +render+ has been called, so the following will work without errors:
@@ -656,7 +656,7 @@ WARNING: The asset tag helpers do _not_ verify the existence of the assets at th
h5. Linking to Feeds with the +auto_discovery_link_tag+
-The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag:
+The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsreaders can use to detect the presence of RSS or Atom feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag:
<erb>
<%= auto_discovery_link_tag(:rss, {:action => "feed"},
@@ -667,23 +667,37 @@ There are three tag options available for the +auto_discovery_link_tag+:
* +:rel+ specifies the +rel+ value in the link. The default value is "alternate".
* +:type+ specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically.
-* +:title+ specifies the title of the link. The default value is the upshifted +:type+ value, for example, "ATOM" or "RSS".
+* +:title+ specifies the title of the link. The default value is the uppercased +:type+ value, for example, "ATOM" or "RSS".
h5. Linking to JavaScript Files with the +javascript_include_tag+
-The +javascript_include_tag+ helper returns an HTML +script+ tag for each source provided. Rails looks in +public/javascripts+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/javascripts/main.js+:
+The +javascript_include_tag+ helper returns an HTML +script+ tag for each source provided.
+
+If you are using Rails with the "Asset Pipeline":asset_pipeline.html enabled, this helper will generate a link to +/assets/javascripts/+ rather than +public/javascripts+ which was used in earlier versions of Rails. This link is then served by the Sprockets gem, which was introduced in Rails 3.1.
+
+A JavaScript file within a Rails application or Rails engine goes in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+. These locations are explained in detail in the "Asset Organization section in the Asset Pipeline Guide":asset_pipeline.html#asset-organization
+
+You can specify a full path relative to the document root, or a URL, if you prefer. For example, to link to a JavaScript file that is inside a directory called +javascripts+ inside of one of +app/assets+, +lib/assets+ or +vendor/assets+, you would do this:
<erb>
<%= javascript_include_tag "main" %>
</erb>
-To include +public/javascripts/main.js+ and +public/javascripts/columns.js+:
+Rails will then output a +script+ tag such as this:
+
+<html>
+<script src='/assets/main.js' type="text/javascript"></script>
+</html>
+
+The request to this asset is then served by the Sprockets gem.
+
+To include multiple files such as +app/assets/javascripts/main.js+ and +app/assets/javascripts/columns.js+ at the same time:
<erb>
<%= javascript_include_tag "main", "columns" %>
</erb>
-To include +public/javascripts/main.js+ and +public/photos/columns.js+:
+To include +app/assets/javascripts/main.js+ and +app/assets/javascripts/photos/columns.js+:
<erb>
<%= javascript_include_tag "main", "/photos/columns" %>
@@ -701,15 +715,38 @@ If the application does not use the asset pipeline, the +:defaults+ option loads
<%= javascript_include_tag :defaults %>
</erb>
-And you can in any case override the expansion in <tt>config/application.rb</tt>:
+Outputting +script+ tags such as this:
+
+<html>
+<script src="/javascripts/jquery.js" type="text/javascript"></script>
+<script src="/javascripts/jquery_ujs.js" type="text/javascript"></script>
+</html>
+
+These two files for jQuery, +jquery.js+ and +jquery_ujs.js+ must be placed inside +public/javascripts+ if the application doesn't use the asset pipeline. These files can be downloaded from the "jquery-rails repository on GitHub":https://github.com/indirect/jquery-rails/tree/master/vendor/assets/javascripts
+
+WARNING: If you are using the asset pipeline, this tag will render a +script+ tag for an asset called +defaults.js+, which would not exist in your application unless you've explicitly created it.
+
+And you can in any case override the +:defaults+ expansion in <tt>config/application.rb</tt>:
<ruby>
config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
</ruby>
-When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in <tt>public/javascripts</tt> it will be included as well at then end.
+You can also define new defaults:
+
+<ruby>
+config.action_view.javascript_expansions[:projects] = %w(projects.js tickets.js)
+</ruby>
+
+And use them by referencing them exactly like +:defaults+:
-Also, the +:all+ option loads every JavaScript file in +public/javascripts+:
+<erb>
+<%= javascript_include_tag :projects %>
+</erb>
+
+When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in <tt>public/javascripts</tt> it will be included as well at the end.
+
+Also, if the asset pipeline is disabled, the +:all+ expansion loads every JavaScript file in +public/javascripts+:
<erb>
<%= javascript_include_tag :all %>
@@ -740,19 +777,23 @@ You can even use dynamic paths such as +cache/#{current_site}/main/display+.
h5. Linking to CSS Files with the +stylesheet_link_tag+
-The +stylesheet_link_tag+ helper returns an HTML +&lt;link&gt;+ tag for each source provided. Rails looks in +public/stylesheets+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/stylesheets/main.css+:
+The +stylesheet_link_tag+ helper returns an HTML +&lt;link&gt;+ tag for each source provided.
+
+If you are using Rails with the "Asset Pipeline" enabled, this helper will generate a link to +/assets/stylesheets/+. This link is then processed by the Sprockets gem. A stylesheet file can be stored in one of three locations: +app/assets+, +lib/assets+ or +vendor/assets+.
+
+You can specify a full path relative to the document root, or a URL. For example, to link to a stylesheet file that is inside a directory called +stylesheets+ inside of one of +app/assets+, +lib/assets+ or +vendor/assets+, you would do this:
<erb>
<%= stylesheet_link_tag "main" %>
</erb>
-To include +public/stylesheets/main.css+ and +public/stylesheets/columns.css+:
+To include +app/assets/stylesheets/main.css+ and +app/assets/stylesheets/columns.css+:
<erb>
<%= stylesheet_link_tag "main", "columns" %>
</erb>
-To include +public/stylesheets/main.css+ and +public/photos/columns.css+:
+To include +app/assets/stylesheets/main.css+ and +app/assets/stylesheets/photos/columns.css+:
<erb>
<%= stylesheet_link_tag "main", "/photos/columns" %>
@@ -770,7 +811,7 @@ By default, the +stylesheet_link_tag+ creates links with +media="screen" rel="st
<%= stylesheet_link_tag "main_print", :media => "print" %>
</erb>
-The +all+ option links every CSS file in +public/stylesheets+:
+If the asset pipeline is disabled, the +all+ option links every CSS file in +public/stylesheets+:
<erb>
<%= stylesheet_link_tag :all %>
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index 23e36b39f9..c11f8e221b 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -1,12 +1,24 @@
h2. Migrations
-Migrations are a convenient way for you to alter your database in a structured and organized manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them. You'd also have to keep track of which changes need to be run against the production machines next time you deploy.
-
-Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database.
-
-Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
-
-You'll learn all about migrations including:
+Migrations are a convenient way for you to alter your database in a structured
+and organized manner. You could edit fragments of SQL by hand but you would then
+be responsible for telling other developers that they need to go and run them.
+You'd also have to keep track of which changes need to be run against the
+production machines next time you deploy.
+
+Active Record tracks which migrations have already been run so all you have to
+do is update your source and run +rake db:migrate+. Active Record will work out
+which migrations should be run. It will also update your +db/schema.rb+ file to
+match the structure of your database.
+
+Migrations also allow you to describe these transformations using Ruby. The
+great thing about this is that (like most of Active Record's functionality) it
+is database independent: you don't need to worry about the precise syntax of
++CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can
+drop down to raw SQL for database specific features). For example you could use
+SQLite3 in development, but MySQL in production.
+
+In this guide, you'll learn all about migrations including:
* The generators you can use to create them
* The methods Active Record provides to manipulate your database
@@ -17,7 +29,8 @@ endprologue.
h3. Anatomy of a Migration
-Before we dive into the details of a migration, here are a few examples of the sorts of things you can do:
+Before we dive into the details of a migration, here are a few examples of the
+sorts of things you can do:
<ruby>
class CreateProducts < ActiveRecord::Migration
@@ -36,9 +49,15 @@ class CreateProducts < ActiveRecord::Migration
end
</ruby>
-This migration adds a table called +products+ with a string column called +name+ and a text column called +description+. A primary key column called +id+ will also be added, however since this is the default we do not need to ask for this. The timestamp columns +created_at+ and +updated_at+ which Active Record populates automatically will also be added. Reversing this migration is as simple as dropping the table.
+This migration adds a table called +products+ with a string column called +name+
+and a text column called +description+. A primary key column called +id+ will
+also be added, however since this is the default we do not need to ask for this.
+The timestamp columns +created_at+ and +updated_at+ which Active Record
+populates automatically will also be added. Reversing this migration is as
+simple as dropping the table.
-Migrations are not limited to changing the schema. You can also use them to fix bad data in the database or populate new fields:
+Migrations are not limited to changing the schema. You can also use them to fix
+bad data in the database or populate new fields:
<ruby>
class AddReceiveNewsletterToUsers < ActiveRecord::Migration
@@ -55,12 +74,18 @@ class AddReceiveNewsletterToUsers < ActiveRecord::Migration
end
</ruby>
-NOTE: Some "caveats":#using-models-in-your-migrations apply to using models in your migrations.
+NOTE: Some "caveats":#using-models-in-your-migrations apply to using models in
+your migrations.
-This migration adds a +receive_newsletter+ column to the +users+ table. We want it to default to +false+ for new users, but existing users are considered
-to have already opted in, so we use the User model to set the flag to +true+ for existing users.
+This migration adds a +receive_newsletter+ column to the +users+ table. We want
+it to default to +false+ for new users, but existing users are considered to
+have already opted in, so we use the User model to set the flag to +true+ for
+existing users.
-Rails 3.1 makes migrations smarter by providing a new <tt>change</tt> method. This method is preferred for writing constructive migrations (adding columns or tables). The migration knows how to migrate your database and reverse it when the migration is rolled back without the need to write a separate +down+ method.
+Rails 3.1 makes migrations smarter by providing a new <tt>change</tt> method.
+This method is preferred for writing constructive migrations (adding columns or
+tables). The migration knows how to migrate your database and reverse it when
+the migration is rolled back without the need to write a separate +down+ method.
<ruby>
class CreateProducts < ActiveRecord::Migration
@@ -77,64 +102,112 @@ end
h4. Migrations are Classes
-A migration is a subclass of <tt>ActiveRecord::Migration</tt> that implements two methods: +up+ (perform the required transformations) and +down+ (revert them).
+A migration is a subclass of <tt>ActiveRecord::Migration</tt> that implements
+two methods: +up+ (perform the required transformations) and +down+ (revert
+them).
-Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later):
+Active Record provides methods that perform common data definition tasks in a
+database independent way (you'll read about them in detail later):
-* +create_table+
-* +change_table+
-* +drop_table+
* +add_column+
+* +add_index+
* +change_column+
-* +rename_column+
+* +change_table+
+* +create_table+
+* +create_join_table+
+* +drop_table+
* +remove_column+
-* +add_index+
* +remove_index+
+* +rename_column+
-If you need to perform tasks specific to your database (for example create a "foreign key":#active-record-and-referential-integrity constraint) then the +execute+ method allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models).
+If you need to perform tasks specific to your database (for example create a
+"foreign key":#active-record-and-referential-integrity constraint) then the
++execute+ method allows you to execute arbitrary SQL. A migration is just a
+regular Ruby class so you're not limited to these functions. For example after
+adding a column you could write code to set the value of that column for
+existing records (if necessary using your models).
-On databases that support transactions with statements that change the schema (such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the database does not support this (for example MySQL) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand.
+On databases that support transactions with statements that change the schema
+(such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the
+database does not support this (for example MySQL) then when a migration fails
+the parts of it that succeeded will not be rolled back. You will have to rollback
+the changes that were made by hand.
h4. What's in a Name
-Migrations are stored in files in +db/migrate+, one for each migration class. The name of the file is of the form +YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The name of the migration class (CamelCased version) should match the latter part of the file name. For example +20080906120000_create_products.rb+ should define class +CreateProducts+ and +20080906120001_add_details_to_products.rb+ should define +AddDetailsToProducts+. If you do feel the need to change the file name then you <em>have to</em> update the name of the class inside or Rails will complain about a missing class.
-
-Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by adding the following line to +config/application.rb+.
+Migrations are stored as files in the +db/migrate+ directory, one for each
+migration class. The name of the file is of the form
++YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp
+identifying the migration followed by an underscore followed by the name
+of the migration. The name of the migration class (CamelCased version)
+should match the latter part of the file name. For example
++20080906120000_create_products.rb+ should define class +CreateProducts+ and
++20080906120001_add_details_to_products.rb+ should define
++AddDetailsToProducts+. If you do feel the need to change the file name then you
+<em>have to</em> update the name of the class inside or Rails will complain
+about a missing class.
+
+Internally Rails only uses the migration's number (the timestamp) to identify
+them. Prior to Rails 2.1 the migration number started at 1 and was incremented
+each time a migration was generated. With multiple developers it was easy for
+these to clash requiring you to rollback migrations and renumber them. With
+Rails 2.1+ this is largely avoided by using the creation time of the migration
+to identify them. You can revert to the old numbering scheme by adding the
+following line to +config/application.rb+.
<ruby>
config.active_record.timestamped_migrations = false
</ruby>
-The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.
+The combination of timestamps and recording which migrations have been run
+allows Rails to handle common situations that occur with multiple developers.
-For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob adds +20080906124500+ and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so +rake db:migrate+ would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their +down+ methods.
+For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob
+adds +20080906124500+ and runs it. Alice finishes her changes and checks in her
+migrations and Bob pulls down the latest changes. When Bob runs +rake db:migrate+,
+Rails knows that it has not run Alice's two migrations so it executes the +up+ method for each migration.
-Of course this is no substitution for communication within the team. For example, if Alice's migration removed a table that Bob's migration assumed to exist, then trouble would certainly strike.
+Of course this is no substitution for communication within the team. For
+example, if Alice's migration removed a table that Bob's migration assumed to
+exist, then trouble would certainly strike.
h4. Changing Migrations
-Occasionally you will make a mistake when writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run +rake db:migrate+. You must rollback the migration (for example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version.
+Occasionally you will make a mistake when writing a migration. If you have
+already run the migration then you cannot just edit the migration and run the
+migration again: Rails thinks it has already run the migration and so will do
+nothing when you run +rake db:migrate+. You must rollback the migration (for
+example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version.
-In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead, you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or, more generally, which has not been propagated beyond your development machine) is relatively harmless.
+In general editing existing migrations is not a good idea: you will be creating
+extra work for yourself and your co-workers and cause major headaches if the
+existing version of the migration has already been run on production machines.
+Instead, you should write a new migration that performs the changes you require.
+Editing a freshly generated migration that has not yet been committed to source
+control (or, more generally, which has not been propagated beyond your
+development machine) is relatively harmless.
h4. Supported Types
-Active Record supports the following types:
+Active Record supports the following database column types:
+* +:binary+
+* +:boolean+
+* +:date+
+* +:datetime+
+* +:decimal+
+* +:float+
+* +:integer+
* +:primary_key+
* +:string+
* +:text+
-* +:integer+
-* +:float+
-* +:decimal+
-* +:datetime+
-* +:timestamp+
* +:time+
-* +:date+
-* +:binary+
-* +:boolean+
+* +:timestamp+
-These will be mapped onto an appropriate underlying database type. For example, with MySQL the type +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non-sexy syntax, for example
+These will be mapped onto an appropriate underlying database type. For example,
+with MySQL the type +:string+ is mapped to +VARCHAR(255)+. You can create
+columns of types not supported by Active Record when using the non-sexy syntax,
+for example
<ruby>
create_table :products do |t|
@@ -148,7 +221,10 @@ h3. Creating a Migration
h4. Creating a Model
-The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want, then statements for adding these columns will also be created. For example, running
+The model and scaffold generators will create migrations appropriate for adding
+a new model. This migration will already contain instructions for creating the
+relevant table. If you tell Rails what columns you want, then statements for
+adding these columns will also be created. For example, running
<shell>
$ rails generate model Product name:string description:text
@@ -169,12 +245,15 @@ class CreateProducts < ActiveRecord::Migration
end
</ruby>
-You can append as many column name/type pairs as you want. By default +t.timestamps+ (which creates the +updated_at+ and +created_at+ columns that
-are automatically populated by Active Record) will be added for you.
+You can append as many column name/type pairs as you want. By default, the
+generated migration will include +t.timestamps+ (which creates the
++updated_at+ and +created_at+ columns that are automatically populated
+by Active Record).
h4. Creating a Standalone Migration
-If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:
+If you are creating migrations for other purposes (for example to add a column
+to an existing table) then you can also use the migration generator:
<shell>
$ rails generate migration AddPartNumberToProducts
@@ -189,7 +268,9 @@ class AddPartNumberToProducts < ActiveRecord::Migration
end
</ruby>
-If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is followed by a list of column names and types then a migration containing the appropriate +add_column+ and +remove_column+ statements will be created.
+If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is
+followed by a list of column names and types then a migration containing the
+appropriate +add_column+ and +remove_column+ statements will be created.
<shell>
$ rails generate migration AddPartNumberToProducts part_number:string
@@ -242,17 +323,23 @@ class AddDetailsToProducts < ActiveRecord::Migration
end
</ruby>
-As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit.
+As always, what has been generated for you is just a starting point. You can add
+or remove from it as you see fit by editing the
+db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb file.
-NOTE: The generated migration file for destructive migrations will still be old-style using the +up+ and +down+ methods. This is because Rails doesn't know the original data types defined when you made the original changes.
+NOTE: The generated migration file for destructive migrations will still be
+old-style using the +up+ and +down+ methods. This is because Rails needs to know
+the original data types defined when you made the original changes.
h3. Writing a Migration
-Once you have created your migration using one of the generators it's time to get to work!
+Once you have created your migration using one of the generators it's time to
+get to work!
h4. Creating a Table
-Migration method +create_table+ will be one of your workhorses. A typical use would be
+Migration method +create_table+ will be one of your workhorses. A typical use
+would be
<ruby>
create_table :products do |t|
@@ -260,9 +347,11 @@ create_table :products do |t|
end
</ruby>
-which creates a +products+ table with a column called +name+ (and as discussed below, an implicit +id+ column).
+which creates a +products+ table with a column called +name+ (and as discussed
+below, an implicit +id+ column).
-The object yielded to the block allows you to create columns on the table. There are two ways of doing it. The first (traditional) form looks like
+The object yielded to the block allows you to create columns on the table. There
+are two ways of doing it. The first (traditional) form looks like
<ruby>
create_table :products do |t|
@@ -270,7 +359,9 @@ create_table :products do |t|
end
</ruby>
-The second form, the so called "sexy" migration, drops the somewhat redundant +column+ method. Instead, the +string+, +integer+, etc. methods create a column of that type. Subsequent parameters are the same.
+The second form, the so called "sexy" migration, drops the somewhat redundant
++column+ method. Instead, the +string+, +integer+, etc. methods create a column
+of that type. Subsequent parameters are the same.
<ruby>
create_table :products do |t|
@@ -278,7 +369,12 @@ create_table :products do |t|
end
</ruby>
-By default, +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or, if you don't want a primary key at all (for example for a HABTM join table), you can pass the option +:id => false+. If you need to pass database specific options you can place an SQL fragment in the +:options+ option. For example,
+By default, +create_table+ will create a primary key called +id+. You can change
+the name of the primary key with the +:primary_key+ option (don't forget to
+update the corresponding model) or, if you don't want a primary key at all (for
+example for a HABTM join table), you can pass the option +:id => false+. If you
+need to pass database specific options you can place an SQL fragment in the
++:options+ option. For example,
<ruby>
create_table :products, :options => "ENGINE=BLACKHOLE" do |t|
@@ -286,11 +382,43 @@ create_table :products, :options => "ENGINE=BLACKHOLE" do |t|
end
</ruby>
-will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table (when using MySQL, the default is +ENGINE=InnoDB+).
+will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table
+(when using MySQL, the default is +ENGINE=InnoDB+).
+
+h4. Creating a Join Table
+
+Migration method +create_join_table+ creates a HABTM join table. A typical use
+would be
+
+<ruby>
+create_join_table :products, :categories
+</ruby>
+
+which creates a +categories_products+ table with two columns called +category_id+ and +product_id+.
+These columns have the option +:null+ set to +false+ by default.
+
+You can pass the option +:table_name+ with you want to customize the table name. For example,
+
+<ruby>
+create_join_table :products, :categories, :table_name => :categorization
+</ruby>
+
+will create a +categorization+ table.
+
+By default, +create_join_table+ will create two columns with no options, but you can specify these
+options using the +:column_options+ option. For example,
+
+<ruby>
+create_join_table :products, :categories, :column_options => {:null => true}
+</ruby>
+
+will create the +product_id+ and +category_id+ with the +:null+ option as +true+.
h4. Changing Tables
-A close cousin of +create_table+ is +change_table+, used for changing existing tables. It is used in a similar fashion to +create_table+ but the object yielded to the block knows more tricks. For example
+A close cousin of +create_table+ is +change_table+, used for changing existing
+tables. It is used in a similar fashion to +create_table+ but the object yielded
+to the block knows more tricks. For example
<ruby>
change_table :products do |t|
@@ -301,28 +429,23 @@ change_table :products do |t|
end
</ruby>
-removes the +description+ and +name+ columns, creates a +part_number+ column and adds an index on it. Finally it renames the +upccode+ column. This is the same as doing
-
-<ruby>
-remove_column :products, :description
-remove_column :products, :name
-add_column :products, :part_number, :string
-add_index :products, :part_number
-rename_column :products, :upccode, :upc_code
-</ruby>
-
-You don't have to keep repeating the table name and it groups all the statements related to modifying one particular table. The individual transformation names are also shorter, for example +remove_column+ becomes just +remove+ and +add_index+ becomes just +index+.
+removes the +description+ and +name+ columns, creates a +part_number+ string
+column and adds an index on it. Finally it renames the +upccode+ column.
h4. Special Helpers
-Active Record provides some shortcuts for common functionality. It is for example very common to add both the +created_at+ and +updated_at+ columns and so there is a method that does exactly that:
+Active Record provides some shortcuts for common functionality. It is for
+example very common to add both the +created_at+ and +updated_at+ columns and so
+there is a method that does exactly that:
<ruby>
create_table :products do |t|
t.timestamps
end
</ruby>
-will create a new products table with those two columns (plus the +id+ column) whereas
+
+will create a new products table with those two columns (plus the +id+ column)
+whereas
<ruby>
change_table :products do |t|
@@ -331,7 +454,8 @@ end
</ruby>
adds those columns to an existing table.
-The other helper is called +references+ (also available as +belongs_to+). In its simplest form it just adds some readability
+Another helper is called +references+ (also available as +belongs_to+). In its
+simplest form it just adds some readability.
<ruby>
create_table :products do |t|
@@ -339,24 +463,42 @@ create_table :products do |t|
end
</ruby>
-will create a +category_id+ column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the +_id+ for you. If you have polymorphic +belongs_to+ associations then +references+ will add both of the columns required:
+will create a +category_id+ column of the appropriate type. Note that you pass
+the model name, not the column name. Active Record adds the +_id+ for you. If
+you have polymorphic +belongs_to+ associations then +references+ will add both
+of the columns required:
<ruby>
create_table :products do |t|
t.references :attachment, :polymorphic => {:default => 'Photo'}
end
</ruby>
-will add an +attachment_id+ column and a string +attachment_type+ column with a default value of 'Photo'.
-NOTE: The +references+ helper does not actually create foreign key constraints for you. You will need to use +execute+ or a plugin that adds "foreign key support":#active-record-and-referential-integrity.
+will add an +attachment_id+ column and a string +attachment_type+ column with
+a default value of 'Photo'.
-If the helpers provided by Active Record aren't enough you can use the +execute+ method to execute arbitrary SQL.
+NOTE: The +references+ helper does not actually create foreign key constraints
+for you. You will need to use +execute+ or a plugin that adds "foreign key
+support":#active-record-and-referential-integrity.
-For more details and examples of individual methods, check the API documentation, in particular the documentation for "<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html (which provides the methods available in the +up+ and +down+ methods), "<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html (which provides the methods available on the object yielded by +create_table+) and "<tt>ActiveRecord::ConnectionAdapters::Table</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html (which provides the methods available on the object yielded by +change_table+).
+If the helpers provided by Active Record aren't enough you can use the +execute+
+method to execute arbitrary SQL.
-h4. Writing Your +change+ Method
+For more details and examples of individual methods, check the API documentation,
+in particular the documentation for
+"<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html
+(which provides the methods available in the +up+ and +down+ methods),
+"<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html
+(which provides the methods available on the object yielded by +create_table+)
+and
+"<tt>ActiveRecord::ConnectionAdapters::Table</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
+(which provides the methods available on the object yielded by +change_table+).
-The +change+ method removes the need to write both +up+ and +down+ methods in those cases that Rails know how to revert the changes automatically. Currently, the +change+ method supports only these migration definitions:
+h4. Using the +change+ Method
+
+The +change+ method removes the need to write both +up+ and +down+ methods in
+those cases that Rails know how to revert the changes automatically. Currently,
+the +change+ method supports only these migration definitions:
* +add_column+
* +add_index+
@@ -367,15 +509,20 @@ The +change+ method removes the need to write both +up+ and +down+ methods in th
* +rename_index+
* +rename_table+
-If you're going to use other methods, you'll have to write the +up+ and +down+ methods normally.
+If you're going to need to use any other methods, you'll have to write the
++up+ and +down+ methods instead of using the +change+ method.
-h4. Writing Your +down+ Method
+h4. Using the +up+/+down+ Methods
-The +down+ method of your migration should revert the transformations done by the +up+ method. In other words, the database schema should be unchanged if you do an +up+ followed by a +down+. For example, if you create a table in the +up+ method, you should drop it in the +down+ method. It is wise to reverse the transformations in precisely the reverse order they were made in the +up+ method. For example,
+The +down+ method of your migration should revert the transformations done by
+the +up+ method. In other words, the database schema should be unchanged if you
+do an +up+ followed by a +down+. For example, if you create a table in the +up+
+method, you should drop it in the +down+ method. It is wise to reverse the
+transformations in precisely the reverse order they were made in the +up+
+method. For example,
<ruby>
class ExampleMigration < ActiveRecord::Migration
-
def up
create_table :products do |t|
t.references :category
@@ -387,47 +534,69 @@ class ExampleMigration < ActiveRecord::Migration
FOREIGN KEY (category_id)
REFERENCES categories(id)
SQL
-
add_column :users, :home_page_url, :string
-
rename_column :users, :email, :email_address
end
def down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
- execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories"
+ execute <<-SQL
+ ALTER TABLE products
+ DROP FOREIGN KEY fk_products_categories
+ SQL
drop_table :products
end
end
</ruby>
-Sometimes your migration will do something which is just plain irreversible; for example, it might destroy some data. In such cases, you can raise +ActiveRecord::IrreversibleMigration+ from your +down+ method. If someone tries to revert your migration, an error message will be displayed saying that it can't be done.
+Sometimes your migration will do something which is just plain irreversible; for
+example, it might destroy some data. In such cases, you can raise
++ActiveRecord::IrreversibleMigration+ from your +down+ method. If someone tries
+to revert your migration, an error message will be displayed saying that it
+can't be done.
h3. Running Migrations
-Rails provides a set of rake tasks to work with migrations which boil down to running certain sets of migrations. The very first migration related rake task you will use will probably be +db:migrate+. In its most basic form it just runs the +up+ method for all the migrations that have not yet been run. If there are no such migrations, it exits.
+Rails provides a set of rake tasks to work with migrations which boil down to
+running certain sets of migrations.
-Note that running the +db:migrate+ also invokes the +db:schema:dump+ task, which will update your db/schema.rb file to match the structure of your database.
+The very first migration related rake task you will use will probably be
++rake db:migrate+. In its most basic form it just runs the +up+ or +change+
+method for all the migrations that have not yet been run. If there are
+no such migrations, it exits. It will run these migrations in order based
+on the date of the migration.
-If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version. The
-version is the numerical prefix on the migration's filename. For example, to migrate to version 20080906120000 run
+Note that running the +db:migrate+ also invokes the +db:schema:dump+ task, which
+will update your db/schema.rb file to match the structure of your database.
+
+If you specify a target version, Active Record will run the required migrations
+(up, down or change) until it has reached the specified version. The version
+is the numerical prefix on the migration's filename. For example, to migrate
+to version 20080906120000 run
<shell>
$ rake db:migrate VERSION=20080906120000
</shell>
-If version 20080906120000 is greater than the current version (i.e., it is migrating upwards), this will run the +up+ method on all migrations up to and including 20080906120000. If migrating downwards, this will run the +down+ method on all the migrations down to, but not including, 20080906120000.
+If version 20080906120000 is greater than the current version (i.e., it is
+migrating upwards), this will run the +up+ method on all migrations up to and
+including 20080906120000, and will not execute any later migrations. If
+migrating downwards, this will run the +down+ method on all the migrations
+down to, but not including, 20080906120000.
h4. Rolling Back
-A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run
+A common task is to rollback the last migration, for example if you made a
+mistake in it and wish to correct it. Rather than tracking down the version
+number associated with the previous migration you can run
<shell>
$ rake db:rollback
</shell>
-This will run the +down+ method from the latest migration. If you need to undo several migrations you can provide a +STEP+ parameter:
+This will run the +down+ method from the latest migration. If you need to undo
+several migrations you can provide a +STEP+ parameter:
<shell>
$ rake db:rollback STEP=3
@@ -435,46 +604,65 @@ $ rake db:rollback STEP=3
will run the +down+ method from the last 3 migrations.
-The +db:migrate:redo+ task is a shortcut for doing a rollback and then migrating back up again. As with the +db:rollback+ task, you can use the +STEP+ parameter if you need to go more than one version back, for example
+The +db:migrate:redo+ task is a shortcut for doing a rollback and then migrating
+back up again. As with the +db:rollback+ task, you can use the +STEP+ parameter
+if you need to go more than one version back, for example
<shell>
$ rake db:migrate:redo STEP=3
</shell>
-Neither of these Rake tasks do anything you could not do with +db:migrate+. They are simply more convenient, since you do not need to explicitly specify the version to migrate to.
+Neither of these Rake tasks do anything you could not do with +db:migrate+. They
+are simply more convenient, since you do not need to explicitly specify the
+version to migrate to.
+
+h4. Resetting the database
-Lastly, the +db:reset+ task will drop the database, recreate it and load the current schema into it.
+The +rake db:reset+ task will drop the database, recreate it and load the
+current schema into it.
-NOTE: This is not the same as running all the migrations - see the section on "schema.rb":#schema-dumping-and-you.
+NOTE: This is not the same as running all the migrations - see the section on
+"schema.rb":#schema-dumping-and-you.
-h4. Being Specific
+h4. Running specific migrations
-If you need to run a specific migration up or down, the +db:migrate:up+ and +db:migrate:down+ tasks will do that. Just specify the appropriate version and the corresponding migration will have its +up+ or +down+ method invoked, for example,
+If you need to run a specific migration up or down, the +db:migrate:up+ and
++db:migrate:down+ tasks will do that. Just specify the appropriate version and
+the corresponding migration will have its +up+ or +down+ method invoked, for
+example,
<shell>
$ rake db:migrate:up VERSION=20080906120000
</shell>
-will run the +up+ method from the 20080906120000 migration. These tasks check whether the migration has already run, so for example +db:migrate:up VERSION=20080906120000+ will do nothing if Active Record believes that 20080906120000 has already been run.
+will run the +up+ method from the 20080906120000 migration. These tasks still
+check whether the migration has already run, so for example +db:migrate:up
+VERSION=20080906120000+ will do nothing if Active Record believes that
+20080906120000 has already been run.
-h4. Being Talkative
+h4. Changing the output of running migrations
-By default migrations tell you exactly what they're doing and how long it took. A migration creating a table and adding an index might produce output like this
+By default migrations tell you exactly what they're doing and how long it took.
+A migration creating a table and adding an index might produce output like this
<shell>
-20080906170109 CreateProducts: migrating
+== CreateProducts: migrating =================================================
-- create_table(:products)
- -> 0.0021s
--- add_index(:products, :name)
- -> 0.0026s
-20080906170109 CreateProducts: migrated (0.0059s)
+ -> 0.0028s
+== CreateProducts: migrated (0.0028s) ========================================
</shell>
-Several methods are provided that allow you to control all this:
+Several methods are provided in migrations that allow you to control all this:
-* +suppress_messages+ takes a block as an argument and suppresses any output generated by the block.
-* +say+ takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent or not.
-* +say_with_time+ outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected.
+|_.Method |_.Purpose|
+|suppress_messages |Takes a block as an argument and suppresses any output
+ generated by the block.|
+|say |Takes a message argument and outputs it as is. A second
+ boolean argument can be passed to specify whether to
+ indent or not.|
+|say_with_time |Outputs text along with how long it took to run its
+ block. If the block returns an integer it assumes it
+ is the number of rows affected.|
For example, this migration
@@ -502,37 +690,46 @@ end
generates the following output
<shell>
-20080906170109 CreateProducts: migrating
- Created a table
+== CreateProducts: migrating =================================================
+-- Created a table
-> and an index!
- Waiting for a while
- -> 10.0001s
+-- Waiting for a while
+ -> 10.0013s
-> 250 rows
-20080906170109 CreateProducts: migrated (10.0097s)
+== CreateProducts: migrated (10.0054s) =======================================
</shell>
-If you just want Active Record to shut up, then running +rake db:migrate VERBOSE=false+ will suppress all output.
+If you want Active Record to not output anything, then running +rake db:migrate
+VERBOSE=false+ will suppress all output.
h3. Using Models in Your Migrations
-When creating or updating data in a migration it is often tempting to use one of your models. After all, they exist to provide easy access to the underlying data. This can be done, but some caution should be observed.
+When creating or updating data in a migration it is often tempting to use one of
+your models. After all, they exist to provide easy access to the underlying
+data. This can be done, but some caution should be observed.
-For example, problems occur when the model uses database columns which are (1) not currently in the database and (2) will be created by this or a subsequent migration.
+For example, problems occur when the model uses database columns which are (1)
+not currently in the database and (2) will be created by this or a subsequent
+migration.
-Consider this example, where Alice and Bob are working on the same code base which contains a +Product+ model:
+Consider this example, where Alice and Bob are working on the same code base
+which contains a +Product+ model:
Bob goes on vacation.
-Alice creates a migration for the +products+ table which adds a new column and initializes it.
-She also adds a validation to the +Product+ model for the new column.
+Alice creates a migration for the +products+ table which adds a new column and
+initializes it. She also adds a validation to the +Product+ model for the new
+column.
<ruby>
# db/migrate/20100513121110_add_flag_to_product.rb
class AddFlagToProduct < ActiveRecord::Migration
def change
- add_column :products, :flag, :int
- Product.all.each { |f| f.update_attributes!(:flag => 'false') }
+ add_column :products, :flag, :boolean
+ Product.all.each do |product|
+ product.update_attributes!(:flag => 'false')
+ end
end
end
</ruby>
@@ -545,7 +742,9 @@ class Product < ActiveRecord::Base
end
</ruby>
-Alice adds a second migration which adds and initializes another column to the +products+ table and also adds a validation to the +Product+ model for the new column.
+Alice adds a second migration which adds and initializes another column to the
++products+ table and also adds a validation to the +Product+ model for the new
+column.
<ruby>
# db/migrate/20100515121110_add_fuzz_to_product.rb
@@ -553,7 +752,9 @@ Alice adds a second migration which adds and initializes another column to the +
class AddFuzzToProduct < ActiveRecord::Migration
def change
add_column :products, :fuzz, :string
- Product.all.each { |f| f.update_attributes! :fuzz => 'fuzzy' }
+ Product.all.each do |product|
+ product.update_attributes! :fuzz => 'fuzzy'
+ end
end
end
</ruby>
@@ -570,10 +771,14 @@ Both migrations work for Alice.
Bob comes back from vacation and:
-# updates the source - which contains both migrations and the latests version of the Product model.
-# runs outstanding migrations with +rake db:migrate+, which includes the one that updates the +Product+ model.
+# Updates the source - which contains both migrations and the latests version of
+the Product model.
+# Runs outstanding migrations with +rake db:migrate+, which
+includes the one that updates the +Product+ model.
-The migration crashes because when the model attempts to save, it tries to validate the second added column, which is not in the database when the _first_ migration runs:
+The migration crashes because when the model attempts to save, it tries to
+validate the second added column, which is not in the database when the _first_
+migration runs:
<plain>
rake aborted!
@@ -582,9 +787,12 @@ An error has occurred, this and all later migrations canceled:
undefined method `fuzz' for #<Product:0x000001049b14a0>
</plain>
-A fix for this is to create a local model within the migration. This keeps rails from running the validations, so that the migrations run to completion.
+A fix for this is to create a local model within the migration. This keeps rails
+from running the validations, so that the migrations run to completion.
-When using a faux model, it's a good idea to call +Product.reset_column_information+ to refresh the +ActiveRecord+ cache for the +Product+ model prior to updating data in the database.
+When using a faux model, it's a good idea to call
++Product.reset_column_information+ to refresh the +ActiveRecord+ cache for the
++Product+ model prior to updating data in the database.
If Alice had done this instead, there would have been no problem:
@@ -596,9 +804,11 @@ class AddFlagToProduct < ActiveRecord::Migration
end
def change
- add_column :products, :flag, :int
+ add_column :products, :flag, :integer
Product.reset_column_information
- Product.all.each { |f| f.update_attributes!(:flag => false) }
+ Product.all.each do |product|
+ product.update_attributes!(:flag => false)
+ end
end
end
</ruby>
@@ -609,32 +819,50 @@ end
class AddFuzzToProduct < ActiveRecord::Migration
class Product < ActiveRecord::Base
end
+
def change
add_column :products, :fuzz, :string
Product.reset_column_information
- Product.all.each { |f| f.update_attributes! :fuzz => 'fuzzy' }
+ Product.all.each do |product|
+ product.update_attributes!(:fuzz => 'fuzzy')
+ end
end
end
</ruby>
-
h3. Schema Dumping and You
h4. What are Schema Files for?
-Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either +db/schema.rb+ or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database.
+Migrations, mighty as they may be, are not the authoritative source for your
+database schema. That role falls to either +db/schema.rb+ or an SQL file which
+Active Record generates by examining the database. They are not designed to be
+edited, they just represent the current state of the database.
-There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema.
+There is no need (and it is error prone) to deploy a new instance of an app by
+replaying the entire migration history. It is much simpler and faster to just
+load into the database a description of the current schema.
-For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database.
+For example, this is how the test database is created: the current development
+database is dumped (either to +db/schema.rb+ or +db/structure.sql+) and then
+loaded into the test database.
-Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations, but is summed up in the schema file. The "annotate_models":https://github.com/ctran/annotate_models gem automatically adds and updates comments at the top of each model summarizing the schema if you desire that functionality.
+Schema files are also useful if you want a quick look at what attributes an
+Active Record object has. This information is not in the model's code and is
+frequently spread across several migrations, but the information is nicely
+summed up in the schema file. The
+"annotate_models":https://github.com/ctran/annotate_models gem automatically
+adds and updates comments at the top of each model summarizing the schema if
+you desire that functionality.
h4. Types of Schema Dumps
-There are two ways to dump the schema. This is set in +config/application.rb+ by the +config.active_record.schema_format+ setting, which may be either +:sql+ or +:ruby+.
+There are two ways to dump the schema. This is set in +config/application.rb+ by
+the +config.active_record.schema_format+ setting, which may be either +:sql+ or
++:ruby+.
-If +:ruby+ is selected then the schema is stored in +db/schema.rb+. If you look at this file you'll find that it looks an awful lot like one very big migration:
+If +:ruby+ is selected then the schema is stored in +db/schema.rb+. If you look
+at this file you'll find that it looks an awful lot like one very big migration:
<ruby>
ActiveRecord::Schema.define(:version => 20080906171750) do
@@ -646,28 +874,57 @@ ActiveRecord::Schema.define(:version => 20080906171750) do
create_table "products", :force => true do |t|
t.string "name"
- t.text "description"
+ t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "part_number"
+ t.string "part_number"
end
end
</ruby>
-In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using +create_table+, +add_index+, and so on. Because this is database-independent, it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases.
-
-There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers, or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this, then you should set the schema format to +:sql+.
-
-Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) into +db/#{Rails.env}_structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it.
+In many ways this is exactly what it is. This file is created by inspecting the
+database and expressing its structure using +create_table+, +add_index+, and so
+on. Because this is database-independent, it could be loaded into any database
+that Active Record supports. This could be very useful if you were to distribute
+an application that is able to run against multiple databases.
+
+There is however a trade-off: +db/schema.rb+ cannot express database specific
+items such as foreign key constraints, triggers, or stored procedures. While in
+a migration you can execute custom SQL statements, the schema dumper cannot
+reconstitute those statements from the database. If you are using features like
+this, then you should set the schema format to +:sql+.
+
+Instead of using Active Record's schema dumper, the database's structure will be
+dumped using a tool specific to the database (via the +db:structure:dump+ Rake task)
+into +db/structure.sql+. For example, for the PostgreSQL RDBMS, the
++pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW
+CREATE TABLE+ for the various tables. Loading these schemas is simply a question
+of executing the SQL statements they contain. By definition, this will create a
+perfect copy of the database's structure. Using the +:sql+ schema format will,
+however, prevent loading the schema into a RDBMS other than the one used to
+create it.
h4. Schema Dumps and Source Control
-Because schema dumps are the authoritative source for your database schema, it is strongly recommended that you check them into source control.
+Because schema dumps are the authoritative source for your database schema, it
+is strongly recommended that you check them into source control.
h3. Active Record and Referential Integrity
-The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.
-
-Validations such as +validates :foreign_key, :uniqueness => true+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level, these cannot guarantee referential integrity and so some people augment them with foreign key constraints.
-
-Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":https://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
+The Active Record way claims that intelligence belongs in your models, not in
+the database. As such, features such as triggers or foreign key constraints,
+which push some of that intelligence back into the database, are not heavily
+used.
+
+Validations such as +validates :foreign_key, :uniqueness => true+ are one way in
+which models can enforce data integrity. The +:dependent+ option on associations
+allows models to automatically destroy child objects when the parent is
+destroyed. Like anything which operates at the application level, these cannot
+guarantee referential integrity and so some people augment them with foreign key
+constraints in the database.
+
+Although Active Record does not provide any tools for working directly with such
+features, the +execute+ method can be used to execute arbitrary SQL. You could
+also use some plugin like "foreigner":https://github.com/matthuhiggins/foreigner
+which add foreign key support to Active Record (including support for dumping
+foreign keys in +db/schema.rb+).
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index f3ea7e38bc..958b13cd9e 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -151,7 +151,7 @@ Performance tests can be run in two modes: Benchmarking and Profiling.
h5. Benchmarking
-Benchmarking makes it easy to quickly gather a few metrics about each test tun. By default, each test case is run *4 times* in benchmarking mode.
+Benchmarking makes it easy to quickly gather a few metrics about each test run. By default, each test case is run *4 times* in benchmarking mode.
To run performance tests in benchmarking mode:
@@ -447,7 +447,7 @@ h4. Using Ruby-Prof on MRI and REE
Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile under MRI or REE:
<ruby>
-gem 'ruby-prof', :path => 'git://github.com/wycats/ruby-prof.git'
+gem 'ruby-prof', :git => 'git://github.com/wycats/ruby-prof.git'
</ruby>
Now run +bundle install+ and you're ready to go.
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index 5cfd336d1e..07fd95c825 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -30,17 +30,7 @@ Before you continue, take a moment to decide if your new plugin will be potentia
* If your plugin is specific to your application, your new plugin will be a _vendored plugin_.
* If you think your plugin may be used across applications, build it as a _gemified plugin_.
-h4. Either generate a vendored plugin...
-
-Use the +rails generate plugin+ command in your Rails root directory
- to create a new plugin that will live in the +vendor/plugins+
- directory. See usage and options by asking for help:
-
-<shell>
-$ rails generate plugin --help
-</shell>
-
-h4. Or generate a gemified plugin.
+h4. Generate a gemified plugin.
Writing your Rails plugin as a gem, rather than as a vendored plugin,
lets you share your plugin across different rails applications using
@@ -412,30 +402,6 @@ After running +bundle install+, your gem functionality will be available to the
When the gem is ready to be shared as a formal release, it can be published to "RubyGems":http://www.rubygems.org.
For more information about publishing gems to RubyGems, see: "http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html":http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html
-h3. Non-Gem Plugins
-
-Non-gem plugins are useful for functionality that won't be shared with another project. Keeping your custom functionality in the
-vendor/plugins directory un-clutters the rest of the application.
-
-Move the directory that you created for the gem based plugin into the vendor/plugins directory of a generated Rails application, create a vendor/plugins/yaffle/init.rb file that contains "require 'yaffle'" and everything will still work.
-
-<ruby>
-# yaffle/init.rb
-
-require 'yaffle'
-</ruby>
-
-You can test this by changing to the Rails application that you added the plugin to and starting a rails console. Once in the
-console we can check to see if the String has an instance method to_squawk:
-
-<shell>
-$ cd my_app
-$ rails console
-$ "Rails plugins are easy!".to_squawk
-</shell>
-
-You can also remove the .gemspec, Gemfile and Gemfile.lock files as they will no longer be needed.
-
h3. RDoc Documentation
Once your plugin is stable and you are ready to deploy do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy.
@@ -461,4 +427,3 @@ h4. References
* "Using Gemspecs As Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/
* "Gemspec Reference":http://docs.rubygems.org/read/chapter/20
* "GemPlugins":http://www.mbleigh.com/2008/06/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins
-* "Keeping init.rb thin":http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html
diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile
index 3b51a52733..f50ced3307 100644
--- a/railties/guides/source/rails_application_templates.textile
+++ b/railties/guides/source/rails_application_templates.textile
@@ -1,6 +1,6 @@
h2. Rails Application Templates
-Application templates are simple Ruby files containing DSL for adding plugins/gems/initializers etc. to your freshly created Rails project or an existing Rails project.
+Application templates are simple Ruby files containing DSL for adding gems/initializers etc. to your freshly created Rails project or an existing Rails project.
By referring to this guide, you will be able to:
@@ -82,31 +82,6 @@ For example, if you need to source a gem from "http://code.whytheluckystiff.net"
add_source "http://code.whytheluckystiff.net"
</ruby>
-h4. plugin(name, options = {})
-
-Installs a plugin to the generated application.
-
-Plugin can be installed from Git:
-
-<ruby>
-plugin 'authentication', :git => 'git://github.com/foor/bar.git'
-</ruby>
-
-You can even install plugins as git submodules:
-
-<ruby>
-plugin 'authentication', :git => 'git://github.com/foor/bar.git',
- :submodule => true
-</ruby>
-
-Please note that you need to +git :init+ before you can install a plugin as a submodule.
-
-Or use plain old SVN:
-
-<ruby>
-plugin 'usingsvn', :svn => 'svn://example.com/usingsvn/trunk'
-</ruby>
-
h4. vendor/lib/file/initializer(filename, data = nil, &block)
Adds an initializer to the generated application’s +config/initializers+ directory.
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index d6cbd84b1f..9526526bc7 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -95,6 +95,7 @@ use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
+use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index f281009fee..0823fb14e3 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -620,7 +620,7 @@ You can specify what Rails should route +"/"+ to with the +root+ method:
root :to => 'pages#main'
</ruby>
-You should put the +root+ route at the end of the file. You also need to delete the +public/index.html+ file for the root route to take effect.
+You should put the +root+ route at the top of the file, because it is the most popular route and should be matched first. You also need to delete the +public/index.html+ file for the root route to take effect.
h3. Customizing Resourceful Routes
@@ -655,7 +655,7 @@ You can use the +:constraints+ option to specify a required format on the implic
resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/}
</ruby>
-This declaration constraints the +:id+ parameter to match the supplied regular expression. So, in this case, the router would no longer match +/photos/1+ to this route. Instead, +/photos/RR27+ would match.
+This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, the router would no longer match +/photos/1+ to this route. Instead, +/photos/RR27+ would match.
You can specify a single constraint to apply to a number of routes by using the block form:
diff --git a/railties/guides/source/ruby_on_rails_guides_guidelines.textile b/railties/guides/source/ruby_on_rails_guides_guidelines.textile
index 29aefd25f8..f3c8fa654d 100644
--- a/railties/guides/source/ruby_on_rails_guides_guidelines.textile
+++ b/railties/guides/source/ruby_on_rails_guides_guidelines.textile
@@ -40,7 +40,9 @@ The guides and the API should be coherent where appropriate. Please have a look
Those guidelines apply also to guides.
-h3. HTML Generation
+h3. HTML Guides
+
+h4. Generation
To generate all the guides, just +cd+ into the +railties+ directory and execute:
@@ -68,7 +70,7 @@ If you want to generate guides in languages other than English, you can keep the
bundle exec rake generate_guides GUIDES_LANGUAGE=es
</plain>
-h3. HTML Validation
+h4. Validation
Please validate the generated HTML with:
@@ -77,3 +79,13 @@ bundle exec rake validate_guides
</plain>
Particularly, titles get an ID generated from their content and this often leads to duplicates. Please set +WARNINGS=1+ when generating guides to detect them. The warning messages suggest a way to fix them.
+
+h3. Kindle Guides
+
+h4. Generation
+
+To generate guides for the Kindle, you need to provide +KINDLE=1+ as an environment variable:
+
+<plain>
+KINDLE=1 bundle exec rake generate_guides
+</plain>
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index c2ef7bf9b5..b1a09c0c05 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -385,7 +385,7 @@ params[:user] # => {:name => “ow3ned”, :admin => true}
So if you create a new user using mass-assignment, it may be too easy to become an administrator.
-Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3+. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
+Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the <tt>attributes=</tt> method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in Rails 2.3<plus>. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example:
<ruby>
class Person < ActiveRecord::Base
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 2341a3522c..1e6b92f45c 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -114,25 +114,18 @@ Rails by default automatically loads all fixtures from the +test/fixtures+ folde
* Load the fixture data into the table
* Dump the fixture data into a variable in case you want to access it directly
-h5. Hashes with Special Powers
+h5. Fixtures are ActiveRecord objects
-Fixtures are basically Hash objects. As mentioned in point #3 above, you can access the hash object directly because it is automatically setup as a local variable of the test case. For example:
+Fixtures are instances of ActiveRecord. As mentioned in point #3 above, you can access the object directly because it is automatically setup as a local variable of the test case. For example:
<ruby>
-# this will return the Hash for the fixture named david
+# this will return the User object for the fixture named david
users(:david)
# this will return the property for david called id
users(:david).id
-</ruby>
-
-Fixtures can also transform themselves into the form of the original class. Thus, you can get at the methods only available to that class.
-
-<ruby>
-# using the find method, we grab the "real" david as a User
-david = users(:david).find
-# and now we have access to methods only available to a User class
+# one can also access methods available on the User class
email(david.girlfriend.email, david.location_tonight)
</ruby>
@@ -738,7 +731,6 @@ You don't need to set up and run your tests by hand on a test-by-test basis. Rai
|+rake test:benchmark+ |Benchmark the performance tests|
|+rake test:functionals+ |Runs all the functional tests from +test/functional+|
|+rake test:integration+ |Runs all the integration tests from +test/integration+|
-|+rake test:plugins+ |Run all the plugin tests from +vendor/plugins/*/**/test+ (or specify with +PLUGIN=_name_+)|
|+rake test:profile+ |Profile the performance tests|
|+rake test:recent+ |Tests recent changes|
|+rake test:uncommitted+ |Runs all the tests which are uncommitted. Supports Subversion and Git|
@@ -927,7 +919,7 @@ class UserControllerTest < ActionController::TestCase
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post :invite_friend, :email => 'friend@example.com'
end
- invite_email = ActionMailer::Base.deliveries.first
+ invite_email = ActionMailer::Base.deliveries.last
assert_equal "You have been invited by me@example.com", invite_email.subject
assert_equal 'friend@example.com', invite_email.to[0]
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 73bdd0b552..77a09507a8 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -5,27 +5,18 @@ require 'pathname'
require 'active_support'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/array/extract_options'
-require 'active_support/core_ext/logger'
require 'rails/application'
require 'rails/version'
+require 'rails/deprecation'
require 'active_support/railtie'
require 'action_dispatch/railtie'
-# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the
-# multibyte safe operations. Plugin authors supporting other encodings
-# should override this behavior and set the relevant +default_charset+
-# on ActionController::Base.
-#
# For Ruby 1.9, UTF-8 is the default internal and external encoding.
-if RUBY_VERSION < '1.9'
- $KCODE='u'
-else
- silence_warnings do
- Encoding.default_external = Encoding::UTF_8
- Encoding.default_internal = Encoding::UTF_8
- end
+silence_warnings do
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = Encoding::UTF_8
end
module Rails
@@ -87,7 +78,11 @@ module Rails
end
def cache
- RAILS_CACHE
+ @@cache ||= nil
+ end
+
+ def cache=(cache)
+ @@cache = cache
end
# Returns all rails groups for loading based on:
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 82fffe86bb..c163081bfc 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -1,7 +1,5 @@
require 'active_support/core_ext/hash/reverse_merge'
-require 'active_support/file_update_checker'
require 'fileutils'
-require 'rails/plugin'
require 'rails/engine'
module Rails
@@ -10,7 +8,7 @@ module Rails
#
# == Initialization
#
- # Rails::Application is responsible for executing all railties, engines and plugin
+ # Rails::Application is responsible for executing all railties and engines
# initializers. It also executes some bootstrap initializers (check
# Rails::Application::Bootstrap) and finishing initializers, after all the others
# are executed (check Rails::Application::Finisher).
@@ -20,7 +18,7 @@ module Rails
# Besides providing the same configuration as Rails::Engine and Rails::Railtie,
# the application object has several specific configurations, for example
# "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters",
- # "logger", "reload_plugins" and so forth.
+ # "logger" and so forth.
#
# Check Rails::Application::Configuration to see them all.
#
@@ -33,6 +31,25 @@ module Rails
#
# The Application is also responsible for building the middleware stack.
#
+ # == Booting process
+ #
+ # The application is also responsible for setting up and executing the booting
+ # process. From the moment you require "config/application.rb" in your app,
+ # the booting process goes like this:
+ #
+ # 1) require "config/boot.rb" to setup load paths
+ # 2) require railties and engines
+ # 3) Define Rails.application as "class MyApp::Application < Rails::Application"
+ # 4) Run config.before_configuration callbacks
+ # 5) Load config/environments/ENV.rb
+ # 6) Run config.before_initialize callbacks
+ # 7) Run Railtie#initializer defined by railties, engines and application.
+ # One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
+ # 9) Custom Railtie#initializers added by railties, engines and applications are executed
+ # 10) Build the middleware stack and run to_prepare callbacks
+ # 11) Run config.before_eager_load and eager_load if cache classes is true
+ # 12) Run config.after_initialize callbacks
+ #
class Application < Engine
autoload :Bootstrap, 'rails/application/bootstrap'
autoload :Configuration, 'rails/application/configuration'
@@ -52,12 +69,14 @@ module Rails
attr_accessor :assets, :sandbox
alias_method :sandbox?, :sandbox
+ attr_reader :reloaders
delegate :default_url_options, :default_url_options=, :to => :routes
def initialize
super
@initialized = false
+ @reloaders = []
end
# This method is called just after an application inherits from Rails::Application,
@@ -83,57 +102,97 @@ module Rails
require environment if environment
end
+ # Reload application routes regardless if they changed or not.
def reload_routes!
routes_reloader.reload!
end
- def routes_reloader
+ def routes_reloader #:nodoc:
@routes_reloader ||= RoutesReloader.new
end
- def initialize!(group=:default)
+ # Returns an array of file paths appended with a hash of directories-extensions
+ # suitable for ActiveSupport::FileUpdateChecker API.
+ def watchable_args
+ files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
+
+ ActiveSupport::Dependencies.autoload_paths.each do |path|
+ dirs[path.to_s] = [:rb]
+ end
+
+ [files, dirs]
+ end
+
+ # Initialize the application passing the given group. By default, the
+ # group is :default but sprockets precompilation passes group equals
+ # to assets if initialize_on_precompile is false to avoid booting the
+ # whole app.
+ def initialize!(group=:default) #:nodoc:
raise "Application has been already initialized." if @initialized
run_initializers(group, self)
@initialized = true
self
end
+ # Load the application and its railties tasks and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
def load_tasks(app=self)
initialize_tasks
super
self
end
+ # Load the application console and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.console</tt> for more info.
def load_console(app=self)
initialize_console
super
self
end
- # Rails.application.env_config stores some of the Rails initial environment parameters.
- # Currently stores:
- #
- # * action_dispatch.parameter_filter" => config.filter_parameters,
- # * action_dispatch.secret_token" => config.secret_token,
- # * action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions
- #
- # These parameters will be used by middlewares and engines to configure themselves.
- #
+ # Stores some of the Rails initial environment parameters which
+ # will be used by middlewares and engines to configure themselves.
def env_config
@env_config ||= super.merge({
"action_dispatch.parameter_filter" => config.filter_parameters,
"action_dispatch.secret_token" => config.secret_token,
- "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions
+ "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
+ "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
+ "action_dispatch.logger" => Rails.logger,
+ "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
})
end
- def initializers
+ # Returns the ordered railties for this application considering railties_order.
+ def ordered_railties #:nodoc:
+ @ordered_railties ||= begin
+ order = config.railties_order.map do |railtie|
+ if railtie == :main_app
+ self
+ elsif railtie.respond_to?(:instance)
+ railtie.instance
+ else
+ railtie
+ end
+ end
+
+ all = (railties.all - order)
+ all.push(self) unless all.include?(self)
+ order.push(:all) unless order.include?(:all)
+
+ index = order.index(:all)
+ order[index] = all
+ order.reverse.flatten
+ end
+ end
+
+ def initializers #:nodoc:
Bootstrap.initializers_for(self) +
super +
Finisher.initializers_for(self)
end
- def config
+ def config #:nodoc:
@config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
end
@@ -141,10 +200,23 @@ module Rails
self
end
+ def helpers_paths #:nodoc:
+ config.helpers_paths
+ end
+
+ def call(env)
+ env["ORIGINAL_FULLPATH"] = build_original_fullpath(env)
+ super(env)
+ end
+
protected
alias :build_middleware_stack :app
+ def reload_dependencies?
+ config.reload_classes_only_on_change != true || reloaders.map(&:updated?).any?
+ end
+
def default_middleware_stack
ActionDispatch::MiddlewareStack.new.tap do |middleware|
if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
@@ -166,16 +238,26 @@ module Rails
middleware.use ::Rack::MethodOverride
middleware.use ::ActionDispatch::RequestId
middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods
- middleware.use ::ActionDispatch::ShowExceptions, config.consider_all_requests_local
+ middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
+ middleware.use ::ActionDispatch::DebugExceptions
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
+
if config.action_dispatch.x_sendfile_header.present?
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
end
- middleware.use ::ActionDispatch::Reloader unless config.cache_classes
+
+ unless config.cache_classes
+ app = self
+ middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? }
+ end
+
middleware.use ::ActionDispatch::Callbacks
middleware.use ::ActionDispatch::Cookies
if config.session_store
+ if config.force_ssl && !config.session_options.key?(:secure)
+ config.session_options[:secure] = true
+ end
middleware.use config.session_store, config.session_options
middleware.use ::ActionDispatch::Flash
end
@@ -191,7 +273,7 @@ module Rails
end
end
- def initialize_tasks
+ def initialize_tasks #:nodoc:
self.class.rake_tasks do
require "rails/tasks"
task :environment do
@@ -201,10 +283,22 @@ module Rails
end
end
- def initialize_console
+ def initialize_console #:nodoc:
require "pp"
require "rails/console/app"
require "rails/console/helpers"
end
+
+ def build_original_fullpath(env)
+ path_info = env["PATH_INFO"]
+ query_string = env["QUERY_STRING"]
+ script_name = env["SCRIPT_NAME"]
+
+ if query_string.present?
+ "#{script_name}#{path_info}?#{query_string}"
+ else
+ "#{script_name}#{path_info}"
+ end
+ end
end
end
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index c2cb121e42..efc7dca0d4 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -24,40 +24,41 @@ module Rails
initializer :initialize_logger, :group => :all do
Rails.logger ||= config.logger || begin
path = config.paths["log"].first
- logger = ActiveSupport::TaggedLogging.new(ActiveSupport::BufferedLogger.new(path))
- logger.level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase)
- logger.auto_flushing = false if Rails.env.production?
+ unless File.exist? File.dirname path
+ FileUtils.mkdir_p File.dirname path
+ end
+
+ f = File.open path, 'a'
+ f.binmode
+ f.sync = true # make sure every write flushes
+
+ logger = ActiveSupport::TaggedLogging.new(
+ ActiveSupport::Logger.new(f)
+ )
+ logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase)
logger
rescue StandardError
- logger = ActiveSupport::TaggedLogging.new(ActiveSupport::BufferedLogger.new(STDERR))
- logger.level = ActiveSupport::BufferedLogger::WARN
+ logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
+ logger.level = ActiveSupport::Logger::WARN
logger.warn(
"Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " +
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
)
logger
end
- at_exit { Rails.logger.flush if Rails.logger.respond_to?(:flush) }
end
# Initialize cache early in the stack so railties can make use of it.
initializer :initialize_cache, :group => :all do
- unless defined?(RAILS_CACHE)
- silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(config.cache_store) }
+ unless Rails.cache
+ Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store)
- if RAILS_CACHE.respond_to?(:middleware)
- config.middleware.insert_before("Rack::Runtime", RAILS_CACHE.middleware)
+ if Rails.cache.respond_to?(:middleware)
+ config.middleware.insert_before("Rack::Runtime", Rails.cache.middleware)
end
end
end
- initializer :set_clear_dependencies_hook, :group => :all do
- ActionDispatch::Reloader.to_cleanup do
- ActiveSupport::DescendantsTracker.clear
- ActiveSupport::Dependencies.clear
- end
- end
-
# Sets the dependency loading mechanism.
# TODO: Remove files from the $" and always use require.
initializer :initialize_dependency_mechanism, :group => :all do
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 8f5b28faf8..825ea985fc 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -1,17 +1,17 @@
-require 'active_support/core_ext/string/encoding'
require 'active_support/core_ext/kernel/reporting'
+require 'active_support/file_update_checker'
require 'rails/engine/configuration'
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets,
- :cache_classes, :cache_store, :consider_all_requests_local,
- :dependency_loading, :filter_parameters,
+ :cache_classes, :cache_store, :consider_all_requests_local, :console,
+ :dependency_loading, :exceptions_app, :file_watcher, :filter_parameters,
:force_ssl, :helpers_paths, :logger, :log_tags, :preload_frameworks,
- :reload_plugins, :secret_token, :serve_static_assets,
- :ssl_options, :static_cache_control, :session_options,
- :time_zone, :whiny_nils
+ :railties_order, :relative_url_root, :secret_token,
+ :serve_static_assets, :ssl_options, :static_cache_control, :session_options,
+ :time_zone, :reload_classes_only_on_change
attr_writer :log_level
attr_reader :encoding
@@ -19,22 +19,27 @@ module Rails
def initialize(*)
super
self.encoding = "utf-8"
- @allow_concurrency = false
- @consider_all_requests_local = false
- @filter_parameters = []
- @helpers_paths = []
- @dependency_loading = true
- @serve_static_assets = true
- @static_cache_control = nil
- @force_ssl = false
- @ssl_options = {}
- @session_store = :cookie_store
- @session_options = {}
- @time_zone = "UTC"
- @log_level = nil
- @middleware = app_middleware
- @generators = app_generators
- @cache_store = [ :file_store, "#{root}/tmp/cache/" ]
+ @allow_concurrency = false
+ @consider_all_requests_local = false
+ @filter_parameters = []
+ @helpers_paths = []
+ @dependency_loading = true
+ @serve_static_assets = true
+ @static_cache_control = nil
+ @force_ssl = false
+ @ssl_options = {}
+ @session_store = :cookie_store
+ @session_options = {}
+ @time_zone = "UTC"
+ @log_level = nil
+ @middleware = app_middleware
+ @generators = app_generators
+ @cache_store = [ :file_store, "#{root}/tmp/cache/" ]
+ @railties_order = [:all]
+ @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
+ @reload_classes_only_on_change = true
+ @file_watcher = ActiveSupport::FileUpdateChecker
+ @exceptions_app = nil
@assets = ActiveSupport::OrderedOptions.new
@assets.enabled = false
@@ -51,6 +56,7 @@ module Rails
@assets.js_compressor = nil
@assets.css_compressor = nil
@assets.initialize_on_precompile = true
+ @assets.logger = nil
end
def compiled_asset_path
@@ -59,17 +65,9 @@ module Rails
def encoding=(value)
@encoding = value
- if "ruby".encoding_aware?
- silence_warnings do
- Encoding.default_external = value
- Encoding.default_internal = value
- end
- else
- $KCODE = value
- if $KCODE == "NONE"
- raise "The value you specified for config.encoding is " \
- "invalid. The possible values are UTF8, SJIS, or EUC"
- end
+ silence_warnings do
+ Encoding.default_external = value
+ Encoding.default_internal = value
end
end
@@ -93,10 +91,10 @@ module Rails
# after boot, and disables reloading code on every request, as these are
# fundamentally incompatible with thread safety.
def threadsafe!
- self.preload_frameworks = true
- self.cache_classes = true
- self.dependency_loading = false
- self.allow_concurrency = true
+ @preload_frameworks = true
+ @cache_classes = true
+ @dependency_loading = false
+ @allow_concurrency = true
self
end
@@ -113,11 +111,10 @@ module Rails
end
def colorize_logging
- @colorize_logging
+ ActiveSupport::LogSubscriber.colorize_logging
end
def colorize_logging=(val)
- @colorize_logging = val
ActiveSupport::LogSubscriber.colorize_logging = val
self.generators.colorize_logging = val
end
@@ -139,6 +136,11 @@ module Rails
@session_options = args.shift || {}
end
end
+
+ def whiny_nils=(*)
+ ActiveSupport::Deprecation.warn "config.whiny_nils option " \
+ "is deprecated and no longer works", caller
+ end
end
end
end
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 028c8814c4..7da495211d 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -19,12 +19,6 @@ module Rails
end
end
- initializer :add_to_prepare_blocks do
- config.to_prepare_blocks.each do |block|
- ActionDispatch::Reloader.to_prepare(&block)
- end
- end
-
initializer :add_builtin_route do |app|
if Rails.env.development?
app.routes.append do
@@ -37,32 +31,60 @@ module Rails
build_middleware_stack
end
- initializer :run_prepare_callbacks do
- ActionDispatch::Reloader.prepare!
- end
-
initializer :define_main_app_helper do |app|
app.routes.define_mounted_helper(:main_app)
end
+ initializer :add_to_prepare_blocks do
+ config.to_prepare_blocks.each do |block|
+ ActionDispatch::Reloader.to_prepare(&block)
+ end
+ end
+
+ # This needs to happen before eager load so it happens
+ # in exactly the same point regardless of config.cache_classes
+ initializer :run_prepare_callbacks do
+ ActionDispatch::Reloader.prepare!
+ end
+
initializer :eager_load! do
- if config.cache_classes && !$rails_rake_task
+ if config.cache_classes && !(defined?($rails_rake_task) && $rails_rake_task)
ActiveSupport.run_load_hooks(:before_eager_load, self)
eager_load!
end
end
+ # All initialization is done, including eager loading in production
initializer :finisher_hook do
ActiveSupport.run_load_hooks(:after_initialize, self)
end
- # Force routes to be loaded just at the end and add it to to_prepare callbacks
- # This needs to be after the finisher hook to ensure routes added in the hook
- # are still loaded.
- initializer :set_routes_reloader do |app|
- reloader = lambda { app.routes_reloader.execute_if_updated }
- reloader.call
- ActionDispatch::Reloader.to_prepare(&reloader)
+ # Set app reload just after the finisher hook to ensure
+ # routes added in the hook are still loaded.
+ initializer :set_routes_reloader_hook do
+ reloader = routes_reloader
+ reloader.execute_if_updated
+ self.reloaders << reloader
+ ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
+ end
+
+ # Set app reload just after the finisher hook to ensure
+ # paths added in the hook are still loaded.
+ initializer :set_clear_dependencies_hook, :group => :all do
+ callback = lambda do
+ ActiveSupport::DescendantsTracker.clear
+ ActiveSupport::Dependencies.clear
+ end
+
+ if config.reload_classes_only_on_change
+ reloader = config.file_watcher.new(*watchable_args, &callback)
+ self.reloaders << reloader
+ # We need to set a to_prepare callback regardless of the reloader result, i.e.
+ # models should be reloaded if any of the reloaders (i18n, routes) were updated.
+ ActionDispatch::Reloader.to_prepare(:prepend => true){ reloader.execute }
+ else
+ ActionDispatch::Reloader.to_cleanup(&callback)
+ end
end
# Disable dependency loading during request cycle
diff --git a/railties/lib/rails/application/railties.rb b/railties/lib/rails/application/railties.rb
index 8f3a3e8bbb..f20a9689de 100644
--- a/railties/lib/rails/application/railties.rb
+++ b/railties/lib/rails/application/railties.rb
@@ -4,7 +4,7 @@ module Rails
class Application < Engine
class Railties < Rails::Engine::Railties
def all(&block)
- @all ||= railties + engines + plugins
+ @all ||= railties + engines
@all.each(&block) if block
@all
end
diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/route_inspector.rb
index 8252f21aa7..2ca0c68243 100644
--- a/railties/lib/rails/application/route_inspector.rb
+++ b/railties/lib/rails/application/route_inspector.rb
@@ -1,36 +1,113 @@
+require 'delegate'
+
module Rails
class Application
+ class RouteWrapper < SimpleDelegator
+ def endpoint
+ rack_app ? rack_app.inspect : "#{controller}##{action}"
+ end
+
+ def constraints
+ requirements.except(:controller, :action)
+ end
+
+ def rack_app(app = self.app)
+ @rack_app ||= begin
+ class_name = app.class.name.to_s
+ if class_name == "ActionDispatch::Routing::Mapper::Constraints"
+ rack_app(app.app)
+ elsif class_name !~ /^ActionDispatch::Routing/
+ app
+ end
+ end
+ end
+
+ def verb
+ super.source.gsub(/[$^]/, '')
+ end
+
+ def path
+ super.spec.to_s
+ end
+
+ def name
+ super.to_s
+ end
+
+ def reqs
+ @reqs ||= begin
+ reqs = endpoint
+ reqs += " #{constraints.inspect}" unless constraints.empty?
+ reqs
+ end
+ end
+
+ def controller
+ requirements[:controller] || ':controller'
+ end
+
+ def action
+ requirements[:action] || ':action'
+ end
+
+ def internal?
+ path =~ %r{/rails/info/properties|^#{Rails.application.config.assets.prefix}}
+ end
+
+ def engine?
+ rack_app && rack_app.respond_to?(:routes)
+ end
+ end
+
##
# This class is just used for displaying route information when someone
# executes `rake routes`. People should not use this class.
class RouteInspector # :nodoc:
+ def initialize
+ @engines = ActiveSupport::OrderedHash.new
+ end
+
def format all_routes, filter = nil
if filter
all_routes = all_routes.select{ |route| route.defaults[:controller] == filter }
end
- routes = all_routes.collect do |route|
- route_reqs = route.requirements
-
- rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
+ routes = collect_routes(all_routes)
- controller = route_reqs[:controller] || ':controller'
- action = route_reqs[:action] || ':action'
+ formatted_routes(routes) +
+ formatted_routes_for_engines
+ end
- endpoint = rack_app ? rack_app.inspect : "#{controller}##{action}"
- constraints = route_reqs.except(:controller, :action)
+ def collect_routes(routes)
+ routes = routes.collect do |route|
+ RouteWrapper.new(route)
+ end.reject do |route|
+ route.internal?
+ end.collect do |route|
+ collect_engine_routes(route)
- reqs = endpoint
- reqs += " #{constraints.inspect}" unless constraints.empty?
+ {:name => route.name, :verb => route.verb, :path => route.path, :reqs => route.reqs }
+ end
+ end
- verb = route.verb.source.gsub(/[$^]/, '')
+ def collect_engine_routes(route)
+ name = route.endpoint
+ return unless route.engine?
+ return if @engines[name]
- {:name => route.name.to_s, :verb => verb, :path => route.path.spec.to_s, :reqs => reqs}
+ routes = route.rack_app.routes
+ if routes.is_a?(ActionDispatch::Routing::RouteSet)
+ @engines[name] = collect_routes(routes.routes)
end
+ end
- # Skip the route if it's internal info route
- routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} }
+ def formatted_routes_for_engines
+ @engines.map do |name, routes|
+ ["\nRoutes for #{name}:"] + formatted_routes(routes)
+ end.flatten
+ end
+ def formatted_routes(routes)
name_width = routes.map{ |r| r[:name].length }.max
verb_width = routes.map{ |r| r[:verb].length }.max
path_width = routes.map{ |r| r[:path].length }.max
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index 1d1f5e1b06..6f9a200aa9 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -1,10 +1,13 @@
+require "active_support/core_ext/module/delegation"
+
module Rails
class Application
- class RoutesReloader < ::ActiveSupport::FileUpdateChecker
- attr_reader :route_sets
+ class RoutesReloader
+ attr_reader :route_sets, :paths
+ delegate :execute_if_updated, :execute, :updated?, :to => :updater
def initialize
- super([]) { reload! }
+ @paths = []
@route_sets = []
end
@@ -16,7 +19,15 @@ module Rails
revert
end
- protected
+ private
+
+ def updater
+ @updater ||= begin
+ updater = ActiveSupport::FileUpdateChecker.new(paths) { reload! }
+ updater.execute
+ updater
+ end
+ end
def clear!
route_sets.each do |routes|
@@ -31,7 +42,7 @@ module Rails
def finalize!
route_sets.each do |routes|
- ActiveSupport.on_load(:action_controller) { routes.finalize! }
+ routes.finalize!
end
end
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index 36fd9aea19..cc26db849d 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -26,17 +26,4 @@ module Rails
add_filter { |line| line.sub(gems_regexp, '\2 (\3) \4') }
end
end
-
- # For installing the BacktraceCleaner in the test/unit
- module BacktraceFilterForTestUnit #:nodoc:
- def self.included(klass)
- klass.send :alias_method_chain, :filter_backtrace, :cleaning
- end
-
- def filter_backtrace_with_cleaning(backtrace, prefix=nil)
- backtrace = filter_backtrace_without_cleaning(backtrace, prefix)
- backtrace = backtrace.first.split("\n") if backtrace.size == 1
- Rails.backtrace_cleaner.clean(backtrace)
- end
- end
end
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
index e6822b75b7..03538b2422 100644
--- a/railties/lib/rails/code_statistics.rb
+++ b/railties/lib/rails/code_statistics.rb
@@ -37,13 +37,26 @@ class CodeStatistics #:nodoc:
next unless file_name =~ pattern
- f = File.open(directory + "/" + file_name)
-
- while line = f.gets
- stats["lines"] += 1
- stats["classes"] += 1 if line =~ /class [A-Z]/
- stats["methods"] += 1 if line =~ /def [a-z]/
- stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
+ comment_started = false
+
+ File.open(directory + "/" + file_name) do |f|
+ while line = f.gets
+ stats["lines"] += 1
+ if(comment_started)
+ if line =~ /^=end/
+ comment_started = false
+ end
+ next
+ else
+ if line =~ /^=begin/
+ comment_started = true
+ next
+ end
+ end
+ stats["classes"] += 1 if line =~ /^\s*class\s+[_A-Z]/
+ stats["methods"] += 1 if line =~ /^\s*def\s+[_a-z]/
+ stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
+ end
end
end
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index ada150ceec..71fe604e69 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -91,7 +91,7 @@ In addition to those, there are:
destroy Undo code generated with "generate" (short-cut alias: "d")
benchmarker See how fast a piece of code runs
profiler Get profile information from a piece of code
- plugin Install a plugin
+ plugin new Generates skeleton for developing a Rails plugin
runner Run a piece of code in the application environment (short-cut alias: "r")
All commands can be run with -h (or --help) for more information.
diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb
index 6c52d0f70f..b745b45e17 100644
--- a/railties/lib/rails/commands/benchmarker.rb
+++ b/railties/lib/rails/commands/benchmarker.rb
@@ -21,7 +21,7 @@ def options
options
end
-class BenchmarkerTest < ActionDispatch::PerformanceTest
+class BenchmarkerTest < ActionDispatch::PerformanceTest #:nodoc:
self.profile_options = options
ARGV.each do |expression|
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
index 32e361d421..86376ac7e6 100644
--- a/railties/lib/rails/commands/console.rb
+++ b/railties/lib/rails/commands/console.rb
@@ -4,45 +4,68 @@ require 'irb/completion'
module Rails
class Console
- def self.start(app)
- new(app).start
- end
+ attr_reader :options, :app, :console, :arguments
- def initialize(app)
- @app = app
+ def self.start(*args)
+ new(*args).start
end
- def start
- options = {}
-
- OptionParser.new do |opt|
- opt.banner = "Usage: console [environment] [options]"
- opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
- opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
- opt.on('--irb', "DEPRECATED: Invoke `/your/choice/of/ruby script/rails console` instead") { |v| abort '--irb option is no longer supported. Invoke `/your/choice/of/ruby script/rails console` instead' }
- opt.parse!(ARGV)
- end
+ def initialize(app, arguments = ARGV)
+ @app = app
+ @arguments = arguments
+ app.load_console
+ @console = app.config.console || IRB
+ end
- @app.sandbox = options[:sandbox]
- @app.load_console
+ def options
+ @options ||= begin
+ options = {}
- if options[:debugger]
- begin
- require 'ruby-debug'
- puts "=> Debugger enabled"
- rescue Exception
- puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
- exit
+ OptionParser.new do |opt|
+ opt.banner = "Usage: console [environment] [options]"
+ opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
+ opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
+ opt.parse!(arguments)
end
+
+ options
end
+ end
+
+ def sandbox?
+ options[:sandbox]
+ end
+
+ def debugger?
+ options[:debugger]
+ end
+
+ def start
+ app.sandbox = sandbox?
- if options[:sandbox]
+ require_debugger if debugger?
+
+ if sandbox?
puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})"
puts "Any modifications you make will be rolled back on exit"
else
puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
end
- IRB.start
+
+ if defined?(console::ExtendCommandBundle)
+ console::ExtendCommandBundle.send :include, Rails::ConsoleMethods
+ end
+ console.start
+ end
+
+ def require_debugger
+ begin
+ require 'ruby-debug'
+ puts "=> Debugger enabled"
+ rescue Exception
+ puts "You need to install ruby-debug19 to run the console in debugging mode. With gems, use 'gem install ruby-debug19'"
+ exit
+ end
end
end
end
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index b0ba76217a..6fc127efae 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -1,10 +1,4 @@
require 'erb'
-
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
require 'optparse'
require 'rbconfig'
@@ -33,7 +27,7 @@ module Rails
options['mode'] = mode
end
- opt.on("-h", "--header") do |h|
+ opt.on("--header") do |h|
options['header'] = h
end
@@ -41,7 +35,7 @@ module Rails
abort opt.to_s unless (0..1).include?(ARGV.size)
end
- unless config = YAML::load(ERB.new(IO.read("#{@app.root}/config/database.yml")).result)[Rails.env]
+ unless config = @app.config.database_configuration[Rails.env]
abort "No database is configured for the environment '#{Rails.env}'"
end
diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb
deleted file mode 100644
index 4df849447d..0000000000
--- a/railties/lib/rails/commands/plugin.rb
+++ /dev/null
@@ -1,542 +0,0 @@
-# Rails Plugin Manager.
-#
-# Installing plugins:
-#
-# $ rails plugin install continuous_builder asset_timestamping
-#
-# Specifying revisions:
-#
-# * Subversion revision is a single integer.
-#
-# * Git revision format:
-# - full - 'refs/tags/1.8.0' or 'refs/heads/experimental'
-# - short: 'experimental' (equivalent to 'refs/heads/experimental')
-# 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0')
-#
-#
-# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com)
-# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php)
-
-$verbose = false
-
-require 'open-uri'
-require 'fileutils'
-require 'tempfile'
-
-include FileUtils
-
-class RailsEnvironment
- attr_reader :root
-
- def initialize(dir)
- @root = dir
- end
-
- def self.find(dir=nil)
- dir ||= pwd
- while dir.length > 1
- return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb'))
- dir = File.dirname(dir)
- end
- end
-
- def self.default
- @default ||= find
- end
-
- def self.default=(rails_env)
- @default = rails_env
- end
-
- def install(name_uri_or_plugin)
- if name_uri_or_plugin.is_a? String
- if name_uri_or_plugin =~ /:\/\//
- plugin = Plugin.new(name_uri_or_plugin)
- else
- plugin = Plugins[name_uri_or_plugin]
- end
- else
- plugin = name_uri_or_plugin
- end
- if plugin
- plugin.install
- else
- puts "Plugin not found: #{name_uri_or_plugin}"
- end
- end
-
- def use_svn?
- require 'active_support/core_ext/kernel'
- silence_stderr {`svn --version` rescue nil}
- !$?.nil? && $?.success?
- end
-
- def use_externals?
- use_svn? && File.directory?("#{root}/vendor/plugins/.svn")
- end
-
- def use_checkout?
- # this is a bit of a guess. we assume that if the rails environment
- # is under subversion then they probably want the plugin checked out
- # instead of exported. This can be overridden on the command line
- File.directory?("#{root}/.svn")
- end
-
- def best_install_method
- return :http unless use_svn?
- case
- when use_externals? then :externals
- when use_checkout? then :checkout
- else :export
- end
- end
-
- def externals
- return [] unless use_externals?
- ext = `svn propget svn:externals "#{root}/vendor/plugins"`
- lines = ext.respond_to?(:lines) ? ext.lines : ext
- lines.reject{ |line| line.strip == '' }.map do |line|
- line.strip.split(/\s+/, 2)
- end
- end
-
- def externals=(items)
- unless items.is_a? String
- items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n")
- end
- Tempfile.open("svn-set-prop") do |file|
- file.write(items)
- file.flush
- system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"")
- end
- end
-end
-
-class Plugin
- attr_reader :name, :uri
-
- def initialize(uri, name = nil)
- @uri = uri
- guess_name(uri)
- end
-
- def self.find(name)
- new(name)
- end
-
- def to_s
- "#{@name.ljust(30)}#{@uri}"
- end
-
- def svn_url?
- @uri =~ /svn(?:\+ssh)?:\/\/*/
- end
-
- def git_url?
- @uri =~ /^git:\/\// || @uri =~ /\.git$/
- end
-
- def installed?
- File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \
- or rails_env.externals.detect{ |name, repo| self.uri == repo }
- end
-
- def install(method=nil, options = {})
- method ||= rails_env.best_install_method?
- if :http == method
- method = :export if svn_url?
- method = :git if git_url?
- end
-
- uninstall if installed? and options[:force]
-
- unless installed?
- send("install_using_#{method}", options)
- run_install_hook
- else
- puts "already installed: #{name} (#{uri}). pass --force to reinstall"
- end
- end
-
- def uninstall
- path = "#{rails_env.root}/vendor/plugins/#{name}"
- if File.directory?(path)
- puts "Removing 'vendor/plugins/#{name}'" if $verbose
- run_uninstall_hook
- rm_r path
- else
- puts "Plugin doesn't exist: #{path}"
- end
-
- if rails_env.use_externals?
- # clean up svn:externals
- externals = rails_env.externals
- externals.reject!{|n, u| name == n or name == u}
- rails_env.externals = externals
- end
- end
-
- def info
- tmp = "#{rails_env.root}/_tmp_about.yml"
- if svn_url?
- cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\""
- puts cmd if $verbose
- system(cmd)
- end
- open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream|
- stream.read
- end rescue "No about.yml found in #{uri}"
- ensure
- FileUtils.rm_rf tmp if svn_url?
- end
-
- private
-
- def run_install_hook
- install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb"
- load install_hook_file if File.exist? install_hook_file
- end
-
- def run_uninstall_hook
- uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb"
- load uninstall_hook_file if File.exist? uninstall_hook_file
- end
-
- def install_using_export(options = {})
- svn_command :export, options
- end
-
- def install_using_checkout(options = {})
- svn_command :checkout, options
- end
-
- def install_using_externals(options = {})
- externals = rails_env.externals
- externals.push([@name, uri])
- rails_env.externals = externals
- install_using_checkout(options)
- end
-
- def install_using_http(options = {})
- root = rails_env.root
- mkdir_p "#{root}/vendor/plugins/#{@name}"
- Dir.chdir "#{root}/vendor/plugins/#{@name}" do
- puts "fetching from '#{uri}'" if $verbose
- fetcher = RecursiveHTTPFetcher.new(uri, -1)
- fetcher.quiet = true if options[:quiet]
- fetcher.fetch
- end
- end
-
- def install_using_git(options = {})
- root = rails_env.root
- mkdir_p(install_path = "#{root}/vendor/plugins/#{name}")
- Dir.chdir install_path do
- init_cmd = "git init"
- init_cmd += " -q" if options[:quiet] and not $verbose
- puts init_cmd if $verbose
- system(init_cmd)
- base_cmd = "git pull --depth 1 #{uri}"
- base_cmd += " -q" if options[:quiet] and not $verbose
- base_cmd += " #{options[:revision]}" if options[:revision]
- puts base_cmd if $verbose
- if system(base_cmd)
- puts "removing: .git .gitignore" if $verbose
- rm_rf %w(.git .gitignore)
- else
- rm_rf install_path
- end
- end
- end
-
- def svn_command(cmd, options = {})
- root = rails_env.root
- mkdir_p "#{root}/vendor/plugins"
- base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\""
- base_cmd += ' -q' if options[:quiet] and not $verbose
- base_cmd += " -r #{options[:revision]}" if options[:revision]
- puts base_cmd if $verbose
- system(base_cmd)
- end
-
- def guess_name(url)
- @name = File.basename(url)
- if @name == 'trunk' || @name.empty?
- @name = File.basename(File.dirname(url))
- end
- @name.gsub!(/\.git$/, '') if @name =~ /\.git$/
- end
-
- def rails_env
- @rails_env || RailsEnvironment.default
- end
-end
-
-# load default environment and parse arguments
-require 'optparse'
-module Commands
- class Plugin
- attr_reader :environment, :script_name
- def initialize
- @environment = RailsEnvironment.default
- @rails_root = RailsEnvironment.default.root
- @script_name = File.basename($0)
- end
-
- def environment=(value)
- @environment = value
- RailsEnvironment.default = value
- end
-
- def options
- OptionParser.new do |o|
- o.set_summary_indent(' ')
- o.banner = "Usage: plugin [OPTIONS] command"
- o.define_head "Rails plugin manager."
-
- o.separator ""
- o.separator "GENERAL OPTIONS"
-
- o.on("-r", "--root=DIR", String,
- "Set an explicit rails app directory.",
- "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) }
-
- o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose }
- o.on("-h", "--help", "Show this help message.") { puts o; exit }
-
- o.separator ""
- o.separator "COMMANDS"
-
- o.separator " install Install plugin(s) from known repositories or URLs."
- o.separator " remove Uninstall plugins."
-
- o.separator ""
- o.separator "EXAMPLES"
- o.separator " Install a plugin from a subversion URL:"
- o.separator " #{@script_name} plugin install http://example.com/my_svn_plugin\n"
- o.separator " Install a plugin from a git URL:"
- o.separator " #{@script_name} plugin install git://github.com/SomeGuy/my_awesome_plugin.git\n"
- o.separator " Install a plugin and add a svn:externals entry to vendor/plugins"
- o.separator " #{@script_name} plugin install -x my_svn_plugin\n"
- end
- end
-
- def parse!(args=ARGV)
- general, sub = split_args(args)
- options.parse!(general)
-
- command = general.shift
- if command =~ /^(install|remove)$/
- command = Commands.const_get(command.capitalize).new(self)
- command.parse!(sub)
- else
- puts "Unknown command: #{command}" unless command.blank?
- puts options
- exit 1
- end
- end
-
- def split_args(args)
- left = []
- left << args.shift while args[0] and args[0] =~ /^-/
- left << args.shift if args[0]
- [left, args]
- end
-
- def self.parse!(args=ARGV)
- Plugin.new.parse!(args)
- end
- end
-
- class Install
- def initialize(base_command)
- @base_command = base_command
- @method = :http
- @options = { :quiet => false, :revision => nil, :force => false }
- end
-
- def options
- OptionParser.new do |o|
- o.set_summary_indent(' ')
- o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]"
- o.define_head "Install one or more plugins."
- o.separator ""
- o.separator "Options:"
- o.on( "-x", "--externals",
- "Use svn:externals to grab the plugin.",
- "Enables plugin updates and plugin versioning.") { |v| @method = :externals }
- o.on( "-o", "--checkout",
- "Use svn checkout to grab the plugin.",
- "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout }
- o.on( "-e", "--export",
- "Use svn export to grab the plugin.",
- "Exports the plugin, allowing you to check it into your local repository. Does not enable updates or add an svn:externals entry.") { |v| @method = :export }
- o.on( "-q", "--quiet",
- "Suppresses the output from installation.",
- "Ignored if -v is passed (rails plugin -v install ...)") { |v| @options[:quiet] = true }
- o.on( "-r REVISION", "--revision REVISION",
- "Checks out the given revision from subversion or git.",
- "Ignored if subversion/git is not used.") { |v| @options[:revision] = v }
- o.on( "-f", "--force",
- "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true }
- o.separator ""
- o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to "
- o.separator "a plugin repository."
- end
- end
-
- def determine_install_method
- best = @base_command.environment.best_install_method
- @method = :http if best == :http and @method == :export
- case
- when (best == :http and @method != :http)
- msg = "Cannot install using subversion because `svn' cannot be found in your PATH"
- when (best == :export and (@method != :export and @method != :http))
- msg = "Cannot install using #{@method} because this project is not under subversion."
- when (best != :externals and @method == :externals)
- msg = "Cannot install using externals because vendor/plugins is not under subversion."
- end
- if msg
- puts msg
- exit 1
- end
- @method
- end
-
- def parse!(args)
- options.parse!(args)
- if args.blank?
- puts options
- exit 1
- end
- environment = @base_command.environment
- install_method = determine_install_method
- puts "Plugins will be installed using #{install_method}" if $verbose
- args.each do |name|
- ::Plugin.find(name).install(install_method, @options)
- end
- rescue StandardError => e
- puts "Plugin not found: #{args.inspect}"
- puts e.inspect if $verbose
- exit 1
- end
- end
-
- class Remove
- def initialize(base_command)
- @base_command = base_command
- end
-
- def options
- OptionParser.new do |o|
- o.set_summary_indent(' ')
- o.banner = "Usage: #{@base_command.script_name} remove name [name]..."
- o.define_head "Remove plugins."
- end
- end
-
- def parse!(args)
- options.parse!(args)
- if args.blank?
- puts options
- exit 1
- end
- root = @base_command.environment.root
- args.each do |name|
- ::Plugin.new(name).uninstall
- end
- end
- end
-
- class Info
- def initialize(base_command)
- @base_command = base_command
- end
-
- def options
- OptionParser.new do |o|
- o.set_summary_indent(' ')
- o.banner = "Usage: #{@base_command.script_name} info name [name]..."
- o.define_head "Shows plugin info at {url}/about.yml."
- end
- end
-
- def parse!(args)
- options.parse!(args)
- args.each do |name|
- puts ::Plugin.find(name).info
- puts
- end
- end
- end
-end
-
-class RecursiveHTTPFetcher
- attr_accessor :quiet
- def initialize(urls_to_fetch, level = 1, cwd = ".")
- @level = level
- @cwd = cwd
- @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a
- @quiet = false
- end
-
- def ls
- @urls_to_fetch.collect do |url|
- if url =~ /^svn(\+ssh)?:\/\/.*/
- `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil
- else
- open(url) do |stream|
- links("", stream.read)
- end rescue nil
- end
- end.flatten
- end
-
- def push_d(dir)
- @cwd = File.join(@cwd, dir)
- FileUtils.mkdir_p(@cwd)
- end
-
- def pop_d
- @cwd = File.dirname(@cwd)
- end
-
- def links(base_url, contents)
- links = []
- contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link|
- link = link.sub(/href="/i, "")
- next if link =~ /svnindex.xsl$/
- next if link =~ /^(\w*:|)\/\// || link =~ /^\./
- links << File.join(base_url, link)
- end
- links
- end
-
- def download(link)
- puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet
- open(link) do |stream|
- File.open(File.join(@cwd, File.basename(link)), "wb") do |file|
- file.write(stream.read)
- end
- end
- end
-
- def fetch(links = @urls_to_fetch)
- links.each do |l|
- (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l)
- end
- end
-
- def fetch_dir(url)
- @level += 1
- push_d(File.basename(url)) if @level > 0
- open(url) do |stream|
- contents = stream.read
- fetch(links(url, contents))
- end
- pop_d if @level > 0
- @level -= 1
- end
-end
-
-Commands::Plugin.parse!
diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb
index ea6347c918..3f6966b4f0 100644
--- a/railties/lib/rails/commands/profiler.rb
+++ b/railties/lib/rails/commands/profiler.rb
@@ -19,7 +19,7 @@ def options
options
end
-class ProfilerTest < ActionDispatch::PerformanceTest
+class ProfilerTest < ActionDispatch::PerformanceTest #:nodoc:
self.profile_options = options
ARGV.each do |expression|
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index 20484a10c8..0b757cbe28 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -67,6 +67,13 @@ module Rails
FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make))
end
+ unless options[:daemonize]
+ wrapped_app # touch the app so the logger is set up
+
+ console = ActiveSupport::Logger.new($stdout)
+ Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
+ end
+
super
ensure
# The '-h' option calls exit before @options is set.
@@ -76,7 +83,6 @@ module Rails
def middleware
middlewares = []
- middlewares << [Rails::Rack::LogTailer, log_path] unless options[:daemonize]
middlewares << [Rails::Rack::Debugger] if options[:debugger]
middlewares << [::Rack::ContentLength]
Hash.new(middlewares)
@@ -89,6 +95,7 @@ module Rails
def default_options
super.merge({
:Port => 3000,
+ :DoNotReverseLookup => true,
:environment => (ENV['RAILS_ENV'] || "development").dup,
:daemonize => false,
:debugger => false,
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index 95c74baae2..ee8bb55f38 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -3,30 +3,33 @@ require 'active_support/test_case'
require 'action_controller'
# work around the at_exit hook in test/unit, which kills IRB
-Test::Unit.run = true if Test::Unit.respond_to?(:run=)
-# reference the global "app" instance, created on demand. To recreate the
-# instance, pass a non-false value as the parameter.
-def app(create=false)
- @app_integration_instance = nil if create
- @app_integration_instance ||= new_session do |sess|
- sess.host! "www.example.com"
- end
-end
+module Rails
+ module ConsoleMethods
+ # reference the global "app" instance, created on demand. To recreate the
+ # instance, pass a non-false value as the parameter.
+ def app(create=false)
+ @app_integration_instance = nil if create
+ @app_integration_instance ||= new_session do |sess|
+ sess.host! "www.example.com"
+ end
+ end
-# create a new session. If a block is given, the new session will be yielded
-# to the block before being returned.
-def new_session
- app = Rails.application
- session = ActionDispatch::Integration::Session.new(app)
- yield session if block_given?
- session
-end
+ # create a new session. If a block is given, the new session will be yielded
+ # to the block before being returned.
+ def new_session
+ app = Rails.application
+ session = ActionDispatch::Integration::Session.new(app)
+ yield session if block_given?
+ session
+ end
-# reloads the environment
-def reload!(print=true)
- puts "Reloading..." if print
- ActionDispatch::Reloader.cleanup!
- ActionDispatch::Reloader.prepare!
- true
+ # reloads the environment
+ def reload!(print=true)
+ puts "Reloading..." if print
+ ActionDispatch::Reloader.cleanup!
+ ActionDispatch::Reloader.prepare!
+ true
+ end
+ end
end
diff --git a/railties/lib/rails/console/helpers.rb b/railties/lib/rails/console/helpers.rb
index 212fc6125a..230d3d9d04 100644
--- a/railties/lib/rails/console/helpers.rb
+++ b/railties/lib/rails/console/helpers.rb
@@ -1,7 +1,11 @@
-def helper
- @helper ||= ApplicationController.helpers
-end
+module Rails
+ module ConsoleMethods
+ def helper
+ @helper ||= ApplicationController.helpers
+ end
-def controller
- @controller ||= ApplicationController.new
+ def controller
+ @controller ||= ApplicationController.new
+ end
+ end
end
diff --git a/railties/lib/rails/deprecation.rb b/railties/lib/rails/deprecation.rb
new file mode 100644
index 0000000000..c5811b2629
--- /dev/null
+++ b/railties/lib/rails/deprecation.rb
@@ -0,0 +1,18 @@
+require 'active_support/deprecation/proxy_wrappers'
+
+module Rails
+ class DeprecatedConstant < ActiveSupport::Deprecation::DeprecatedConstantProxy
+ def self.deprecate(old, current)
+ constant = new(old, current)
+ eval "::#{old} = constant"
+ end
+
+ private
+
+ def target
+ ::Kernel.eval @new_const.to_s
+ end
+ end
+
+ DeprecatedConstant.deprecate('RAILS_CACHE', '::Rails.cache')
+end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 1c9627734e..77a68eb7f1 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -228,7 +228,7 @@ module Rails
# resources :articles
# end
#
- # The routes above will automatically point to <tt>MyEngine::ApplicationContoller</tt>. Furthermore, you don't
+ # The routes above will automatically point to <tt>MyEngine::ArticlesController</tt>. Furthermore, you don't
# need to use longer url helpers like <tt>my_engine_articles_path</tt>. Instead, you should simply use
# <tt>articles_path</tt> as you would do with your application.
#
@@ -296,16 +296,16 @@ module Rails
# If you want to share just a few specific helpers you can add them to application's
# helpers in ApplicationController:
#
- # class ApplicationController < ActionController::Base
- # helper MyEngine::SharedEngineHelper
- # end
+ # class ApplicationController < ActionController::Base
+ # helper MyEngine::SharedEngineHelper
+ # end
#
# If you want to include all of the engine's helpers, you can use #helpers method on an engine's
# instance:
#
- # class ApplicationController < ActionController::Base
- # helper MyEngine::Engine.helpers
- # end
+ # class ApplicationController < ActionController::Base
+ # helper MyEngine::Engine.helpers
+ # end
#
# It will include all of the helpers from engine's directory. Take into account that this does
# not include helpers defined in controllers with helper_method or other similar solutions,
@@ -330,6 +330,17 @@ module Rails
#
# MyEngine::Engine.load_seed
#
+ # == Loading priority
+ #
+ # In order to change engine's priority you can use config.railties_order in main application.
+ # It will affect the priority of loading views, helpers, assets and all the other files
+ # related to engine or application.
+ #
+ # Example:
+ #
+ # # load Blog::Engine with highest priority, followed by application and other railties
+ # config.railties_order = [Blog::Engine, :main_app, :all]
+ #
class Engine < Railtie
autoload :Configuration, "rails/engine/configuration"
autoload :Railties, "rails/engine/railties"
@@ -371,20 +382,28 @@ module Rails
self.routes.default_scope = { :module => ActiveSupport::Inflector.underscore(mod.name) }
self.isolated = true
- unless mod.respond_to?(:_railtie)
- name = engine_name
- _railtie = self
+ unless mod.respond_to?(:railtie_namespace)
+ name, railtie = engine_name, self
+
mod.singleton_class.instance_eval do
- define_method(:_railtie) do
- _railtie
- end
+ define_method(:railtie_namespace) { railtie }
unless mod.respond_to?(:table_name_prefix)
- define_method(:table_name_prefix) do
- "#{name}_"
- end
+ define_method(:table_name_prefix) { "#{name}_" }
+ end
+
+ unless mod.respond_to?(:use_relative_model_naming?)
+ class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__
+ end
+
+ unless mod.respond_to?(:railtie_helpers_paths)
+ define_method(:railtie_helpers_paths) { railtie.helpers_paths }
+ end
+
+ unless mod.respond_to?(:railtie_routes_url_helpers)
+ define_method(:railtie_routes_url_helpers) { railtie.routes_url_helpers }
end
- end
+ end
end
end
@@ -429,13 +448,6 @@ module Rails
def helpers
@helpers ||= begin
helpers = Module.new
-
- helpers_paths = if config.respond_to?(:helpers_paths)
- config.helpers_paths
- else
- paths["app/helpers"].existent
- end
-
all = ActionController::Base.all_helpers_from_path(helpers_paths)
ActionController::Base.modules_for_helpers(all).each do |mod|
helpers.send(:include, mod)
@@ -444,6 +456,14 @@ module Rails
end
end
+ def helpers_paths
+ paths["app/helpers"].existent
+ end
+
+ def routes_url_helpers
+ routes.url_helpers
+ end
+
def app
@app ||= begin
config.middleware = config.middleware.merge_into(default_middleware_stack)
@@ -471,10 +491,19 @@ module Rails
@routes
end
+ def ordered_railties
+ railties.all + [self]
+ end
+
def initializers
initializers = []
- railties.all { |r| initializers += r.initializers }
- initializers += super
+ ordered_railties.each do |r|
+ if r == self
+ initializers += super
+ else
+ initializers += r.initializers
+ end
+ end
initializers
end
@@ -488,7 +517,7 @@ module Rails
# Blog::Engine.load_seed
def load_seed
seed_file = paths["db/seeds"].existent.first
- load(seed_file) if seed_file && File.exist?(seed_file)
+ load(seed_file) if seed_file
end
# Add configured load paths to ruby load paths and remove duplicates.
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index f424492bb4..d7405cb519 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -5,7 +5,6 @@ module Rails
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
- attr_accessor :plugins
def initialize(root=nil)
super()
@@ -30,7 +29,7 @@ module Rails
#
# config.generators.colorize_logging = false
#
- def generators #:nodoc
+ def generators #:nodoc:
@generators ||= Rails::Configuration::Generators.new
yield(@generators) if block_given?
@generators
@@ -59,7 +58,6 @@ module Rails
paths.add "db/seeds", :with => "db/seeds.rb"
paths.add "vendor", :load_path => true
paths.add "vendor/assets", :glob => "*"
- paths.add "vendor/plugins"
paths
end
end
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
index d5ecd2e48d..033d9c4180 100644
--- a/railties/lib/rails/engine/railties.rb
+++ b/railties/lib/rails/engine/railties.rb
@@ -7,18 +7,11 @@ module Rails
end
def all(&block)
- @all ||= plugins
+ @all ||= []
@all.each(&block) if block
@all
end
- def plugins
- @plugins ||= begin
- plugin_names = (@config.plugins || [:all]).map { |p| p.to_sym }
- Plugin.all(plugin_names, @config.paths["vendor/plugins"].existent)
- end
- end
-
def self.railties
@railties ||= ::Rails::Railtie.subclasses.map(&:instance)
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 27f8d13ce8..cd277c5097 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -38,11 +38,6 @@ module Rails
:test_unit => {
:fixture_replacement => '-r',
- },
-
- :plugin => {
- :generator => '-g',
- :tasks => '-r'
}
}
@@ -62,11 +57,6 @@ module Rails
:stylesheet_engine => :css,
:test_framework => false,
:template_engine => :erb
- },
-
- :plugin => {
- :generator => false,
- :tasks => false
}
}
@@ -195,7 +185,6 @@ module Rails
"#{test}:scaffold",
"#{test}:view",
"#{test}:performance",
- "#{test}:plugin",
"#{template}:controller",
"#{template}:scaffold",
"#{template}:mailer",
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index b26839644e..32793b1a70 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -5,40 +5,6 @@ module Rails
module Generators
module Actions
- # Install a plugin. You must provide either a Subversion url or Git url.
- #
- # For a Git-hosted plugin, you can specify a branch and
- # whether it should be added as a submodule instead of cloned.
- #
- # For a Subversion-hosted plugin you can specify a revision.
- #
- # ==== Examples
- #
- # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git'
- # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :branch => 'stable'
- # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true
- # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
- # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk', :revision => 1234
- #
- def plugin(name, options)
- log :plugin, name
-
- if options[:git] && options[:submodule]
- options[:git] = "-b #{options[:branch]} #{options[:git]}" if options[:branch]
- in_root do
- run "git submodule add #{options[:git]} vendor/plugins/#{name}", :verbose => false
- end
- elsif options[:git] || options[:svn]
- options[:git] = "-b #{options[:branch]} #{options[:git]}" if options[:branch]
- options[:svn] = "-r #{options[:revision]} #{options[:svn]}" if options[:revision]
- in_root do
- run_ruby_script "script/rails plugin install #{options[:svn] || options[:git]}", :verbose => false
- end
- else
- log "! no git or svn provided for #{name}. Skipping..."
- end
- end
-
# Adds an entry into Gemfile for the supplied gem. If env
# is specified, add the gem to the given environment.
#
@@ -68,8 +34,9 @@ module Rails
end
in_root do
- str = "gem #{parts.join(", ")}\n"
+ str = "gem #{parts.join(", ")}"
str = " " + str if @in_group
+ str = "\n" + str
append_file "Gemfile", str, :verbose => false
end
end
@@ -87,13 +54,13 @@ module Rails
log :gemfile, "group #{name}"
in_root do
- append_file "Gemfile", "\ngroup #{name} do\n", :force => true
+ append_file "Gemfile", "\ngroup #{name} do", :force => true
@in_group = true
instance_eval(&block)
@in_group = false
- append_file "Gemfile", "end\n", :force => true
+ append_file "Gemfile", "\nend\n", :force => true
end
end
@@ -124,7 +91,7 @@ module Rails
if options[:env].nil?
inject_into_file 'config/application.rb', "\n #{data}", :after => sentinel, :verbose => false
else
- Array.wrap(options[:env]).each do |env|
+ Array(options[:env]).each do |env|
inject_into_file "config/environments/#{env}.rb", "\n #{data}", :after => env_file_sentinel, :verbose => false
end
end
@@ -276,10 +243,10 @@ module Rails
#
def route(routing_code)
log :route, routing_code
- sentinel = /\.routes\.draw do(?:\s*\|map\|)?\s*$/
+ sentinel = /\.routes\.draw do\s*$/
in_root do
- inject_into_file 'config/routes.rb', "\n #{routing_code}\n", { :after => sentinel, :verbose => false }
+ inject_into_file 'config/routes.rb', "\n #{routing_code}", { :after => sentinel, :verbose => false }
end
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 10fdfdd8a9..7c449657b5 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -60,9 +60,6 @@ module Rails
class_option :help, :type => :boolean, :aliases => "-h", :group => :rails,
:desc => "Show this help message and quit"
-
- class_option :old_style_hash, :type => :boolean, :default => false,
- :desc => "Force using old style hash (:foo => 'bar') on Ruby >= 1.9"
end
def initialize(*args)
@@ -138,19 +135,21 @@ module Rails
if options.dev?
<<-GEMFILE.strip_heredoc
gem 'rails', :path => '#{Rails::Generators::RAILS_DEV_PATH}'
- gem 'journey', :git => 'git://github.com/rails/journey.git'
+ gem 'journey', :git => 'https://github.com/rails/journey.git'
+ gem 'arel', :git => 'https://github.com/rails/arel.git'
GEMFILE
elsif options.edge?
<<-GEMFILE.strip_heredoc
- gem 'rails', :git => 'git://github.com/rails/rails.git'
- gem 'journey', :git => 'git://github.com/rails/journey.git'
+ gem 'rails', :git => 'https://github.com/rails/rails.git'
+ gem 'journey', :git => 'https://github.com/rails/journey.git'
+ gem 'arel', :git => 'https://github.com/rails/arel.git'
GEMFILE
else
<<-GEMFILE.strip_heredoc
gem 'rails', '#{Rails::VERSION::STRING}'
# Bundle edge Rails instead:
- # gem 'rails', :git => 'git://github.com/rails/rails.git'
+ # gem 'rails', :git => 'https://github.com/rails/rails.git'
GEMFILE
end
end
@@ -182,30 +181,52 @@ module Rails
end
end
- def ruby_debugger_gemfile_entry
- if RUBY_VERSION < "1.9"
- "gem 'ruby-debug'"
+ def assets_gemfile_entry
+ return if options[:skip_sprockets]
+
+ gemfile = if options.dev? || options.edge?
+ <<-GEMFILE
+ # Gems used only for assets and not required
+ # in production environments by default.
+ group :assets do
+ gem 'sass-rails', :git => 'https://github.com/rails/sass-rails.git'
+ gem 'coffee-rails', :git => 'https://github.com/rails/coffee-rails.git'
+
+ # See https://github.com/sstephenson/execjs#readme for more supported runtimes
+ #{javascript_runtime_gemfile_entry}
+ gem 'uglifier', '>= 1.0.3'
+ end
+ GEMFILE
else
- "gem 'ruby-debug19', :require => 'ruby-debug'"
+ <<-GEMFILE
+ # Gems used only for assets and not required
+ # in production environments by default.
+ group :assets do
+ gem 'sass-rails', '~> 4.0.0.beta'
+ gem 'coffee-rails', '~> 4.0.0.beta'
+
+ # See https://github.com/sstephenson/execjs#readme for more supported runtimes
+ #{javascript_runtime_gemfile_entry}
+ gem 'uglifier', '>= 1.0.3'
+ end
+ GEMFILE
end
- end
- def assets_gemfile_entry
- <<-GEMFILE.strip_heredoc
- # Gems used only for assets and not required
- # in production environments by default.
- group :assets do
- gem 'sass-rails', :git => 'git://github.com/rails/sass-rails.git'
- gem 'coffee-rails', :git => 'git://github.com/rails/coffee-rails.git'
- gem 'uglifier', '>= 1.0.3'
- end
- GEMFILE
+ gemfile.strip_heredoc.gsub(/^[ \t]*$/, '')
end
def javascript_gemfile_entry
"gem '#{options[:javascript]}-rails'" unless options[:skip_javascript]
end
+ def javascript_runtime_gemfile_entry
+ if defined?(JRUBY_VERSION)
+ "gem 'therubyrhino'\n"
+ else
+ "# gem 'therubyracer'\n"
+ end
+ end
+
def bundle_command(command)
say_status :run, "bundle #{command}"
@@ -223,7 +244,7 @@ module Rails
end
def run_bundle
- bundle_command('install') unless options[:skip_gemfile] || options[:skip_bundle]
+ bundle_command('install') unless options[:skip_gemfile] || options[:skip_bundle] || options[:pretend]
end
def empty_directory_with_gitkeep(destination, config = {})
@@ -235,14 +256,9 @@ module Rails
create_file("#{destination}/.gitkeep") unless options[:skip_git]
end
- # Returns Ruby 1.9 style key-value pair if current code is running on
- # Ruby 1.9.x. Returns the old-style (with hash rocket) otherwise.
+ # Returns Ruby 1.9 style key-value pair.
def key_value(key, value)
- if options[:old_style_hash] || RUBY_VERSION < '1.9'
- ":#{key} => #{value}"
- else
- "#{key}: #{value}"
- end
+ "#{key}: #{value}"
end
end
end
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 911f80cf3a..60e94486bb 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -34,7 +34,7 @@ module Rails
usage = source_root && File.expand_path("../USAGE", source_root)
@desc ||= if usage && File.exist?(usage)
- File.read(usage)
+ ERB.new(File.read(usage)).result(binding)
else
"Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
end
@@ -48,6 +48,12 @@ module Rails
@namespace ||= super.sub(/_generator$/, '').sub(/:generators:/, ':')
end
+ # Convenience method to hide this generator from the available ones when
+ # running rails generator command.
+ def self.hide!
+ Rails::Generators.hide_namespace self.namespace
+ end
+
# Invoke a generator based on the value supplied by the user to the
# given option named "name". A class option is created when this method
# is invoked and you can set a hash to customize it.
@@ -91,7 +97,7 @@ module Rails
#
# The lookup in this case for test_unit as input is:
#
- # "test_framework:awesome", "test_framework"
+ # "test_unit:awesome", "test_unit"
#
# Which is not the desired the lookup. You can change it by providing the
# :as option:
@@ -102,7 +108,7 @@ module Rails
#
# And now it will lookup at:
#
- # "test_framework:controller", "test_framework"
+ # "test_unit:controller", "test_unit"
#
# Similarly, if you want it to also lookup in the rails namespace, you just
# need to provide the :base value:
@@ -113,7 +119,7 @@ module Rails
#
# And the lookup is exactly the same as previously:
#
- # "rails:test_framework", "test_framework:controller", "test_framework"
+ # "rails:test_unit", "test_unit:controller", "test_unit"
#
# ==== Switches
#
@@ -128,13 +134,13 @@ module Rails
#
# ==== Boolean hooks
#
- # In some cases, you want to provide a boolean hook. For example, webrat
+ # In some cases, you may want to provide a boolean hook. For example, webrat
# developers might want to have webrat available on controller generator.
# This can be achieved as:
#
# Rails::Generators::ControllerGenerator.hook_for :webrat, :type => :boolean
#
- # Then, if you want, webrat to be invoked, just supply:
+ # Then, if you want webrat to be invoked, just supply:
#
# rails generate controller Account --webrat
#
@@ -146,7 +152,7 @@ module Rails
#
# You can also supply a block to hook_for to customize how the hook is
# going to be invoked. The block receives two arguments, an instance
- # of the current class and the klass to be invoked.
+ # of the current class and the class to be invoked.
#
# For example, in the resource generator, the controller should be invoked
# with a pluralized class name. But by default it is invoked with the same
@@ -254,17 +260,13 @@ module Rails
nesting = class_name.split('::')
last_name = nesting.pop
- # Hack to limit const_defined? to non-inherited on 1.9
- extra = []
- extra << false unless Object.method(:const_defined?).arity == 1
-
# Extract the last Module in the nesting
last = nesting.inject(Object) do |last_module, nest|
- break unless last_module.const_defined?(nest, *extra)
+ break unless last_module.const_defined?(nest, false)
last_module.const_get(nest)
end
- if last && last.const_defined?(last_name.camelize, *extra)
+ if last && last.const_defined?(last_name.camelize, false)
raise Error, "The name '#{class_name}' is either already used in your application " <<
"or reserved by Ruby on Rails. Please choose an alternative and run " <<
"this generator again."
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
index 7b1a2a1841..85296ca37b 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -10,16 +10,14 @@
<th></th>
</tr>
-<%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
- <tr>
+ <%%= content_tag_for(:tr, @<%= plural_table_name %>) do |<%= singular_table_name %>| %>
<% attributes.each do |attribute| -%>
<td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td>
<% end -%>
<td><%%= link_to 'Show', <%= singular_table_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
<td><%%= link_to 'Destroy', <%= singular_table_name %>, <%= key_value :confirm, "'Are you sure?'" %>, <%= key_value :method, ":delete" %> %></td>
- </tr>
-<%% end %>
+ <%% end %>
</table>
<br />
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 816d82cac3..7dfc1aa599 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -1,14 +1,51 @@
require 'active_support/time'
require 'active_support/core_ext/object/inclusion'
+require 'active_support/core_ext/object/blank'
module Rails
module Generators
class GeneratedAttribute
+ INDEX_OPTIONS = %w(index uniq)
+ UNIQ_INDEX_OPTIONS = %w(uniq)
+
attr_accessor :name, :type
+ attr_reader :attr_options
+
+ class << self
+ def parse(column_definition)
+ name, type, has_index = column_definition.split(':')
+
+ # if user provided "name:index" instead of "name:string:index"
+ # type should be set blank so GeneratedAttribute's constructor
+ # could set it to :string
+ has_index, type = type, nil if INDEX_OPTIONS.include?(type)
+
+ type, attr_options = *parse_type_and_options(type)
+ new(name, type, has_index, attr_options)
+ end
+
+ private
+
+ # parse possible attribute options like :limit for string/text/binary/integer or :precision/:scale for decimals
+ # when declaring options curly brackets should be used
+ def parse_type_and_options(type)
+ case type
+ when /(string|text|binary|integer)\{(\d+)\}/
+ return $1, :limit => $2.to_i
+ when /decimal\{(\d+)[,.-](\d+)\}/
+ return :decimal, :precision => $1.to_i, :scale => $2.to_i
+ else
+ return type, {}
+ end
+ end
+ end
- def initialize(name, type)
- type = :string if type.blank?
- @name, @type = name, type.to_sym
+ def initialize(name, type=nil, index_type=false, attr_options={})
+ @name = name
+ @type = (type.presence || :string).to_sym
+ @has_index = INDEX_OPTIONS.include?(index_type)
+ @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type)
+ @attr_options = attr_options
end
def field_type
@@ -45,8 +82,28 @@ module Rails
name.to_s.humanize
end
+ def index_name
+ reference? ? "#{name}_id" : name
+ end
+
def reference?
- self.type.in?([:references, :belongs_to])
+ self.type.in?(:references, :belongs_to)
+ end
+
+ def has_index?
+ @has_index
+ end
+
+ def has_uniq_index?
+ @has_uniq_index
+ end
+
+ def inject_options
+ "".tap { |s| @attr_options.each { |k,v| s << ", #{k}: #{v.inspect}" } }
+ end
+
+ def inject_index_options
+ has_uniq_index? ? ", unique: true" : ""
end
end
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index c6c0392f43..9cef55e0a6 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -9,9 +9,6 @@ module Rails
class_option :skip_namespace, :type => :boolean, :default => false,
:desc => "Skip namespace (affects only isolated applications)"
- class_option :old_style_hash, :type => :boolean, :default => false,
- :desc => "Force using old style hash (:foo => 'bar') on Ruby >= 1.9"
-
def initialize(args, *options) #:nodoc:
@inside_template = nil
# Unfreeze name in case it's given as a frozen string
@@ -153,9 +150,8 @@ module Rails
# Convert attributes array into GeneratedAttribute objects.
def parse_attributes! #:nodoc:
- self.attributes = (attributes || []).map do |key_value|
- name, type = key_value.split(':')
- Rails::Generators::GeneratedAttribute.new(name, type)
+ self.attributes = (attributes || []).map do |attr|
+ Rails::Generators::GeneratedAttribute.parse(attr)
end
end
@@ -185,14 +181,9 @@ module Rails
end
end
- # Returns Ruby 1.9 style key-value pair if current code is running on
- # Ruby 1.9.x. Returns the old-style (with hash rocket) otherwise.
+ # Returns Ruby 1.9 style key-value pair.
def key_value(key, value)
- if options[:old_style_hash] || RUBY_VERSION < '1.9'
- ":#{key} => #{value}"
- else
- "#{key}: #{value}"
- end
+ "#{key}: #{value}"
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 3e32f758a4..f0745df667 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -38,7 +38,7 @@ module Rails
end
def readme
- copy_file "README"
+ copy_file "README", "README.rdoc"
end
def gemfile
@@ -124,7 +124,6 @@ module Rails
def vendor
vendor_javascripts
vendor_stylesheets
- vendor_plugins
end
def vendor_javascripts
@@ -134,10 +133,6 @@ module Rails
def vendor_stylesheets
empty_directory_with_gitkeep "vendor/assets/stylesheets"
end
-
- def vendor_plugins
- empty_directory_with_gitkeep "vendor/plugins"
- end
end
module Generators
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index d3b8f4d595..5fdfe8f04e 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -1,11 +1,10 @@
-source 'http://rubygems.org'
+source 'https://rubygems.org'
<%= rails_gemfile_entry -%>
<%= database_gemfile_entry -%>
<%= "gem 'jruby-openssl'\n" if defined?(JRUBY_VERSION) -%>
-<%= "gem 'json'\n" if RUBY_VERSION < "1.9.2" -%>
<%= assets_gemfile_entry %>
<%= javascript_gemfile_entry %>
@@ -13,11 +12,14 @@ source 'http://rubygems.org'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
+# To use Jbuilder templates for JSON
+# gem 'jbuilder'
+
# Use unicorn as the web server
# gem 'unicorn'
# Deploy with Capistrano
-# gem 'capistrano'
+# gem 'capistrano', :group => :development
# To use debugger
-# <%= ruby_debugger_gemfile_entry %>
+# gem 'ruby-debug19', :require => 'ruby-debug'
diff --git a/railties/lib/rails/generators/rails/app/templates/README b/railties/lib/rails/generators/rails/app/templates/README
index 7c36f2356e..d2014bd35f 100644
--- a/railties/lib/rails/generators/rails/app/templates/README
+++ b/railties/lib/rails/generators/rails/app/templates/README
@@ -86,8 +86,8 @@ programming in general.
Debugger support is available through the debugger command when you start your
Mongrel or WEBrick server with --debugger. This means that you can break out of
execution at any point in the code, investigate and change the model, and then,
-resume execution! You need to install ruby-debug to run the server in debugging
-mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
+resume execution! You need to install ruby-debug19 to run the server in debugging
+mode. With gems, use <tt>sudo gem install ruby-debug19</tt>. Example:
class WeblogController < ActionController::Base
def index
@@ -191,7 +191,6 @@ The default directory structure of a generated Ruby on Rails application:
`-- vendor
|-- assets
`-- stylesheets
- `-- plugins
app
Holds all the code that's specific to this particular application.
@@ -256,6 +255,5 @@ test
directory.
vendor
- External libraries that the application depends on. Also includes the plugins
- subdirectory. If the app has frozen rails, those gems also go here, under
- vendor/rails/. This directory is in the load path.
+ External libraries that the application depends on. If the app has frozen rails,
+ those gems also go here, under vendor/rails/. This directory is in the load path.
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
index c63d1b6ac5..bba96a7431 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -2,7 +2,7 @@
<html>
<head>
<title><%= camelized %></title>
- <%%= stylesheet_link_tag "application" %>
+ <%%= stylesheet_link_tag "application", :media => "all" %>
<%%= javascript_include_tag "application" %>
<%%= csrf_meta_tags %>
</head>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 13fbe9e526..acf47a03e5 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -13,9 +13,9 @@ require "active_resource/railtie"
<% end -%>
if defined?(Bundler)
- # If you precompile assets before deploying to production, use this line
+ # If you precompile assets before deploying to production, use this line.
Bundler.require(*Rails.groups(:assets => %w(development test)))
- # If you want your assets lazily compiled in production, use this line
+ # If you want your assets lazily compiled in production, use this line.
# Bundler.require(:default, :assets, Rails.env)
end
@@ -28,10 +28,6 @@ module <%= app_const_base %>
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
- # Only load the plugins named here, in the order given (default is alphabetical).
- # :all can be used as a placeholder for all plugins not explicitly named.
- # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
-
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
@@ -49,11 +45,27 @@ module <%= app_const_base %>
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
+ # Use SQL instead of Active Record's schema dumper when creating the database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types.
+ # config.active_record.schema_format = :sql
+
+ # Enforce whitelist mode for mass assignment.
+ # This will create an empty whitelist of attributes available for mass-assignment for all models
+ # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
+ # parameters by using an attr_accessible or attr_protected declaration.
+ # config.active_record.whitelist_attributes = true
+
+ # Specifies wether or not has_many or has_one association option :dependent => :restrict raises
+ # an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
+ # raised. If set to false, then an error will be added on the model instead.
+ <%= comment_if :skip_active_record %>config.active_record.dependent_restrict_raises = false
+
<% unless options.skip_sprockets? -%>
- # Enable the asset pipeline
+ # Enable the asset pipeline.
config.assets.enabled = true
- # Version of your assets, change this if you want to expire all your assets
+ # Version of your assets, change this if you want to expire all your assets.
config.assets.version = '1.0'
<% end -%>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 47078e3af9..eb4dfa7c89 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -1,35 +1,38 @@
<%= app_const %>.configure do
- # Settings specified here will take precedence over those in config/application.rb
+ # Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
- # Log error messages when you accidentally call methods on nil.
- config.whiny_nils = true
-
- # Show full error reports and disable caching
+ # Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
- # Don't care if the mailer can't send
+ # Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
- # Print deprecation notices to the Rails logger
+ # Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
- # Only use best-standards-support built into browsers
+ # Only use best-standards-support built into browsers.
config.action_dispatch.best_standards_support = :builtin
<%- unless options.skip_active_record? -%>
- # Raise exception on mass assignment protection for ActiveRecord models
+ # Raise exception on mass assignment protection for Active Record models.
config.active_record.mass_assignment_sanitizer = :strict
+
+ # Log the query plan for queries taking more than this (works
+ # with SQLite, MySQL, and PostgreSQL).
+ config.active_record.auto_explain_threshold_in_seconds = 0.5
<%- end -%>
- # Do not compress assets
+ <%- unless options.skip_sprockets? -%>
+ # Do not compress assets.
config.assets.compress = false
- # Expands the lines which load the assets
+ # Expands the lines which load the assets.
config.assets.debug = true
+ <%- end -%>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 50f2df3d35..e9a86d175e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -1,63 +1,73 @@
<%= app_const %>.configure do
- # Settings specified here will take precedence over those in config/application.rb
+ # Settings specified here will take precedence over those in config/application.rb.
- # Code is not reloaded between requests
+ # Code is not reloaded between requests.
config.cache_classes = true
- # Full error reports are disabled and caching is turned on
+ # Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
- # Disable Rails's static asset server (Apache or nginx will already do this)
+ # Disable Rails's static asset server (Apache or nginx will already do this).
config.serve_static_assets = false
- # Compress JavaScripts and CSS
+ <%- unless options.skip_sprockets? -%>
+ # Compress JavaScripts and CSS.
config.assets.compress = true
- # Don't fallback to assets pipeline if a precompiled asset is missed
+ # Don't fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
- # Generate digests for assets URLs
+ # Generate digests for assets URLs.
config.assets.digest = true
- # Defaults to Rails.root.join("public/assets")
+ # Defaults to Rails.root.join("public/assets").
# config.assets.manifest = YOUR_PATH
+ <%- end -%>
- # Specifies the header that your server uses for sending files
+ # Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
- # See everything in the log (default is :info)
+ # See everything in the log (default is :info).
# config.log_level = :debug
- # Prepend all log lines with the following tags
+ # Prepend all log lines with the following tags.
# config.log_tags = [ :subdomain, :uuid ]
- # Use a different logger for distributed setups
+ # Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
- # Use a different cache store in production
+ # Use a different cache store in production.
# config.cache_store = :mem_cache_store
- # Enable serving of images, stylesheets, and JavaScripts from an asset server
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = "http://assets.example.com"
- # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ <%- unless options.skip_sprockets? -%>
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added).
# config.assets.precompile += %w( search.js )
+ <%- end -%>
- # Disable delivery errors, bad email addresses will be ignored
+ # Disable delivery errors, bad email addresses will be ignored.
# config.action_mailer.raise_delivery_errors = false
- # Enable threaded mode
+ # Enable threaded mode.
# config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
- # the I18n.default_locale when a translation can not be found)
+ # the I18n.default_locale when a translation can not be found).
config.i18n.fallbacks = true
- # Send deprecation notices to registered listeners
+ # Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
+
+ <%- unless options.skip_active_record? -%>
+ # Log the query plan for queries taking more than this (works
+ # with SQLite, MySQL, and PostgreSQL).
+ # config.active_record.auto_explain_threshold_in_seconds = 0.5
+ <%- end -%>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 80198cc21e..b725dd19f6 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -1,5 +1,5 @@
<%= app_const %>.configure do
- # Settings specified here will take precedence over those in config/application.rb
+ # Settings specified here will take precedence over those in config/application.rb.
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
@@ -7,38 +7,30 @@
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
- # Configure static asset server for tests with Cache-Control for performance
+ # Configure static asset server for tests with Cache-Control for performance.
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
- # Log error messages when you accidentally call methods on nil
- config.whiny_nils = true
-
- # Show full error reports and disable caching
+ # Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
- # Raise exceptions instead of rendering exception templates
+ # Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
- # Disable request forgery protection in test environment
- config.action_controller.allow_forgery_protection = false
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
- # Use SQL instead of Active Record's schema dumper when creating the test database.
- # This is necessary if your schema can't be completely dumped by the schema dumper,
- # like if you have constraints or database-specific column types
- # config.active_record.schema_format = :sql
-
<%- unless options.skip_active_record? -%>
- # Raise exception on mass assignment protection for ActiveRecord models
+ # Raise exception on mass assignment protection for Active Record models.
config.active_record.mass_assignment_sanitizer = :strict
<%- end -%>
- # Print deprecation notices to the stderr
+ # Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
end
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore
index eb3489a986..8910bf5a06 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore
@@ -9,6 +9,7 @@
# Ignore the default SQLite database.
/db/*.sqlite3
+/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
/log/*.log
diff --git a/railties/lib/rails/generators/rails/app/templates/public/index.html b/railties/lib/rails/generators/rails/app/templates/public/index.html
index 9d9811a5bf..a1d50995c5 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/index.html
+++ b/railties/lib/rails/generators/rails/app/templates/public/index.html
@@ -59,7 +59,7 @@
#header {
- background-image: url("/assets/rails.png");
+ background-image: url("assets/rails.png");
background-repeat: no-repeat;
background-position: top left;
height: 64px;
diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb
index 8f5f78556f..52243f4a2f 100644
--- a/railties/lib/rails/generators/rails/controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb
@@ -3,7 +3,7 @@ class <%= class_name %>Controller < ApplicationController
<% actions.each do |action| -%>
def <%= action %>
end
-
+<%= "\n" unless action == actions.last -%>
<% end -%>
end
<% end -%>
diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb
index 39fa5b63b1..f87dce1502 100644
--- a/railties/lib/rails/generators/rails/migration/migration_generator.rb
+++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
class MigrationGenerator < NamedBase #metagenerator
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
hook_for :orm, :required => true
end
end
diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb
index 629d5eed3f..9bb29b784e 100644
--- a/railties/lib/rails/generators/rails/model/model_generator.rb
+++ b/railties/lib/rails/generators/rails/model/model_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
class ModelGenerator < NamedBase #metagenerator
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
hook_for :orm, :required => true
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index 4baa2110e7..f4263d1b98 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -39,7 +39,7 @@ module Rails
end
def gitignore
- copy_file "gitignore", ".gitignore"
+ template "gitignore", ".gitignore"
end
def lib
@@ -133,6 +133,16 @@ task :default => :test
end
chmod "script", 0755, :verbose => false
end
+
+ def gemfile_entry
+ return unless inside_application?
+
+ gemfile_in_app_path = File.join(rails_app_path, "Gemfile")
+ if File.exist? gemfile_in_app_path
+ entry = "gem '#{name}', :path => '#{relative_path}'"
+ append_file gemfile_in_app_path, entry
+ end
+ end
end
module Generators
@@ -153,6 +163,10 @@ task :default => :test
class_option :skip_gemspec, :type => :boolean, :default => false,
:desc => "Skip gemspec file"
+ class_option :skip_gemfile_entry, :type => :boolean, :default => false,
+ :desc => "If creating plugin in application's directory " +
+ "skip adding entry to Gemfile"
+
def initialize(*args)
raise Error, "Options should be given after the plugin name. For details run: rails plugin --help" if args[0].blank?
@@ -208,6 +222,10 @@ task :default => :test
create_dummy_app
end
+ def update_gemfile
+ build(:gemfile_entry) unless options[:skip_gemfile_entry]
+ end
+
def finish_template
build(:leftovers)
end
@@ -246,8 +264,20 @@ task :default => :test
"rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]"
end
+ def original_name
+ @original_name ||= File.basename(destination_root)
+ end
+
def name
- @name ||= File.basename(destination_root)
+ @name ||= begin
+ # same as ActiveSupport::Inflector#underscore except not replacing '-'
+ underscored = original_name.dup
+ underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
+ underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
+ underscored.downcase!
+
+ underscored
+ end
end
def camelized
@@ -255,12 +285,14 @@ task :default => :test
end
def valid_const?
- if camelized =~ /^\d/
- raise Error, "Invalid plugin name #{name}. Please give a name which does not start with numbers."
+ if original_name =~ /[^0-9a-zA-Z_]+/
+ raise Error, "Invalid plugin name #{original_name}. Please give a name which use only alphabetic or numeric or \"_\" characters."
+ elsif camelized =~ /^\d/
+ raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(name)
- raise Error, "Invalid plugin name #{name}. Please give a name which does not match one of the reserved rails words."
+ raise Error, "Invalid plugin name #{original_name}. Please give a name which does not match one of the reserved rails words."
elsif Object.const_defined?(camelized)
- raise Error, "Invalid plugin name #{name}, constant #{camelized} is already in use. Please choose another plugin name."
+ raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name."
end
end
@@ -301,6 +333,19 @@ end
def mute(&block)
shell.mute(&block)
end
+
+ def rails_app_path
+ APP_PATH.sub("/config/application", "") if defined?(APP_PATH)
+ end
+
+ def inside_application?
+ rails_app_path && app_path =~ /^#{rails_app_path}/
+ end
+
+ def relative_path
+ return unless inside_application?
+ app_path.sub(/^#{rails_app_path}\//, '')
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
index f4efd3af74..d316b00c43 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
@@ -20,4 +20,4 @@ gem "jquery-rails"
<% end -%>
# To use debugger
-# <%= ruby_debugger_gemfile_entry %>
+# gem 'ruby-debug19', :require => 'ruby-debug'
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt
index 01550dec2f..bd983fb90f 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt
@@ -2,7 +2,7 @@
<html>
<head>
<title><%= camelized %></title>
- <%%= stylesheet_link_tag "<%= name %>/application" %>
+ <%%= stylesheet_link_tag "<%= name %>/application", :media => "all" %>
<%%= javascript_include_tag "<%= name %>/application" %>
<%%= csrf_meta_tags %>
</head>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
index 1463de6dfb..458b2c662e 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/gitignore
@@ -1,6 +1,8 @@
.bundle/
log/*.log
pkg/
-test/dummy/db/*.sqlite3
-test/dummy/log/*.log
-test/dummy/tmp/ \ No newline at end of file
+<%= dummy_path %>/db/*.sqlite3
+<%= dummy_path %>/db/*.sqlite3-journal
+<%= dummy_path %>/log/*.log
+<%= dummy_path %>/tmp/
+<%= dummy_path %>/.sass-cache
diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE
index be1d113ed8..4a3eb2c7c7 100644
--- a/railties/lib/rails/generators/rails/scaffold/USAGE
+++ b/railties/lib/rails/generators/rails/scaffold/USAGE
@@ -7,23 +7,29 @@ Description:
under_scored, as the first argument, and an optional list of attribute
pairs.
- Attribute pairs are field:type arguments specifying the
- model's attributes. Timestamps are added by default, so you don't have to
- specify them by hand as 'created_at:datetime updated_at:datetime'.
+ Attributes are field arguments specifying the model's attributes. You can
+ optionally pass the type and an index to each field. For instance:
+ "title body:text tracking_id:integer:uniq" will generate a title field of
+ string type, a body with text type and a tracking_id as an integer with an
+ unique index. "index" could also be given instead of "uniq" if one desires
+ a non unique index.
+
+ Timestamps are added by default, so you don't have to specify them by hand
+ as 'created_at:datetime updated_at:datetime'.
You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the resource immediately.
- For example, 'scaffold post title:string body:text published:boolean'
- gives you a model with those three attributes, a controller that handles
+ For example, 'scaffold post title body:text published:boolean' gives
+ you a model with those three attributes, a controller that handles
the create/show/update/destroy, forms to create and edit your posts, and
- an index that lists them all, as well as a resources :posts
- declaration in config/routes.rb.
+ an index that lists them all, as well as a resources :posts declaration
+ in config/routes.rb.
If you want to remove all the generated files, run
'rails destroy scaffold ModelName'.
Examples:
`rails generate scaffold post`
- `rails generate scaffold post title:string body:text published:boolean`
- `rails generate scaffold purchase order_id:integer amount:decimal`
+ `rails generate scaffold post title body:text published:boolean`
+ `rails generate scaffold purchase amount:decimal tracking_id:integer:uniq`
diff --git a/railties/lib/rails/generators/rails/task/USAGE b/railties/lib/rails/generators/rails/task/USAGE
new file mode 100644
index 0000000000..dbe9bbaf08
--- /dev/null
+++ b/railties/lib/rails/generators/rails/task/USAGE
@@ -0,0 +1,9 @@
+Description:
+ Stubs out a new Rake task. Pass the namespace name, and a list of tasks as arguments.
+
+ This generates a task file in lib/tasks.
+
+Example:
+ `rails generate task feeds fetch erase add`
+
+ Task: lib/tasks/feeds.rake \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/task/task_generator.rb b/railties/lib/rails/generators/rails/task/task_generator.rb
new file mode 100644
index 0000000000..8a62d9e8eb
--- /dev/null
+++ b/railties/lib/rails/generators/rails/task/task_generator.rb
@@ -0,0 +1,12 @@
+module Rails
+ module Generators
+ class TaskGenerator < NamedBase
+ argument :actions, :type => :array, :default => [], :banner => "action action"
+
+ def create_task_files
+ template 'task.rb', File.join('lib/tasks', "#{file_name}.rake")
+ end
+
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb
new file mode 100644
index 0000000000..b7407bd6dc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/task/templates/task.rb
@@ -0,0 +1,8 @@
+namespace :<%= file_name %> do
+<% actions.each do |action| -%>
+ desc "TODO"
+ task :<%= action %> => :environment do
+ end
+
+<% end -%>
+end
diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb
index b34bc4a524..3c5b39fa16 100644
--- a/railties/lib/rails/generators/resource_helpers.rb
+++ b/railties/lib/rails/generators/resource_helpers.rb
@@ -64,7 +64,7 @@ module Rails
end
begin
- "#{options[:orm].to_s.classify}::Generators::ActiveModel".constantize
+ "#{options[:orm].to_s.camelize}::Generators::ActiveModel".constantize
rescue NameError
Rails::Generators::ActiveModel
end
@@ -73,7 +73,7 @@ module Rails
# Initialize ORM::Generators::ActiveModel to access instance methods.
def orm_instance(name=singular_table_name)
- @orm_instance ||= @orm_class.new(name)
+ @orm_instance ||= orm_class.new(name)
end
end
end
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index 7319fb79f6..d81c4c3e1d 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -218,8 +218,8 @@ module Rails
#
# create_generated_attribute(:string, 'name')
#
- def create_generated_attribute(attribute_type, name = 'test')
- Rails::Generators::GeneratedAttribute.new(name, attribute_type.to_s)
+ def create_generated_attribute(attribute_type, name = 'test', index = nil)
+ Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(':'))
end
protected
diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
index 2ca36a1e44..e82e321914 100644
--- a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
+++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
@@ -1,3 +1,3 @@
require 'rubygems'
-require 'test/unit'
+require 'minitest/autorun'
require 'active_support'
diff --git a/railties/lib/rails/performance_test_help.rb b/railties/lib/rails/performance_test_help.rb
index 4ac38981d0..b1285efde2 100644
--- a/railties/lib/rails/performance_test_help.rb
+++ b/railties/lib/rails/performance_test_help.rb
@@ -1,3 +1,3 @@
ActionController::Base.perform_caching = true
ActiveSupport::Dependencies.mechanism = :require
-Rails.logger.level = ActiveSupport::BufferedLogger::INFO
+Rails.logger.level = ActiveSupport::Logger::INFO
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
deleted file mode 100644
index 3e27688bb9..0000000000
--- a/railties/lib/rails/plugin.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-require 'rails/engine'
-require 'active_support/core_ext/array/conversions'
-
-module Rails
- # Rails::Plugin is nothing more than a Rails::Engine, but since it's loaded too late
- # in the boot process, it does not have the same configuration powers as a bare
- # Rails::Engine.
- #
- # Opposite to Rails::Railtie and Rails::Engine, you are not supposed to inherit from
- # Rails::Plugin. Rails::Plugin is automatically configured to be an engine by simply
- # placing inside vendor/plugins. Since this is done automatically, you actually cannot
- # declare a Rails::Engine inside your Plugin, otherwise it would cause the same files
- # to be loaded twice. This means that if you want to ship an Engine as gem it cannot
- # be used as plugin and vice-versa.
- #
- # Besides this conceptual difference, the only difference between Rails::Engine and
- # Rails::Plugin is that plugins automatically load the file "init.rb" at the plugin
- # root during the boot process.
- #
- class Plugin < Engine
- def self.global_plugins
- @global_plugins ||= []
- end
-
- def self.inherited(base)
- raise "You cannot inherit from Rails::Plugin"
- end
-
- def self.all(list, paths)
- plugins = []
- paths.each do |path|
- Dir["#{path}/*"].each do |plugin_path|
- plugin = new(plugin_path)
- next unless list.include?(plugin.name) || list.include?(:all)
- if global_plugins.include?(plugin.name)
- warn "WARNING: plugin #{plugin.name} from #{path} was not loaded. Plugin with the same name has been already loaded."
- next
- end
- global_plugins << plugin.name
- plugins << plugin
- end
- end
-
- plugins.sort_by do |p|
- [list.index(p.name) || list.index(:all), p.name.to_s]
- end
- end
-
- attr_reader :name, :path
-
- def railtie_name
- name.to_s
- end
-
- def initialize(root)
- @name = File.basename(root).to_sym
- config.root = root
- end
-
- def config
- @config ||= Engine::Configuration.new
- end
-
- initializer :handle_lib_autoload, :before => :set_load_path do |app|
- autoload = if app.config.reload_plugins
- config.autoload_paths
- else
- config.autoload_once_paths
- end
-
- autoload.concat paths["lib"].existent
- end
-
- initializer :load_init_rb, :before => :load_config_initializers do |app|
- init_rb = File.expand_path("init.rb", root)
- if File.file?(init_rb)
- # This double assignment is to prevent an "unused variable" warning on Ruby 1.9.3.
- config = config = app.config
- # TODO: think about evaling initrb in context of Engine (currently it's
- # always evaled in context of Rails::Application)
- eval(File.read(init_rb), binding, init_rb)
- end
- end
-
- initializer :sanity_check_railties_collision do
- if Engine.subclasses.map { |k| k.root.to_s }.include?(root.to_s)
- raise "\"#{name}\" is a Railtie/Engine and cannot be installed as a plugin"
- end
- end
- end
-end
diff --git a/railties/lib/rails/rack/debugger.rb b/railties/lib/rails/rack/debugger.rb
index 831188eeee..5a78da1731 100644
--- a/railties/lib/rails/rack/debugger.rb
+++ b/railties/lib/rails/rack/debugger.rb
@@ -12,7 +12,7 @@ module Rails
::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings)
puts "=> Debugger enabled"
rescue LoadError
- puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
+ puts "You need to install ruby-debug19 to run the server in debugging mode. With gems, use 'gem install ruby-debug19'"
exit
end
diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb
index 830d840894..18f22e8089 100644
--- a/railties/lib/rails/rack/log_tailer.rb
+++ b/railties/lib/rails/rack/log_tailer.rb
@@ -4,10 +4,13 @@ module Rails
def initialize(app, log = nil)
@app = app
- path = Pathname.new(log || "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath
- @cursor = ::File.size(path)
+ path = Pathname.new(log || "#{::File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath
- @file = ::File.open(path, 'r')
+ @cursor = @file = nil
+ if ::File.exists?(path)
+ @cursor = ::File.size(path)
+ @file = ::File.open(path, 'r')
+ end
end
def call(env)
@@ -17,6 +20,7 @@ module Rails
end
def tail!
+ return unless @cursor
@file.seek @cursor
unless @file.eof?
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index e8fb1f3d98..7fed7c8631 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -103,11 +103,11 @@ module Rails
# end
# end
#
- # == Application, Plugin and Engine
+ # == Application and Engine
#
# A Rails::Engine is nothing more than a Railtie with some initializers already set.
- # And since Rails::Application and Rails::Plugin are engines, the same configuration
- # described here can be used in all three.
+ # And since Rails::Application is an engine, the same configuration described here
+ # can be used in both.
#
# Be sure to look at the documentation of those specific classes for more information.
#
@@ -117,7 +117,7 @@ module Rails
include Initializable
- ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Plugin Rails::Engine Rails::Application)
+ ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
class << self
private :new
@@ -181,7 +181,7 @@ module Rails
def load_tasks(app=self)
extend Rake::DSL if defined? Rake::DSL
- self.class.rake_tasks.each { |block| block.call(app) }
+ self.class.rake_tasks.each { |block| self.instance_exec(app, &block) }
# load also tasks from all superclasses
klass = self.class.superclass
@@ -196,7 +196,7 @@ module Rails
end
def railtie_namespace
- @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:_railtie) }
+ @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
end
end
end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index f888684117..cf9e4ad500 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -7,6 +7,18 @@ module Rails
@@options ||= {}
end
+ # Add files that should be watched for change.
+ def watchable_files
+ @@watchable_files ||= []
+ end
+
+ # Add directories that should be watched for change.
+ # The key of the hashes should be directories and the values should
+ # be an array of extensions to match in each directory.
+ def watchable_dirs
+ @@watchable_dirs ||= {}
+ end
+
# This allows you to modify the application's middlewares from Engines.
#
# All operations you run on the app_middleware will be replayed on the
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index 4d57c5973c..4536fedaa3 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -1,8 +1,8 @@
-if RUBY_VERSION < '1.8.7'
+if RUBY_VERSION < '1.9.3'
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
- Rails 3 requires Ruby 1.8.7 or 1.9.2.
+ Rails 4 requires Ruby 1.9.3+.
You're running
#{desc}
@@ -10,14 +10,4 @@ if RUBY_VERSION < '1.8.7'
Please upgrade to continue.
end_message
-elsif RUBY_VERSION > '1.9' and RUBY_VERSION < '1.9.2'
- $stderr.puts <<-end_message
-
- Rails 3 doesn't officially support Ruby 1.9.1 since recent stable
- releases have segfaulted the test suite. Please upgrade to Ruby 1.9.2.
-
- You're running
- #{RUBY_DESCRIPTION}
-
- end_message
end
diff --git a/railties/lib/rails/rubyprof_ext.rb b/railties/lib/rails/rubyprof_ext.rb
index f6e90357ce..017eba3a76 100644
--- a/railties/lib/rails/rubyprof_ext.rb
+++ b/railties/lib/rails/rubyprof_ext.rb
@@ -12,7 +12,7 @@ module Prof #:nodoc:
io.puts " time seconds seconds calls ms/call ms/call name"
sum = 0.0
- for r in results
+ results.each do |r|
sum += r.self_time
name = if r.method_class.nil?
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 1eed763aa3..9bfc2b16ab 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -22,7 +22,7 @@ class SourceAnnotationExtractor
# If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
# Otherwise the string contains just line and text.
def to_s(options={})
- s = "[%3d] " % line
+ s = "[#{line.to_s.rjust(options[:indent])}]"
s << "[#{tag}] " if options[:tag]
s << text
end
@@ -30,7 +30,7 @@ class SourceAnnotationExtractor
# Prints all annotations with tag +tag+ under the root directories +app+, +config+, +lib+,
# +script+, and +test+ (recursively). Only filenames with extension
- # +.builder+, +.rb+, +.rxml+, +.rhtml+, or +.erb+ are taken into account. The +options+
+ # +.builder+, +.rb+, and +.erb+ are taken into account. The +options+
# hash is passed to each annotation's +to_s+.
#
# This class method is the single entry point for the rake tasks.
@@ -46,16 +46,14 @@ class SourceAnnotationExtractor
end
# Returns a hash that maps filenames under +dirs+ (recursively) to arrays
- # with their annotations. Only files with annotations are included, and only
- # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+
- # are taken into account.
+ # with their annotations.
def find(dirs=%w(app config lib script test))
dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
end
# Returns a hash that maps filenames under +dir+ (recursively) to arrays
# with their annotations. Only files with annotations are included, and only
- # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+
+ # those with extension +.builder+, +.rb+, +.erb+, +.haml+, +.slim+ and +.coffee+
# are taken into account.
def find_in(dir)
results = {}
@@ -65,10 +63,14 @@ class SourceAnnotationExtractor
if File.directory?(item)
results.update(find_in(item))
- elsif item =~ /\.(builder|(r(?:b|xml|js)))$/
+ elsif item =~ /\.(builder|rb|coffee)$/
results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
- elsif item =~ /\.(rhtml|erb)$/
+ elsif item =~ /\.erb$/
results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/))
+ elsif item =~ /\.haml$/
+ results.update(extract_annotations_from(item, /-\s*#\s*(#{tag}):?\s*(.*)$/))
+ elsif item =~ /\.slim$/
+ results.update(extract_annotations_from(item, /\/\s*\s*(#{tag}):?\s*(.*)$/))
end
end
@@ -91,6 +93,7 @@ class SourceAnnotationExtractor
# Prints the mapping from filenames to annotations in +results+ ordered by filename.
# The +options+ hash is passed to each annotation's +to_s+.
def display(results, options={})
+ options[:indent] = results.map { |f, a| a.map(&:line) }.flatten.max.to_s.size
results.keys.sort.each do |file|
puts "#{file}:"
results[file].each do |note|
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index ca8875ad9b..e09379c8c2 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -55,7 +55,7 @@ namespace :doc do
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('lib/**/*.rb')
}
- Rake::Task['doc:app'].comment = "Generate docs for the app -- also available doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")"
+ Rake::Task['doc:app'].comment = "Generate docs for the app -- also available doc:rails, doc:guides (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")"
# desc 'Generate documentation for the Rails framework.'
RDocTaskWithoutDescriptions.new("rails") { |rdoc|
@@ -63,93 +63,55 @@ namespace :doc do
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.title = "Rails Framework Documentation"
rdoc.options << '--line-numbers'
- rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('README.rdoc')
gem_path('actionmailer') do |actionmailer|
- %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_mailer/base.rb).each do |file|
+ %w(README.rdoc CHANGELOG.md MIT-LICENSE lib/action_mailer/base.rb).each do |file|
rdoc.rdoc_files.include("#{actionmailer}/#{file}")
end
end
gem_path('actionpack') do |actionpack|
- %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file|
+ %w(README.rdoc CHANGELOG.md MIT-LICENSE lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file|
rdoc.rdoc_files.include("#{actionpack}/#{file}")
end
end
gem_path('activemodel') do |activemodel|
- %w(README.rdoc CHANGELOG MIT-LICENSE lib/active_model/**/*.rb).each do |file|
+ %w(README.rdoc CHANGELOG.md MIT-LICENSE lib/active_model/**/*.rb).each do |file|
rdoc.rdoc_files.include("#{activemodel}/#{file}")
end
end
gem_path('activerecord') do |activerecord|
- %w(README.rdoc CHANGELOG lib/active_record/**/*.rb).each do |file|
+ %w(README.rdoc CHANGELOG.md lib/active_record/**/*.rb).each do |file|
rdoc.rdoc_files.include("#{activerecord}/#{file}")
end
end
gem_path('activeresource') do |activeresource|
- %w(README.rdoc CHANGELOG lib/active_resource.rb lib/active_resource/*).each do |file|
+ %w(README.rdoc CHANGELOG.md lib/active_resource.rb lib/active_resource/*).each do |file|
rdoc.rdoc_files.include("#{activeresource}/#{file}")
end
end
gem_path('activesupport') do |activesupport|
- %w(README.rdoc CHANGELOG lib/active_support/**/*.rb).each do |file|
+ %w(README.rdoc CHANGELOG.md lib/active_support/**/*.rb).each do |file|
rdoc.rdoc_files.include("#{activesupport}/#{file}")
end
end
gem_path('railties') do |railties|
- %w(README.rdoc CHANGELOG lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file|
+ %w(README.rdoc CHANGELOG.md lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file|
rdoc.rdoc_files.include("#{railties}/#{file}")
end
end
}
- plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) }
-
- # desc "Generate documentation for all installed plugins"
- task :plugins => plugins.collect { |plugin| "doc:plugins:#{plugin}" }
-
- # desc "Remove plugin documentation"
- task :clobber_plugins do
- rm_rf 'doc/plugins' rescue nil
- end
-
# desc "Generate Rails Guides"
task :guides do
# FIXME: Reaching outside lib directory is a bad idea
require File.expand_path('../../../../guides/rails_guides', __FILE__)
RailsGuides::Generator.new(Rails.root.join("doc/guides")).generate
end
-
- namespace :plugins do
- # Define doc tasks for each plugin
- plugins.each do |plugin|
- # desc "Generate documentation for the #{plugin} plugin"
- task(plugin => :environment) do
- plugin_base = "vendor/plugins/#{plugin}"
- options = []
- files = Rake::FileList.new
- options << "-o doc/plugins/#{plugin}"
- options << "--title '#{plugin.titlecase} Plugin Documentation'"
- options << '--line-numbers'
- options << '--charset' << 'utf-8'
- options << '-T html'
-
- files.include("#{plugin_base}/lib/**/*.rb")
- if File.exist?("#{plugin_base}/README")
- files.include("#{plugin_base}/README")
- options << "--main '#{plugin_base}/README'"
- end
- files.include("#{plugin_base}/CHANGELOG") if File.exist?("#{plugin_base}/CHANGELOG")
-
- options << files.to_s
-
- sh %(rdoc #{options * ' '})
- end
- end
- end
end
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 40f8c1034a..f684e71267 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -2,6 +2,7 @@ STATS_DIRECTORIES = [
%w(Controllers app/controllers),
%w(Helpers app/helpers),
%w(Models app/models),
+ %w(Mailers app/mailers),
%w(Libraries lib/),
%w(APIs app/apis),
%w(Integration\ tests test/integration),
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 8d0d8cacac..11e4353c87 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -2,31 +2,19 @@
# so fixtures aren't loaded into that environment
abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
-require 'test/unit'
+require 'minitest/autorun'
require 'active_support/test_case'
require 'action_controller/test_case'
require 'action_dispatch/testing/integration'
-if defined?(Test::Unit::Util::BacktraceFilter) && ENV['BACKTRACE'].nil?
- require 'rails/backtrace_cleaner'
- Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit }
-end
-
-if defined?(MiniTest)
- # Enable turn if it is available
- begin
- require 'turn'
-
- if MiniTest::Unit.respond_to?(:use_natural_language_case_names=)
- MiniTest::Unit.use_natural_language_case_names = true
- end
- rescue LoadError
- end
+# Enable turn if it is available
+begin
+ require 'turn'
+ MiniTest::Unit.use_natural_language_case_names = true
+rescue LoadError
end
if defined?(ActiveRecord::Base)
- require 'active_record/test_case'
-
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
self.fixture_path = "#{Rails.root}/test/fixtures/"
diff --git a/railties/lib/rails/test_unit/sub_test_task.rb b/railties/lib/rails/test_unit/sub_test_task.rb
new file mode 100644
index 0000000000..87b6f9b5a4
--- /dev/null
+++ b/railties/lib/rails/test_unit/sub_test_task.rb
@@ -0,0 +1,8 @@
+module Rails
+ # Silence the default description to cut down on `rake -T` noise.
+ class SubTestTask < Rake::TestTask
+ def desc(string)
+ # Ignore the description.
+ end
+ end
+end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 3d87529ad4..55bebb549b 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -1,35 +1,6 @@
require 'rbconfig'
require 'rake/testtask'
-
-# Monkey-patch to silence the description from Rake::TestTask to cut down on rake -T noise
-class TestTaskWithoutDescription < Rake::TestTask
- # Create the tasks defined by this task lib.
- def define
- lib_path = @libs.join(File::PATH_SEPARATOR)
- task @name do
- run_code = ''
- RakeFileUtils.verbose(@verbose) do
- run_code =
- case @loader
- when :direct
- "-e 'ARGV.each{|f| load f}'"
- when :testrb
- "-S testrb #{fix}"
- when :rake
- rake_loader
- end
- @ruby_opts.unshift( "-I\"#{lib_path}\"" )
- @ruby_opts.unshift( "-w" ) if @warning
- ruby @ruby_opts.join(" ") +
- " \"#{run_code}\" " +
- file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
- " #{option_list}"
- end
- end
- self
- end
-end
-
+require 'rails/test_unit/sub_test_task'
TEST_CHANGES_SINCE = Time.now - 600
@@ -74,22 +45,9 @@ end
task :default => :test
-desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile, test:plugins)'
+desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile)'
task :test do
- tests_to_run = ENV['TEST'] ? ["test:single"] : %w(test:units test:functionals test:integration)
- errors = tests_to_run.collect do |task|
- begin
- Rake::Task[task].invoke
- nil
- rescue => e
- { :task => task, :exception => e }
- end
- end.compact
-
- if errors.any?
- puts errors.map { |e| "Errors running #{e[:task]}! #{e[:exception].inspect}" }.join("\n")
- abort
- end
+ Rake::Task[ENV['TEST'] ? 'test:single' : 'test:run'].invoke
end
namespace :test do
@@ -97,6 +55,22 @@ namespace :test do
# Placeholder task for other Railtie and plugins to enhance. See Active Record for an example.
end
+ task :run do
+ errors = %w(test:units test:functionals test:integration).collect do |task|
+ begin
+ Rake::Task[task].invoke
+ nil
+ rescue => e
+ { :task => task, :exception => e }
+ end
+ end.compact
+
+ if errors.any?
+ puts errors.map { |e| "Errors running #{e[:task]}! #{e[:exception].inspect}" }.join("\n")
+ abort
+ end
+ end
+
Rake::TestTask.new(:recent => "test:prepare") do |t|
since = TEST_CHANGES_SINCE
touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
@@ -123,8 +97,7 @@ namespace :test do
unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
-
- unit_tests.uniq + functional_tests.uniq
+ (unit_tests + functional_tests).uniq.select { |file| File.exist?(file) }
end
t.libs << 'test'
@@ -135,39 +108,29 @@ namespace :test do
t.libs << "test"
end
- TestTaskWithoutDescription.new(:units => "test:prepare") do |t|
+ Rails::SubTestTask.new(:units => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/unit/**/*_test.rb'
end
- TestTaskWithoutDescription.new(:functionals => "test:prepare") do |t|
+ Rails::SubTestTask.new(:functionals => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/functional/**/*_test.rb'
end
- TestTaskWithoutDescription.new(:integration => "test:prepare") do |t|
+ Rails::SubTestTask.new(:integration => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
end
- TestTaskWithoutDescription.new(:benchmark => 'test:prepare') do |t|
+ Rails::SubTestTask.new(:benchmark => 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
t.options = '-- --benchmark'
end
- TestTaskWithoutDescription.new(:profile => 'test:prepare') do |t|
+ Rails::SubTestTask.new(:profile => 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
end
-
- TestTaskWithoutDescription.new(:plugins => :environment) do |t|
- t.libs << "test"
-
- if ENV['PLUGIN']
- t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb"
- else
- t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb'
- end
- end
end
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 254227ecf7..ec878f9dcf 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -1,7 +1,7 @@
module Rails
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index b917128885..82655ad394 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -6,13 +6,13 @@ Gem::Specification.new do |s|
s.version = version
s.summary = 'Tools for creating, working with, and running Rails applications.'
s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.'
- s.required_ruby_version = '>= 1.8.7'
+ s.required_ruby_version = '>= 1.9.3'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.files = Dir['CHANGELOG', 'README.rdoc', 'bin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}']
+ s.files = Dir['CHANGELOG.md', 'README.rdoc', 'bin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}']
s.require_path = 'lib'
s.bindir = 'bin'
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index 1c3f8a701a..29ebdc6511 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -1,11 +1,10 @@
require File.expand_path("../../../load_paths", __FILE__)
require 'stringio'
-require 'test/unit'
+require 'minitest/autorun'
require 'fileutils'
require 'active_support'
-require 'active_support/core_ext/logger'
require 'action_controller'
require 'rails/all'
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index 1b99af22a4..a2a7f184b8 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -2,7 +2,7 @@ require 'isolation/abstract_unit'
require 'rack/test'
module ApplicationTests
- class AssetDebuggingTest < Test::Unit::TestCase
+ class AssetDebuggingTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
include Rack::Test::Methods
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index d4ffbe3d66..1469c9af4d 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/kernel/reporting'
require 'rack/test'
module ApplicationTests
- class AssetsTest < Test::Unit::TestCase
+ class AssetsTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
include Rack::Test::Methods
@@ -17,10 +17,6 @@ module ApplicationTests
teardown_app
end
- def app
- @app ||= Rails.application
- end
-
def precompile!
quietly do
Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
@@ -68,11 +64,11 @@ module ApplicationTests
files << Dir["#{app_path}/public/assets/foo/application.js"].first
files.each do |file|
assert_not_nil file, "Expected application.js asset to be generated, but none found"
- assert_equal "alert()", File.read(file)
+ assert_equal "alert();", File.read(file)
end
end
- test "precompile application.js and application.css and all other files not ending with .js or .css by default" do
+ test "precompile application.js and application.css and all other non JS/CSS files" do
app_file "app/assets/javascripts/application.js", "alert();"
app_file "app/assets/stylesheets/application.css", "body{}"
@@ -82,8 +78,11 @@ module ApplicationTests
app_file "app/assets/javascripts/something.min.js", "alert();"
app_file "app/assets/stylesheets/something.min.css", "body{}"
+ app_file "app/assets/javascripts/something.else.js.erb", "alert();"
+ app_file "app/assets/stylesheets/something.else.css.erb", "body{}"
+
images_should_compile = ["a.png", "happyface.png", "happy_face.png", "happy.face.png",
- "happy-face.png", "happy.happy_face.png", "happy_happy.face.png",
+ "happy-face.png", "happy.happy_face.png", "happy_happy.face.png",
"happy.happy.face.png", "happy", "happy.face", "-happyface",
"-happy.png", "-happy.face.png", "_happyface", "_happy.face.png",
"_happy.png"]
@@ -106,6 +105,9 @@ module ApplicationTests
assert !File.exists?("#{app_path}/public/assets/something.min.js")
assert !File.exists?("#{app_path}/public/assets/something.min.css")
+
+ assert !File.exists?("#{app_path}/public/assets/something.else.js")
+ assert !File.exists?("#{app_path}/public/assets/something.else.css")
end
test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
@@ -213,7 +215,9 @@ module ApplicationTests
app_file "app/assets/javascripts/app.js", "alert();"
require "#{app_path}/config/environment"
- class ::PostsController < ActionController::Base ; end
+ class ::PostsController < ActionController::Base
+ def show_detailed_exceptions?() true end
+ end
get '/posts'
assert_match(/AssetNotPrecompiledError/, last_response.body)
@@ -310,7 +314,7 @@ module ApplicationTests
Dir.chdir(app_path){ `bundle exec rake assets:clean` }
end
- files = Dir["#{app_path}/public/assets/**/*", "#{app_path}/tmp/cache/*"]
+ files = Dir["#{app_path}/public/assets/**/*", "#{app_path}/tmp/cache/assets/*"]
assert_equal 0, files.length, "Expected no assets, but found #{files.join(', ')}"
end
@@ -419,6 +423,12 @@ module ApplicationTests
assert_equal "NoPost;\n", File.read("#{app_path}/public/assets/application.js")
end
+ test "initialization on the assets group should set assets_dir" do
+ require "#{app_path}/config/application"
+ Rails.application.initialize!(:assets)
+ assert_not_nil Rails.application.config.action_controller.assets_dir
+ end
+
test "enhancements to assets:precompile should only run once" do
app_file "lib/tasks/enhance.rake", "Rake::Task['assets:precompile'].enhance { puts 'enhancement' }"
output = precompile!
@@ -451,6 +461,49 @@ module ApplicationTests
assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists"
end
+ test "asset urls should use the request's protocol by default" do
+ app_with_assets_in_view
+ add_to_config "config.asset_host = 'example.com'"
+ require "#{app_path}/config/environment"
+ class ::PostsController < ActionController::Base; end
+
+ get '/posts', {}, {'HTTPS'=>'off'}
+ assert_match('src="http://example.com/assets/application.js', last_response.body)
+ get '/posts', {}, {'HTTPS'=>'on'}
+ assert_match('src="https://example.com/assets/application.js', last_response.body)
+ end
+
+ test "asset urls should be protocol-relative if no request is in scope" do
+ app_file "app/assets/javascripts/image_loader.js.erb", 'var src="<%= image_path("rails.png") %>";'
+ add_to_config "config.assets.precompile = %w{image_loader.js}"
+ add_to_config "config.asset_host = 'example.com'"
+ precompile!
+
+ assert_match 'src="//example.com/assets/rails.png"', File.read("#{app_path}/public/assets/image_loader.js")
+ end
+
+ test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do
+ ENV["RAILS_RELATIVE_URL_ROOT"] = "/sub/uri"
+
+ app_file "app/assets/javascripts/app.js.erb", 'var src="<%= image_path("rails.png") %>";'
+ add_to_config "config.assets.precompile = %w{app.js}"
+ precompile!
+
+ assert_match 'src="/sub/uri/assets/rails.png"', File.read("#{app_path}/public/assets/app.js")
+ end
+
+ test "assets:cache:clean should clean cache" do
+ ENV["RAILS_ENV"] = "production"
+ precompile!
+
+ quietly do
+ Dir.chdir(app_path){ `bundle exec rake assets:cache:clean` }
+ end
+
+ require "#{app_path}/config/environment"
+ assert_equal 0, Dir.entries(Rails.application.assets.cache.cache_path).size - 2 # reject [".", ".."]
+ end
+
private
def app_with_assets_in_view
diff --git a/railties/test/application/build_original_fullpath_test.rb b/railties/test/application/build_original_fullpath_test.rb
new file mode 100644
index 0000000000..647ffb097a
--- /dev/null
+++ b/railties/test/application/build_original_fullpath_test.rb
@@ -0,0 +1,27 @@
+require "abstract_unit"
+
+module ApplicationTests
+ class BuildOriginalPathTest < ActiveSupport::TestCase
+ def test_include_original_PATH_info_in_ORIGINAL_FULLPATH
+ env = { 'PATH_INFO' => '/foo/' }
+ assert_equal "/foo/", Rails.application.send(:build_original_fullpath, env)
+ end
+
+ def test_include_SCRIPT_NAME
+ env = {
+ 'SCRIPT_NAME' => '/foo',
+ 'PATH_INFO' => '/bar'
+ }
+
+ assert_equal "/foo/bar", Rails.application.send(:build_original_fullpath, env)
+ end
+
+ def test_include_QUERY_STRING
+ env = {
+ 'PATH_INFO' => '/foo',
+ 'QUERY_STRING' => 'bar',
+ }
+ assert_equal "/foo?bar", Rails.application.send(:build_original_fullpath, env)
+ end
+ end
+end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index f356805d6e..3dffd1c74c 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -1,4 +1,5 @@
require "isolation/abstract_unit"
+require 'rack/test'
class ::MyMailInterceptor
def self.delivering_email(email); email; end
@@ -13,8 +14,9 @@ end
class ::MyOtherMailObserver < ::MyMailObserver; end
module ApplicationTests
- class ConfigurationTest < Test::Unit::TestCase
+ class ConfigurationTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
def new_app
File.expand_path("#{app_path}/../new_app")
@@ -181,20 +183,14 @@ module ApplicationTests
assert !$prepared
require "#{app_path}/config/environment"
- require 'rack/test'
- extend Rack::Test::Methods
get "/"
assert $prepared
end
def assert_utf8
- if RUBY_VERSION < '1.9'
- assert_equal "UTF8", $KCODE
- else
- assert_equal Encoding::UTF_8, Encoding.default_external
- assert_equal Encoding::UTF_8, Encoding.default_internal
- end
+ assert_equal Encoding::UTF_8, Encoding.default_external
+ assert_equal Encoding::UTF_8, Encoding.default_internal
end
test "skipping config.encoding still results in 'utf-8' as the default" do
@@ -286,6 +282,19 @@ module ApplicationTests
assert_equal res, last_response.body # value should be unchanged
end
+ test "sets ActionDispatch.test_app" do
+ make_basic_app
+ assert_equal Rails.application, ActionDispatch.test_app
+ end
+
+ test "sets ActionDispatch::Response.default_charset" do
+ make_basic_app do |app|
+ app.config.action_dispatch.default_charset = "utf-16"
+ end
+
+ assert_equal "utf-16", ActionDispatch::Response.default_charset
+ end
+
test "sets all Active Record models to whitelist all attributes by default" do
add_to_config <<-RUBY
config.active_record.whitelist_attributes = true
@@ -483,14 +492,12 @@ module ApplicationTests
RUBY
add_to_config <<-RUBY
- routes.append do
+ routes.prepend do
resources :posts
end
RUBY
require "#{app_path}/config/environment"
- require "rack/test"
- extend Rack::Test::Methods
post "/posts.json", '{ "title": "foo", "name": "bar" }', "CONTENT_TYPE" => "application/json"
assert_equal '{"title"=>"foo"}', last_response.body
@@ -521,9 +528,16 @@ module ApplicationTests
make_basic_app
assert_respond_to app, :env_config
- assert_equal app.env_config['action_dispatch.parameter_filter'], app.config.filter_parameters
- assert_equal app.env_config['action_dispatch.secret_token'], app.config.secret_token
- assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
+ assert_equal app.env_config['action_dispatch.parameter_filter'], app.config.filter_parameters
+ assert_equal app.env_config['action_dispatch.secret_token'], app.config.secret_token
+ assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
+ assert_equal app.env_config['action_dispatch.logger'], Rails.logger
+ assert_equal app.env_config['action_dispatch.backtrace_cleaner'], Rails.backtrace_cleaner
+ end
+
+ test "config.colorize_logging default is true" do
+ make_basic_app
+ assert app.config.colorize_logging
end
end
end
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 1528d5dd87..f372afa51c 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -1,6 +1,6 @@
require 'isolation/abstract_unit'
-class ConsoleTest < Test::Unit::TestCase
+class ConsoleTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -18,16 +18,20 @@ class ConsoleTest < Test::Unit::TestCase
Rails.application.load_console
end
+ def irb_context
+ Object.new.extend(Rails::ConsoleMethods)
+ end
+
def test_app_method_should_return_integration_session
TestHelpers::Rack.send :remove_method, :app
load_environment
- console_session = app
+ console_session = irb_context.app
assert_instance_of ActionDispatch::Integration::Session, console_session
end
def test_new_session_should_return_integration_session
load_environment
- session = new_session
+ session = irb_context.new_session
assert_instance_of ActionDispatch::Integration::Session, session
end
@@ -41,7 +45,7 @@ class ConsoleTest < Test::Unit::TestCase
ActionDispatch::Reloader.to_prepare { c = 3 }
# Hide Reloading... output
- silence_stream(STDOUT) { reload! }
+ silence_stream(STDOUT) { irb_context.reload! }
assert_equal 1, a
assert_equal 2, b
@@ -57,7 +61,6 @@ class ConsoleTest < Test::Unit::TestCase
load_environment
assert User.new.respond_to?(:name)
- assert !User.new.respond_to?(:age)
app_file "app/models/user.rb", <<-MODEL
class User
@@ -66,12 +69,13 @@ class ConsoleTest < Test::Unit::TestCase
MODEL
assert !User.new.respond_to?(:age)
- silence_stream(STDOUT) { reload! }
+ silence_stream(STDOUT) { irb_context.reload! }
assert User.new.respond_to?(:age)
end
def test_access_to_helpers
load_environment
+ helper = irb_context.helper
assert_not_nil helper
assert_instance_of ActionView::Base, helper
assert_equal 'Once upon a time in a world...',
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 4365d00b1f..bf58bb3f74 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class GeneratorsTest < Test::Unit::TestCase
+ class GeneratorsTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/initializers/boot_test.rb b/railties/test/application/initializers/boot_test.rb
index b1e01dc13f..04c46058cb 100644
--- a/railties/test/application/initializers/boot_test.rb
+++ b/railties/test/application/initializers/boot_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class GemBooting < Test::Unit::TestCase
+ class GemBooting < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/initializers/check_ruby_version_test.rb b/railties/test/application/initializers/check_ruby_version_test.rb
deleted file mode 100644
index df7e9696a9..0000000000
--- a/railties/test/application/initializers/check_ruby_version_test.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require "isolation/abstract_unit"
-
-module ApplicationTests
- class CheckRubyVersionTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Isolation
-
- def setup
- build_app
- boot_rails
- end
-
- def teardown
- teardown_app
- end
-
- test "rails initializes with ruby 1.8.7 or later, except for 1.9.1" do
- if RUBY_VERSION < '1.8.7'
- assert_rails_does_not_boot
- elsif RUBY_VERSION == '1.9.1'
- assert_rails_does_not_boot
- else
- assert_rails_boots
- end
- end
-
- def assert_rails_boots
- assert_nothing_raised "It appears that rails does not boot" do
- require "rails/all"
- end
- end
-
- def assert_rails_does_not_boot
- $stderr = File.open("/dev/null", "w")
- assert_raises(SystemExit) do
- require "rails/all"
- end
- end
- end
-end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 446c85d65a..8812685620 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class FrameworksTest < Test::Unit::TestCase
+ class FrameworksTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -136,6 +136,13 @@ module ApplicationTests
assert_equal 2, ActionDispatch::Http::URL.tld_length
end
+ test "assignment config.encoding to default_charset" do
+ charset = 'Shift_JIS'
+ add_to_config "config.encoding = '#{charset}'"
+ require "#{app_path}/config/environment"
+ assert_equal charset, ActionDispatch::Response.default_charset
+ end
+
# AS
test "if there's no config.active_support.bare, all of ActiveSupport is required" do
use_frameworks []
diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb
index 8c7726339c..7b3e6844fd 100644
--- a/railties/test/application/initializers/hooks_test.rb
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class InitializersTest < Test::Unit::TestCase
+ class InitializersTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
index 8c2c079fb8..abb277dc1d 100644
--- a/railties/test/application/initializers/i18n_test.rb
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class I18nTest < Test::Unit::TestCase
+ class I18nTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -120,6 +120,9 @@ en:
get "/i18n"
assert_equal "1", last_response.body
+ # Wait a full second so we have time for changes to propagate
+ sleep(1)
+
app_file "config/locales/en.yml", <<-YAML
en:
foo: "2"
diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb
index 644b8208a9..31811e7f92 100644
--- a/railties/test/application/initializers/load_path_test.rb
+++ b/railties/test/application/initializers/load_path_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class LoadPathTest < Test::Unit::TestCase
+ class LoadPathTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb
index b72c14eaf0..d866a63fe0 100644
--- a/railties/test/application/initializers/notifications_test.rb
+++ b/railties/test/application/initializers/notifications_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class NotificationsTest < Test::Unit::TestCase
+ class NotificationsTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 47c6fd5c6e..5ad51f8476 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -1,6 +1,6 @@
require 'isolation/abstract_unit'
-class LoadingTest < Test::Unit::TestCase
+class LoadingTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -16,7 +16,7 @@ class LoadingTest < Test::Unit::TestCase
@app ||= Rails.application
end
- def test_constants_in_app_are_autoloaded
+ test "constants in app are autoloaded" do
app_file "app/models/post.rb", <<-MODEL
class Post < ActiveRecord::Base
validates_acceptance_of :title, :accept => "omg"
@@ -33,7 +33,7 @@ class LoadingTest < Test::Unit::TestCase
assert_equal 'omg', p.title
end
- def test_models_without_table_do_not_panic_on_scope_definitions_when_loaded
+ test "models without table do not panic on scope definitions when loaded" do
app_file "app/models/user.rb", <<-MODEL
class User < ActiveRecord::Base
default_scope where(:published => true)
@@ -63,9 +63,10 @@ class LoadingTest < Test::Unit::TestCase
assert ::AppTemplate::Application.config.loaded
end
- def test_descendants_are_cleaned_on_each_request_without_cache_classes
+ test "descendants loaded after framework initialization are cleaned on each request without cache classes" do
add_to_config <<-RUBY
config.cache_classes = false
+ config.reload_classes_only_on_change = false
RUBY
app_file "app/models/post.rb", <<-MODEL
@@ -86,11 +87,11 @@ class LoadingTest < Test::Unit::TestCase
require "#{rails_root}/config/environment"
setup_ar!
- assert_equal [], ActiveRecord::Base.descendants
+ assert_equal [ActiveRecord::SchemaMigration], ActiveRecord::Base.descendants
get "/load"
- assert_equal [Post], ActiveRecord::Base.descendants
+ assert_equal [ActiveRecord::SchemaMigration, Post], ActiveRecord::Base.descendants
get "/unload"
- assert_equal [], ActiveRecord::Base.descendants
+ assert_equal [ActiveRecord::SchemaMigration], ActiveRecord::Base.descendants
end
test "initialize_cant_be_called_twice" do
@@ -98,6 +99,162 @@ class LoadingTest < Test::Unit::TestCase
assert_raise(RuntimeError) { ::AppTemplate::Application.initialize! }
end
+ test "reload constants on development" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match '/c', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 1; end
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 2; end
+ end
+ MODEL
+
+ get "/c"
+ assert_equal "2", last_response.body
+ end
+
+ test "does not reload constants on development if custom file watcher always returns false" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ config.file_watcher = Class.new do
+ def initialize(*); end
+ def updated?; false; end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match '/c', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 1; end
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ def self.counter; 2; end
+ end
+ MODEL
+
+ get "/c"
+ assert_equal "1", last_response.body
+ end
+
+ test "added files (like db/schema.rb) also trigger reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ $counter = 0
+ AppTemplate::Application.routes.draw do
+ match '/c', :to => lambda { |env| User; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ end
+ RUBY
+
+ app_file "app/models/user.rb", <<-MODEL
+ class User
+ $counter += 1
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ require "#{rails_root}/config/environment"
+
+ get "/c"
+ assert_equal "1", last_response.body
+
+ app_file "db/schema.rb", ""
+
+ get "/c"
+ assert_equal "2", last_response.body
+ end
+
+ test "columns migrations also trigger reloading" do
+ add_to_config <<-RUBY
+ config.cache_classes = false
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match '/title', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.title]] }
+ match '/body', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.body]] }
+ end
+ RUBY
+
+ app_file "app/models/post.rb", <<-MODEL
+ class Post < ActiveRecord::Base
+ end
+ MODEL
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ app_file "db/migrate/1_create_posts.rb", <<-MIGRATION
+ class CreatePosts < ActiveRecord::Migration
+ def change
+ create_table :posts do |t|
+ t.string :title, :default => "TITLE"
+ end
+ end
+ end
+ MIGRATION
+
+ Dir.chdir(app_path) { `rake db:migrate`}
+ require "#{rails_root}/config/environment"
+
+ get "/title"
+ assert_equal "TITLE", last_response.body
+
+ app_file "db/migrate/2_add_body_to_posts.rb", <<-MIGRATION
+ class AddBodyToPosts < ActiveRecord::Migration
+ def change
+ add_column :posts, :body, :text, :default => "BODY"
+ end
+ end
+ MIGRATION
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+
+ get "/body"
+ assert_equal "BODY", last_response.body
+ end
+
protected
def setup_ar!
diff --git a/railties/test/application/middleware/best_practices_test.rb b/railties/test/application/middleware/best_practices_test.rb
index 1c88b9bf06..f6783c6ca2 100644
--- a/railties/test/application/middleware/best_practices_test.rb
+++ b/railties/test/application/middleware/best_practices_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class BestPracticesTest < Test::Unit::TestCase
+ class BestPracticesTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
index 050a2161ae..561b020707 100644
--- a/railties/test/application/middleware/cache_test.rb
+++ b/railties/test/application/middleware/cache_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class RoutingTest < Test::Unit::TestCase
+ class RoutingTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -54,9 +54,9 @@ module ApplicationTests
def test_cache_keeps_if_modified_since
simple_controller
expected = "Wed, 30 May 1984 19:43:31 GMT"
-
+
get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected
-
+
assert_equal 200, last_response.status
assert_equal expected, last_response.body, "cache should have kept If-Modified-Since"
end
diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb
new file mode 100644
index 0000000000..18af7abafc
--- /dev/null
+++ b/railties/test/application/middleware/cookies_test.rb
@@ -0,0 +1,47 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class CookiesTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def new_app
+ File.expand_path("#{app_path}/../new_app")
+ end
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ FileUtils.rm_rf(new_app) if File.directory?(new_app)
+ end
+
+ test 'always_write_cookie is true by default in development' do
+ require 'rails'
+ Rails.env = 'development'
+ require "#{app_path}/config/environment"
+ assert_equal true, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+
+ test 'always_write_cookie is false by default in production' do
+ require 'rails'
+ Rails.env = 'production'
+ require "#{app_path}/config/environment"
+ assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+
+ test 'always_write_cookie can be overrided' do
+ add_to_config <<-RUBY
+ config.action_dispatch.always_write_cookie = false
+ RUBY
+
+ require 'rails'
+ Rails.env = 'development'
+ require "#{app_path}/config/environment"
+ assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie
+ end
+ end
+end
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
new file mode 100644
index 0000000000..a80898092d
--- /dev/null
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -0,0 +1,116 @@
+# encoding: utf-8
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareExceptionsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "show exceptions middleware filter backtrace before logging" do
+ my_middleware = Struct.new(:app) do
+ def call(env)
+ raise "Failure"
+ end
+ end
+
+ app.config.middleware.use my_middleware
+
+ stringio = StringIO.new
+ Rails.logger = Logger.new(stringio)
+
+ get "/"
+ assert_no_match(/action_dispatch/, stringio.string)
+ end
+
+ test "renders active record exceptions as 404" do
+ my_middleware = Struct.new(:app) do
+ def call(env)
+ raise ActiveRecord::RecordNotFound
+ end
+ end
+
+ app.config.middleware.use my_middleware
+
+ get "/"
+ assert_equal 404, last_response.status
+ end
+
+ test "uses custom exceptions app" do
+ add_to_config <<-RUBY
+ config.exceptions_app = lambda do |env|
+ [404, { "Content-Type" => "text/plain" }, ["YOU FAILED BRO"]]
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+
+ get "/foo"
+ assert_equal 404, last_response.status
+ assert_equal "YOU FAILED BRO", last_response.body
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions is not set raises an exception" do
+ app.config.action_dispatch.show_exceptions = false
+
+ assert_raise(ActionController::RoutingError) do
+ get '/foo'
+ end
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions is set shows 404" do
+ app.config.action_dispatch.show_exceptions = true
+
+ assert_nothing_raised(ActionController::RoutingError) do
+ get '/foo'
+ assert_match "The page you were looking for doesn't exist.", last_response.body
+ end
+ end
+
+ test "unspecified route when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ assert_nothing_raised(ActionController::RoutingError) do
+ get '/foo'
+ assert_match "No route matches", last_response.body
+ end
+ end
+
+ test "displays diagnostics message when exception raised in template that contains UTF-8" do
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ app_file 'app/views/foo/index.html.erb', <<-ERB
+ <% raise 'boooom' %>
+ ✓測試テスト시험
+ ERB
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match ':controller(/:action)'
+ end
+ RUBY
+
+ post '/foo', :utf8 => '✓'
+ assert_match(/boooom/, last_response.body)
+ assert_match(/測試テスト시험/, last_response.body)
+ end
+ end
+end
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
index da291f061c..066f0c1c84 100644
--- a/railties/test/application/middleware/remote_ip_test.rb
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class RemoteIpTest < Test::Unit::TestCase
+ class RemoteIpTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
index d2ad2668bb..0591386a87 100644
--- a/railties/test/application/middleware/sendfile_test.rb
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class SendfileTest < Test::Unit::TestCase
+ class SendfileTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
new file mode 100644
index 0000000000..f4e77ee244
--- /dev/null
+++ b/railties/test/application/middleware/session_test.rb
@@ -0,0 +1,30 @@
+# encoding: utf-8
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareSessionTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ test "config.force_ssl sets cookie to secure only" do
+ add_to_config "config.force_ssl = true"
+ require "#{app_path}/config/environment"
+ assert app.config.session_options[:secure], "Expected session to be marked as secure"
+ end
+ end
+end
diff --git a/railties/test/application/middleware/show_exceptions_test.rb b/railties/test/application/middleware/show_exceptions_test.rb
deleted file mode 100644
index e3f27f63c3..0000000000
--- a/railties/test/application/middleware/show_exceptions_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require 'isolation/abstract_unit'
-
-module ApplicationTests
- class ShowExceptionsTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Isolation
-
- def setup
- build_app
- boot_rails
- FileUtils.rm_rf "#{app_path}/config/environments"
- end
-
- def teardown
- teardown_app
- end
-
- def app
- @app ||= Rails.application
- end
-
- test "unspecified route when set action_dispatch.show_exceptions to false" do
- make_basic_app do |app|
- app.config.action_dispatch.show_exceptions = false
- end
-
- assert_raise(ActionController::RoutingError) do
- get '/foo'
- end
- end
-
- test "unspecified route when set action_dispatch.show_exceptions to true" do
- make_basic_app do |app|
- app.config.action_dispatch.show_exceptions = true
- end
-
- assert_nothing_raised(ActionController::RoutingError) do
- get '/foo'
- end
- end
- end
-end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 4703a59326..d0a550d2f0 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -1,8 +1,9 @@
require 'isolation/abstract_unit'
require 'stringio'
+require 'rack/test'
module ApplicationTests
- class MiddlewareTest < Test::Unit::TestCase
+ class MiddlewareTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -33,6 +34,7 @@ module ApplicationTests
"ActionDispatch::RequestId",
"Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods
"ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
"ActionDispatch::RemoteIp",
"Rack::Sendfile",
"ActionDispatch::Reloader",
@@ -74,7 +76,7 @@ module ApplicationTests
add_to_config "config.force_ssl = true"
add_to_config "config.ssl_options = { :host => 'example.com' }"
boot!
-
+
assert_equal AppTemplate::Application.middleware.first.args, [{:host => 'example.com'}]
end
@@ -104,10 +106,11 @@ module ApplicationTests
assert !middleware.include?("ActionDispatch::Static")
end
- test "includes show exceptions even action_dispatch.show_exceptions is disabled" do
+ test "includes exceptions middlewares even if action_dispatch.show_exceptions is disabled" do
add_to_config "config.action_dispatch.show_exceptions = false"
boot!
assert middleware.include?("ActionDispatch::ShowExceptions")
+ assert middleware.include?("ActionDispatch::DebugExceptions")
end
test "removes ActionDispatch::Reloader if cache_classes is true" do
@@ -129,13 +132,13 @@ module ApplicationTests
assert_equal "Rack::Config", middleware.second
end
- test "RAILS_CACHE does not respond to middleware" do
+ test "Rails.cache does not respond to middleware" do
add_to_config "config.cache_store = :memory_store"
boot!
assert_equal "Rack::Runtime", middleware.third
end
- test "RAILS_CACHE does respond to middleware" do
+ test "Rails.cache does respond to middleware" do
boot!
assert_equal "Rack::Runtime", middleware.fourth
end
@@ -191,24 +194,12 @@ module ApplicationTests
assert_equal nil, last_response.headers["Etag"]
end
- # Show exceptions middleware
- test "show exceptions middleware filter backtrace before logging" do
- my_middleware = Struct.new(:app) do
- def call(env)
- raise "Failure"
- end
- end
-
- make_basic_app do |app|
- app.config.middleware.use my_middleware
- end
-
- stringio = StringIO.new
- Rails.logger = Logger.new(stringio)
-
- env = Rack::MockRequest.env_for("/")
+ test "ORIGINAL_FULLPATH is passed to env" do
+ boot!
+ env = ::Rack::MockRequest.env_for("/foo/?something")
Rails.application.call(env)
- assert_no_match(/action_dispatch/, stringio.string)
+
+ assert_equal "/foo/?something", env["ORIGINAL_FULLPATH"]
end
private
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
index 964cff48cd..4029984ce9 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class PathsTest < Test::Unit::TestCase
+ class PathsTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -15,7 +15,6 @@ module ApplicationTests
app.config.session_store nil
end
RUBY
- use_frameworks [:action_controller, :action_view, :action_mailer, :active_record]
require "#{app_path}/config/environment"
@paths = Rails.application.config.paths
end
@@ -46,7 +45,6 @@ module ApplicationTests
assert_path @paths["app/views"], "app/views"
assert_path @paths["lib"], "lib"
assert_path @paths["vendor"], "vendor"
- assert_path @paths["vendor/plugins"], "vendor/plugins"
assert_path @paths["tmp"], "tmp"
assert_path @paths["config"], "config"
assert_path @paths["config/locales"], "config/locales/en.yml"
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index 387eb25525..a77c6f472c 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -4,7 +4,7 @@ require "rack/test"
module ApplicationTests
module RackTests
- class LoggerTest < Test::Unit::TestCase
+ class LoggerTest < ActiveSupport::TestCase
include ActiveSupport::LogSubscriber::TestHelper
include Rack::Test::Methods
diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb
index ff9cdcadc7..49ac9fc66c 100644
--- a/railties/test/application/rackup_test.rb
+++ b/railties/test/application/rackup_test.rb
@@ -1,12 +1,12 @@
require "isolation/abstract_unit"
module ApplicationTests
- class RackupTest < Test::Unit::TestCase
+ class RackupTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def rackup
require "rack"
- app, options = Rack::Builder.parse_file("#{app_path}/config.ru")
+ app, _ = Rack::Builder.parse_file("#{app_path}/config.ru")
app
end
diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb
new file mode 100644
index 0000000000..c94334f189
--- /dev/null
+++ b/railties/test/application/rake/migrations_test.rb
@@ -0,0 +1,157 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeMigrationsTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test 'running migrations with given scope' do
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ end
+ app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION
+ class AMigration < ActiveRecord::Migration
+ end
+ MIGRATION
+
+ output = Dir.chdir(app_path) { `rake db:migrate SCOPE=bukkits` }
+ assert_no_match(/create_table\(:users\)/, output)
+ assert_no_match(/CreateUsers/, output)
+ assert_no_match(/add_column\(:users, :email, :string\)/, output)
+
+ assert_match(/AMigration: migrated/, output)
+
+ output = Dir.chdir(app_path) { `rake db:migrate SCOPE=bukkits VERSION=0` }
+ assert_no_match(/drop_table\(:users\)/, output)
+ assert_no_match(/CreateUsers/, output)
+ assert_no_match(/remove_column\(:users, :email\)/, output)
+
+ assert_match(/AMigration: reverted/, output)
+ end
+
+ test 'model and migration generator with change syntax' do
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ output = Dir.chdir(app_path){ `rake db:migrate` }
+ assert_match(/create_table\(:users\)/, output)
+ assert_match(/CreateUsers: migrated/, output)
+ assert_match(/add_column\(:users, :email, :string\)/, output)
+ assert_match(/AddEmailToUsers: migrated/, output)
+
+ output = Dir.chdir(app_path){ `rake db:rollback STEP=2` }
+ assert_match(/drop_table\("users"\)/, output)
+ assert_match(/CreateUsers: reverted/, output)
+ assert_match(/remove_column\("users", :email\)/, output)
+ assert_match(/AddEmailToUsers: reverted/, output)
+ end
+
+ test 'migration status when schema migrations table is not present' do
+ output = Dir.chdir(app_path){ `rake db:migrate:status` }
+ assert_equal "Schema migrations table does not exist yet.\n", output
+ end
+
+ test 'test migration status' do
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:rollback STEP=1` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+ end
+
+ test 'migration status without timestamps' do
+ add_to_config('config.active_record.timestamped_migrations = false')
+
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:rollback STEP=1` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/down\s+\d{3,}\s+Add email to users/, output)
+ end
+
+ test 'test migration status after rollback and redo' do
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:rollback STEP=2` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/down\s+\d{14}\s+Create users/, output)
+ assert_match(/down\s+\d{14}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:migrate:redo` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{14}\s+Create users/, output)
+ assert_match(/up\s+\d{14}\s+Add email to users/, output)
+ end
+
+ test 'migration status after rollback and redo without timestamps' do
+ add_to_config('config.active_record.timestamped_migrations = false')
+
+ Dir.chdir(app_path) do
+ `rails generate model user username:string password:string`
+ `rails generate migration add_email_to_users email:string`
+ end
+
+ Dir.chdir(app_path) { `rake db:migrate` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:rollback STEP=2` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/down\s+\d{3,}\s+Create users/, output)
+ assert_match(/down\s+\d{3,}\s+Add email to users/, output)
+
+ Dir.chdir(app_path) { `rake db:migrate:redo` }
+ output = Dir.chdir(app_path) { `rake db:migrate:status` }
+
+ assert_match(/up\s+\d{3,}\s+Create users/, output)
+ assert_match(/up\s+\d{3,}\s+Add email to users/, output)
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
new file mode 100644
index 0000000000..4ab20afc47
--- /dev/null
+++ b/railties/test/application/rake/notes_test.rb
@@ -0,0 +1,56 @@
+require "isolation/abstract_unit"
+
+module ApplicationTests
+ module RakeTests
+ class RakeNotesTest < ActiveSupport::TestCase
+ def setup
+ build_app
+ require "rails/all"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test 'notes' do
+
+ app_file "app/views/home/index.html.erb", "<% # TODO: note in erb %>"
+ app_file "app/views/home/index.html.haml", "-# TODO: note in haml"
+ app_file "app/views/home/index.html.slim", "/ TODO: note in slim"
+ app_file "app/assets/javascripts/application.js.coffee", "# TODO: note in coffee"
+ app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby"
+
+ boot_rails
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+
+ Rails.application.load_tasks
+
+ Dir.chdir(app_path) do
+ output = `bundle exec rake notes`
+ lines = output.scan(/\[([0-9\s]+)\]/).flatten
+
+ assert_match /note in erb/, output
+ assert_match /note in haml/, output
+ assert_match /note in slim/, output
+ assert_match /note in ruby/, output
+ assert_match /note in coffee/, output
+
+ assert_equal 5, lines.size
+
+ lines.each do |line_number|
+ assert_equal 4, line_number.size
+ end
+ end
+
+ end
+
+ private
+ def boot_rails
+ super
+ require "#{app_path}/config/environment"
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index c76bc3d526..ff12b3e9fc 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -2,7 +2,7 @@
require "isolation/abstract_unit"
module ApplicationTests
- class RakeTest < Test::Unit::TestCase
+ class RakeTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -63,26 +63,23 @@ module ApplicationTests
def test_rake_test_error_output
Dir.chdir(app_path){ `rake db:migrate` }
- app_file "config/database.yml", <<-RUBY
- development:
- RUBY
-
app_file "test/unit/one_unit_test.rb", <<-RUBY
+ raise 'unit'
RUBY
app_file "test/functional/one_functional_test.rb", <<-RUBY
- raise RuntimeError
+ raise 'functional'
RUBY
app_file "test/integration/one_integration_test.rb", <<-RUBY
- raise RuntimeError
+ raise 'integration'
RUBY
silence_stderr do
- output = Dir.chdir(app_path){ `rake test` }
- assert_match(/Errors running test:units! #<ActiveRecord::AdapterNotSpecified/, output)
- assert_match(/Errors running test:functionals! #<RuntimeError/, output)
- assert_match(/Errors running test:integration! #<RuntimeError/, output)
+ output = Dir.chdir(app_path) { `rake test 2>&1` }
+ assert_match 'unit', output
+ assert_match 'functional', output
+ assert_match 'integration', output
end
end
@@ -108,74 +105,6 @@ module ApplicationTests
assert_match "Sample log message", output
end
- def test_model_and_migration_generator_with_change_syntax
- Dir.chdir(app_path) do
- `rails generate model user username:string password:string`
- `rails generate migration add_email_to_users email:string`
- end
-
- output = Dir.chdir(app_path){ `rake db:migrate` }
- assert_match(/create_table\(:users\)/, output)
- assert_match(/CreateUsers: migrated/, output)
- assert_match(/add_column\(:users, :email, :string\)/, output)
- assert_match(/AddEmailToUsers: migrated/, output)
-
- output = Dir.chdir(app_path){ `rake db:rollback STEP=2` }
- assert_match(/drop_table\("users"\)/, output)
- assert_match(/CreateUsers: reverted/, output)
- assert_match(/remove_column\("users", :email\)/, output)
- assert_match(/AddEmailToUsers: reverted/, output)
- end
-
- def test_migration_status_when_schema_migrations_table_is_not_present
- output = Dir.chdir(app_path){ `rake db:migrate:status` }
- assert_equal "Schema migrations table does not exist yet.\n", output
- end
-
- def test_migration_status
- Dir.chdir(app_path) do
- `rails generate model user username:string password:string`
- `rails generate migration add_email_to_users email:string`
- end
-
- Dir.chdir(app_path) { `rake db:migrate`}
- output = Dir.chdir(app_path) { `rake db:migrate:status` }
-
- assert_match(/up\s+\d{14}\s+Create users/, output)
- assert_match(/up\s+\d{14}\s+Add email to users/, output)
-
- Dir.chdir(app_path) { `rake db:rollback STEP=1` }
- output = Dir.chdir(app_path) { `rake db:migrate:status` }
-
- assert_match(/up\s+\d{14}\s+Create users/, output)
- assert_match(/down\s+\d{14}\s+Add email to users/, output)
- end
-
- def test_migration_status_after_rollback_and_redo
- Dir.chdir(app_path) do
- `rails generate model user username:string password:string`
- `rails generate migration add_email_to_users email:string`
- end
-
- Dir.chdir(app_path) { `rake db:migrate`}
- output = Dir.chdir(app_path) { `rake db:migrate:status` }
-
- assert_match(/up\s+\d{14}\s+Create users/, output)
- assert_match(/up\s+\d{14}\s+Add email to users/, output)
-
- Dir.chdir(app_path) { `rake db:rollback STEP=2` }
- output = Dir.chdir(app_path) { `rake db:migrate:status` }
-
- assert_match(/down\s+\d{14}\s+Create users/, output)
- assert_match(/down\s+\d{14}\s+Add email to users/, output)
-
- Dir.chdir(app_path) { `rake db:migrate:redo` }
- output = Dir.chdir(app_path) { `rake db:migrate:status` }
-
- assert_match(/up\s+\d{14}\s+Create users/, output)
- assert_match(/up\s+\d{14}\s+Add email to users/, output)
- end
-
def test_loading_specific_fixtures
Dir.chdir(app_path) do
`rails generate model user username:string password:string`
@@ -199,7 +128,15 @@ module ApplicationTests
`bundle exec rake db:migrate db:test:clone test`
end
- assert_match(/7 tests, 10 assertions, 0 failures, 0 errors/, content)
+ assert_match(/\d+ tests, \d+ assertions, 0 failures, 0 errors/, content)
+ end
+
+ def test_rake_dump_structure_should_respect_db_structure_env_variable
+ Dir.chdir(app_path) do
+ `bundle exec rake db:migrate` # ensure we have a schema_migrations table to dump
+ `bundle exec rake db:structure:dump DB_STRUCTURE=db/my_structure.sql`
+ end
+ assert File.exists?(File.join(app_path, 'db', 'my_structure.sql'))
end
end
end
diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/route_inspect_test.rb
index 78980705ed..7c0a379112 100644
--- a/railties/test/application/route_inspect_test.rb
+++ b/railties/test/application/route_inspect_test.rb
@@ -1,12 +1,43 @@
-require 'test/unit'
+require 'minitest/autorun'
require 'rails/application/route_inspector'
require 'action_controller'
+require 'rails/engine'
module ApplicationTests
- class RouteInspectTest < Test::Unit::TestCase
+ class RouteInspectTest < ActiveSupport::TestCase
def setup
@set = ActionDispatch::Routing::RouteSet.new
@inspector = Rails::Application::RouteInspector.new
+ app = ActiveSupport::OrderedOptions.new
+ app.config = ActiveSupport::OrderedOptions.new
+ app.config.assets = ActiveSupport::OrderedOptions.new
+ app.config.assets.prefix = '/sprockets'
+ Rails.stubs(:application).returns(app)
+ end
+
+ def test_displaying_routes_for_engines
+ engine = Class.new(Rails::Engine) do
+ def self.to_s
+ "Blog::Engine"
+ end
+ end
+ engine.routes.draw do
+ get '/cart', :to => 'cart#show'
+ end
+
+ @set.draw do
+ get '/custom/assets', :to => 'custom_assets#show'
+ mount engine => "/blog", :as => "blog"
+ end
+
+ output = @inspector.format @set.routes
+ expected = [
+ "custom_assets GET /custom/assets(.:format) custom_assets#show",
+ " blog /blog Blog::Engine",
+ "\nRoutes for Blog::Engine:",
+ "cart GET /cart(.:format) cart#show"
+ ]
+ assert_equal expected, output
end
def test_cart_inspect
@@ -101,5 +132,31 @@ module ApplicationTests
output = @inspector.format @set.routes
assert_equal [" /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}"], output
end
+
+ def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints
+ constraint = Class.new do
+ def to_s
+ "( my custom constraint )"
+ end
+ end
+
+ @set.draw do
+ scope :constraint => constraint.new do
+ mount RackApp => '/foo'
+ end
+ end
+
+ output = @inspector.format @set.routes
+ assert_equal [" /foo #{RackApp.name} {:constraint=>( my custom constraint )}"], output
+ end
+
+ def test_rake_routes_dont_show_app_mounted_in_assets_prefix
+ @set.draw do
+ match '/sprockets' => RackApp
+ end
+ output = @inspector.format @set.routes
+ assert_no_match(/RackApp/, output.first)
+ assert_no_match(/\/sprockets/, output.first)
+ end
end
end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index a05e39658d..28ce3beea9 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -2,7 +2,7 @@ require 'isolation/abstract_unit'
require 'rack/test'
module ApplicationTests
- class RoutingTest < Test::Unit::TestCase
+ class RoutingTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
include Rack::Test::Methods
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 4468fa295e..e1d283a7fd 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class RunnerTest < Test::Unit::TestCase
+ class RunnerTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
index 27a7959e84..f6bcaa466a 100644
--- a/railties/test/application/test_test.rb
+++ b/railties/test/application/test_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class TestTest < Test::Unit::TestCase
+ class TestTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
@@ -24,24 +24,7 @@ module ApplicationTests
end
RUBY
- run_test 'unit/foo_test.rb'
- end
-
- # Run just in Ruby < 1.9
- if defined?(Test::Unit::Util::BacktraceFilter)
- test "adds backtrace cleaner" do
- app_file 'test/unit/backtrace_test.rb', <<-RUBY
- require 'test_helper'
-
- class FooTest < ActiveSupport::TestCase
- def test_truth
- assert Test::Unit::Util::BacktraceFilter.ancestors.include?(Rails::BacktraceFilterForTestUnit)
- end
- end
- RUBY
-
- run_test 'unit/backtrace_test.rb'
- end
+ run_test_file 'unit/foo_test.rb'
end
test "integration test" do
@@ -66,7 +49,7 @@ module ApplicationTests
end
RUBY
- run_test 'integration/posts_test.rb'
+ run_test_file 'integration/posts_test.rb'
end
test "performance test" do
@@ -91,11 +74,11 @@ module ApplicationTests
end
RUBY
- run_test 'performance/posts_test.rb'
+ run_test_file 'performance/posts_test.rb'
end
private
- def run_test(name)
+ def run_test_file(name)
result = ruby '-Itest', "#{app_path}/test/#{name}"
assert_equal 0, $?.to_i, result
end
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
index 2b6ec26cd0..85a8a15fcc 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class UrlGenerationTest < Test::Unit::TestCase
+ class UrlGenerationTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def app
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index 80077378db..cbe7d35f6d 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -1,34 +1,6 @@
require 'abstract_unit'
require 'rails/backtrace_cleaner'
-if defined? Test::Unit::Util::BacktraceFilter
- class TestWithBacktrace
- include Test::Unit::Util::BacktraceFilter
- include Rails::BacktraceFilterForTestUnit
- end
-
- class BacktraceCleanerFilterTest < ActiveSupport::TestCase
- def setup
- @test = TestWithBacktrace.new
- @backtrace = [ './test/rails/benchmark_test.rb', './test/rails/dependencies.rb', '/opt/local/lib/ruby/kernel.rb' ]
- end
-
- test "test with backtrace should use the rails backtrace cleaner to clean" do
- Rails.stubs(:backtrace_cleaner).returns(stub(:clean))
- Rails.backtrace_cleaner.expects(:clean).with(@backtrace, nil)
- @test.send(:filter_backtrace, @backtrace)
- end
-
- test "filter backtrace should have the same arity as Test::Unit::Util::BacktraceFilter" do
- assert_nothing_raised do
- @test.send(:filter_backtrace, @backtrace, '/opt/local/lib')
- end
- end
- end
-else
- $stderr.puts 'No BacktraceFilter for minitest'
-end
-
if defined? Gem
class BacktraceCleanerVendorGemTest < ActiveSupport::TestCase
def setup
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
new file mode 100644
index 0000000000..01847ae58c
--- /dev/null
+++ b/railties/test/commands/console_test.rb
@@ -0,0 +1,87 @@
+require 'abstract_unit'
+require 'rails/commands/console'
+
+class Rails::ConsoleTest < ActiveSupport::TestCase
+ class FakeConsole
+ end
+
+ def setup
+ end
+
+ def test_sandbox_option
+ console = Rails::Console.new(app, ["--sandbox"])
+ assert console.sandbox?
+ end
+
+ def test_short_version_of_sandbox_option
+ console = Rails::Console.new(app, ["-s"])
+ assert console.sandbox?
+ end
+
+ def test_debugger_option
+ console = Rails::Console.new(app, ["--debugger"])
+ assert console.debugger?
+ end
+
+ def test_no_options
+ console = Rails::Console.new(app, [])
+ assert !console.debugger?
+ assert !console.sandbox?
+ end
+
+ def test_start
+ app.expects(:sandbox=).with(nil)
+ FakeConsole.expects(:start)
+
+ start
+
+ assert_match /Loading \w+ environment \(Rails/, output
+ end
+
+ def test_start_with_debugger
+ app.expects(:sandbox=).with(nil)
+ rails_console.expects(:require_debugger).returns(nil)
+ FakeConsole.expects(:start)
+
+ start ["--debugger"]
+ end
+
+ def test_start_with_sandbox
+ app.expects(:sandbox=).with(true)
+ FakeConsole.expects(:start)
+
+ start ["--sandbox"]
+
+ assert_match /Loading \w+ environment in sandbox \(Rails/, output
+ end
+
+ def test_console_defaults_to_IRB
+ config = mock("config", :console => nil)
+ app = mock("app", :config => config)
+ app.expects(:load_console).returns(nil)
+
+ assert_equal IRB, Rails::Console.new(app).console
+ end
+
+ private
+
+ attr_reader :output
+
+ def rails_console
+ @rails_console ||= Rails::Console.new(app)
+ end
+
+ def start(argv = [])
+ rails_console.stubs(:arguments => argv)
+ @output = output = capture(:stdout) { rails_console.start }
+ end
+
+ def app
+ @app ||= begin
+ config = mock("config", :console => FakeConsole)
+ app = mock("app", :config => config)
+ app.expects(:load_console)
+ app
+ end
+ end
+end
diff --git a/railties/test/fixtures/lib/generators/usage_template/USAGE b/railties/test/fixtures/lib/generators/usage_template/USAGE
new file mode 100644
index 0000000000..bcd63c52e2
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/usage_template/USAGE
@@ -0,0 +1 @@
+:: <%= 1 + 1 %> :: \ No newline at end of file
diff --git a/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
new file mode 100644
index 0000000000..078b0f9412
--- /dev/null
+++ b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
@@ -0,0 +1,5 @@
+require 'rails/generators'
+
+class UsageTemplateGenerator < Rails::Generators::Base
+ source_root File.expand_path("templates", File.dirname(__FILE__))
+end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 51fa2fe16f..a8c8fcd5b7 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -9,8 +9,6 @@ class ActionsTest < Rails::Generators::TestCase
def setup
Rails.application = TestApp::Application
super
- @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git'
- @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
end
def teardown
@@ -37,41 +35,6 @@ class ActionsTest < Rails::Generators::TestCase
assert_file 'lib/test_file.rb', 'heres block data'
end
- def test_plugin_with_git_option_should_run_plugin_install
- generator.expects(:run_ruby_script).once.with("script/rails plugin install #{@git_plugin_uri}", :verbose => false)
- action :plugin, 'restful-authentication', :git => @git_plugin_uri
- end
-
- def test_plugin_with_svn_option_should_run_plugin_install
- generator.expects(:run_ruby_script).once.with("script/rails plugin install #{@svn_plugin_uri}", :verbose => false)
- action :plugin, 'restful-authentication', :svn => @svn_plugin_uri
- end
-
- def test_plugin_with_git_option_and_branch_should_run_plugin_install
- generator.expects(:run_ruby_script).once.with("script/rails plugin install -b stable #{@git_plugin_uri}", :verbose => false)
- action :plugin, 'restful-authentication', :git => @git_plugin_uri, :branch => 'stable'
- end
-
- def test_plugin_with_svn_option_and_revision_should_run_plugin_install
- generator.expects(:run_ruby_script).once.with("script/rails plugin install -r 1234 #{@svn_plugin_uri}", :verbose => false)
- action :plugin, 'restful-authentication', :svn => @svn_plugin_uri, :revision => 1234
- end
-
- def test_plugin_with_git_option_and_submodule_should_use_git_scm
- generator.expects(:run).with("git submodule add #{@git_plugin_uri} vendor/plugins/rest_auth", :verbose => false)
- action :plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true
- end
-
- def test_plugin_with_git_option_and_submodule_should_use_git_scm
- generator.expects(:run).with("git submodule add -b stable #{@git_plugin_uri} vendor/plugins/rest_auth", :verbose => false)
- action :plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true, :branch => 'stable'
- end
-
- def test_plugin_with_no_options_should_skip_method
- generator.expects(:run).never
- action :plugin, 'rest_auth', {}
- end
-
def test_add_source_adds_source_to_gemfile
run_generator
action :add_source, 'http://gems.github.com'
@@ -95,11 +58,13 @@ class ActionsTest < Rails::Generators::TestCase
def test_gem_should_insert_on_separate_lines
run_generator
+ File.open('Gemfile', 'a') {|f| f.write('# Some content...') }
+
action :gem, 'rspec'
action :gem, 'rspec-rails'
- assert_file 'Gemfile', /gem "rspec"$/
- assert_file 'Gemfile', /gem "rspec-rails"$/
+ assert_file 'Gemfile', /^gem "rspec"$/
+ assert_file 'Gemfile', /^gem "rspec-rails"$/
end
def test_gem_group_should_wrap_gems_in_a_group
@@ -231,14 +196,14 @@ class ActionsTest < Rails::Generators::TestCase
def test_readme
run_generator
Rails::Generators::AppGenerator.expects(:source_root).times(2).returns(destination_root)
- assert_match(/Welcome to Rails/, action(:readme, "README"))
+ assert_match(/Welcome to Rails/, action(:readme, "README.rdoc"))
end
def test_readme_with_quiet
generator(default_arguments, :quiet => true)
run_generator
Rails::Generators::AppGenerator.expects(:source_root).times(2).returns(destination_root)
- assert_no_match(/Welcome to Rails/, action(:readme, "README"))
+ assert_no_match(/Welcome to Rails/, action(:readme, "README.rdoc"))
end
def test_log
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 955ed09361..a3c24c392b 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -1,4 +1,3 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/app/app_generator'
require 'generators/shared_generator_tests.rb'
@@ -33,7 +32,6 @@ DEFAULT_APP_FILES = %w(
test/unit
vendor
vendor/assets
- vendor/plugins
tmp/cache
tmp/cache/assets
)
@@ -55,6 +53,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "app/views/layouts/application.html.erb", /javascript_include_tag\s+"application"/
assert_file "app/assets/stylesheets/application.css"
assert_file "config/application.rb", /config\.assets\.enabled = true/
+ assert_file "public/index.html", /url\("assets\/rails.png"\);/
end
def test_invalid_application_name_raises_an_error
@@ -123,6 +122,16 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "hats/config/environment.rb", /Hats::Application\.initialize!/
end
+ def test_gemfile_has_no_whitespace_errors
+ run_generator
+ absolute = File.expand_path("Gemfile", destination_root)
+ File.open(absolute, 'r') do |f|
+ f.each_line do |line|
+ assert_no_match %r{/^[ \t]+$/}, line
+ end
+ end
+ end
+
def test_config_database_is_added_by_default
run_generator
assert_file "config/database.yml", /sqlite3/
@@ -193,6 +202,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "--skip-active-record"]
assert_no_file "config/database.yml"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
+ assert_file "config/application.rb", /#\s+config\.active_record\.dependent_restrict_raises = false/
assert_file "test/test_helper.rb" do |helper_content|
assert_no_match(/fixtures :all/, helper_content)
end
@@ -205,9 +215,30 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content)
assert_no_match(/config\.assets\.enabled = true/, content)
end
+ assert_file "Gemfile" do |content|
+ assert_no_match(/sass-rails/, content)
+ assert_no_match(/coffee-rails/, content)
+ assert_no_match(/uglifier/, content)
+ end
+ assert_file "config/environments/development.rb" do |content|
+ assert_no_match(/config\.assets\.debug = true/, content)
+ end
+ assert_file "config/environments/production.rb" do |content|
+ assert_no_match(/config\.assets\.digest = true/, content)
+ assert_no_match(/config\.assets\.compress = true/, content)
+ end
assert_file "test/performance/browsing_test.rb"
end
+ def test_inclusion_of_javascript_runtime
+ run_generator([destination_root])
+ if defined?(JRUBY_VERSION)
+ assert_file "Gemfile", /gem\s+["']therubyrhino["']$/
+ else
+ assert_file "Gemfile", /# gem\s+["']therubyracer["']$/
+ end
+ end
+
def test_creation_of_a_test_directory
run_generator
assert_file 'test'
@@ -252,17 +283,10 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_inclusion_of_ruby_debug
+ def test_inclusion_of_ruby_debug19
run_generator
assert_file "Gemfile" do |contents|
- assert_match(/gem 'ruby-debug'/, contents) if RUBY_VERSION < '1.9'
- end
- end
-
- def test_inclusion_of_ruby_debug19_if_ruby19
- run_generator
- assert_file "Gemfile" do |contents|
- assert_match(/gem 'ruby-debug19', :require => 'ruby-debug'/, contents) unless RUBY_VERSION < '1.9'
+ assert_match(/gem 'ruby-debug19', :require => 'ruby-debug'/, contents)
end
end
@@ -304,30 +328,38 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_new_hash_style
run_generator [destination_root]
assert_file "config/initializers/session_store.rb" do |file|
- if RUBY_VERSION < "1.9"
- assert_match(/config.session_store :cookie_store, :key => '_.+_session'/, file)
- else
- assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file)
- end
+ assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file)
end
end
- def test_force_old_style_hash
- run_generator [destination_root, "--old-style-hash"]
- assert_file "config/initializers/session_store.rb" do |file|
- assert_match(/config.session_store :cookie_store, :key => '_.+_session'/, file)
+ def test_generated_environments_file_for_sanitizer
+ run_generator [destination_root, "--skip-active-record"]
+ %w(development test).each do |env|
+ assert_file "config/environments/#{env}.rb" do |file|
+ assert_no_match(/config.active_record.mass_assignment_sanitizer = :strict/, file)
+ end
end
end
- def test_generated_environments_file_for_sanitizer
+ def test_generated_environments_file_for_auto_explain
run_generator [destination_root, "--skip-active-record"]
- ["config/environments/development.rb", "config/environments/test.rb"].each do |env_file|
- assert_file env_file do |file|
- assert_no_match(/config.active_record.mass_assignment_sanitizer = :strict/, file)
+ %w(development production).each do |env|
+ assert_file "config/environments/#{env}.rb" do |file|
+ assert_no_match %r(auto_explain_threshold_in_seconds), file
end
end
end
+ def test_active_record_dependent_restrict_raises_is_present_application_config
+ run_generator
+ assert_file "config/application.rb", /config\.active_record\.dependent_restrict_raises = false/
+ end
+
+ def test_pretend_option
+ output = run_generator [File.join(destination_root, "myapp"), "--pretend"]
+ assert_no_match(/run bundle install/, output)
+ end
+
protected
def action(*args, &block)
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index a85829085c..6e3fc84781 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -69,7 +69,7 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
end
def test_default_value_for_type
- att = Rails::Generators::GeneratedAttribute.new("type", "string")
+ att = Rails::Generators::GeneratedAttribute.parse("type:string")
assert_equal("", att.default)
end
@@ -122,4 +122,9 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
assert_equal :string, create_generated_attribute(nil, 'title').type
assert_equal :string, create_generated_attribute("", 'title').type
end
+
+ def test_handles_index_names_for_references
+ assert_equal "post", create_generated_attribute('string', 'post').index_name
+ assert_equal "post_id", create_generated_attribute('references', 'post').index_name
+ end
end
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index 139d6b1421..c501780e7f 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -10,11 +10,7 @@ class MailerGeneratorTest < Rails::Generators::TestCase
run_generator
assert_file "app/mailers/notifier.rb" do |mailer|
assert_match(/class Notifier < ActionMailer::Base/, mailer)
- if RUBY_VERSION < "1.9"
- assert_match(/default :from => "from@example.com"/, mailer)
- else
- assert_match(/default from: "from@example.com"/, mailer)
- end
+ assert_match(/default from: "from@example.com"/, mailer)
end
end
@@ -77,33 +73,14 @@ class MailerGeneratorTest < Rails::Generators::TestCase
assert_file "app/mailers/notifier.rb" do |mailer|
assert_instance_method :foo, mailer do |foo|
- if RUBY_VERSION < "1.9"
- assert_match(/mail :to => "to@example.org"/, foo)
- else
- assert_match(/mail to: "to@example.org"/, foo)
- end
+ assert_match(/mail to: "to@example.org"/, foo)
assert_match(/@greeting = "Hi"/, foo)
end
assert_instance_method :bar, mailer do |bar|
- if RUBY_VERSION < "1.9"
- assert_match(/mail :to => "to@example.org"/, bar)
- else
- assert_match(/mail to: "to@example.org"/, bar)
- end
+ assert_match(/mail to: "to@example.org"/, bar)
assert_match(/@greeting = "Hi"/, bar)
end
end
end
-
- def test_force_old_style_hash
- run_generator ["notifier", "foo", "--old-style-hash"]
- assert_file "app/mailers/notifier.rb" do |mailer|
- assert_match(/default :from => "from@example.com"/, mailer)
-
- assert_instance_method :foo, mailer do |foo|
- assert_match(/mail :to => "to@example.org"/, foo)
- end
- end
- end
end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 337257df7d..4e08e5dae1 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -58,6 +58,68 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_add_migration_with_attributes_and_indices
+ migration = "add_title_with_index_and_body_to_posts"
+ run_generator [migration, "title:string:index", "body:text", "user_id:integer:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/add_column :posts, :title, :string/, up)
+ assert_match(/add_column :posts, :body, :text/, up)
+ assert_match(/add_column :posts, :user_id, :integer/, up)
+ end
+ assert_match(/add_index :posts, :title/, content)
+ assert_match(/add_index :posts, :user_id, unique: true/, content)
+ end
+ end
+
+ def test_add_migration_with_attributes_and_wrong_index_declaration
+ migration = "add_title_and_content_to_books"
+ run_generator [migration, "title:string:inex", "content:text", "user_id:integer:unik"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/add_column :books, :title, :string/, up)
+ assert_match(/add_column :books, :content, :text/, up)
+ assert_match(/add_column :books, :user_id, :integer/, up)
+ end
+ assert_no_match(/add_index :books, :title/, content)
+ assert_no_match(/add_index :books, :user_id/, content)
+ end
+ end
+
+ def test_add_migration_with_attributes_without_type_and_index
+ migration = "add_title_with_index_and_body_to_posts"
+ run_generator [migration, "title:index", "body:text", "user_uuid:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/add_column :posts, :title, :string/, up)
+ assert_match(/add_column :posts, :body, :text/, up)
+ assert_match(/add_column :posts, :user_uuid, :string/, up)
+ end
+ assert_match(/add_index :posts, :title/, content)
+ assert_match(/add_index :posts, :user_uuid, unique: true/, content)
+ end
+ end
+
+ def test_add_migration_with_attributes_index_declaration_and_attribute_options
+ migration = "add_title_and_content_to_books"
+ run_generator [migration, "title:string{40}:index", "content:string{255}", "price:decimal{1,2}:index", "discount:decimal{3.4}:uniq"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/add_column :books, :title, :string, limit: 40/, up)
+ assert_match(/add_column :books, :content, :string, limit: 255/, up)
+ assert_match(/add_column :books, :price, :decimal, precision: 1, scale: 2/, up)
+ assert_match(/add_column :books, :discount, :decimal, precision: 3, scale: 4/, up)
+ end
+ assert_match(/add_index :books, :title/, content)
+ assert_match(/add_index :books, :price/, content)
+ assert_match(/add_index :books, :discount, unique: true/, content)
+ end
+ end
+
def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove
migration = "create_books"
run_generator [migration, "title:string", "content:text"]
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 1b0cb425c6..156fa86eee 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -113,6 +113,74 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_migration_with_attributes_and_with_index
+ run_generator ["product", "name:string:index", "supplier_id:integer:index", "user_id:integer:uniq", "order_id:uniq"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+ assert_match(/t\.integer :user_id/, up)
+ assert_match(/t\.string :order_id/, up)
+
+ assert_match(/add_index :products, :name/, up)
+ assert_match(/add_index :products, :supplier_id/, up)
+ assert_match(/add_index :products, :user_id, unique: true/, up)
+ assert_match(/add_index :products, :order_id, unique: true/, up)
+ end
+ end
+ end
+
+ def test_migration_with_attributes_and_with_wrong_index_declaration
+ run_generator ["product", "name:string", "supplier_id:integer:inex", "user_id:integer:unqu"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+ assert_match(/t\.integer :user_id/, up)
+
+ assert_no_match(/add_index :products, :name/, up)
+ assert_no_match(/add_index :products, :supplier_id/, up)
+ assert_no_match(/add_index :products, :user_id/, up)
+ end
+ end
+ end
+
+ def test_migration_with_missing_attribute_type_and_with_index
+ run_generator ["product", "name:index", "supplier_id:integer:index", "year:integer"]
+
+ assert_migration "db/migrate/create_products.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t\.string :name/, up)
+ assert_match(/t\.integer :supplier_id/, up)
+
+ assert_match(/add_index :products, :name/, up)
+ assert_match(/add_index :products, :supplier_id/, up)
+ assert_no_match(/add_index :products, :year/, up)
+ end
+ end
+ end
+
+ def test_add_migration_with_attributes_index_declaration_and_attribute_options
+ run_generator ["product", "title:string{40}:index", "content:string{255}", "price:decimal{5,2}:index", "discount:decimal{5,2}:uniq"]
+
+ assert_migration "db/migrate/create_products.rb" do |content|
+ assert_method :change, content do |up|
+ assert_match(/create_table :products/, up)
+ assert_match(/t.string :title, limit: 40/, up)
+ assert_match(/t.string :content, limit: 255/, up)
+ assert_match(/t.decimal :price, precision: 5, scale: 2/, up)
+ end
+ assert_match(/add_index :products, :title/, content)
+ assert_match(/add_index :products, :price/, content)
+ assert_match(/add_index :products, :discount, unique: true/, content)
+ end
+ end
+
def test_migration_without_timestamps
ActiveRecord::Base.timestamped_migrations = false
run_generator ["account"]
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index f23701e99e..2bc2c33a72 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -108,6 +108,15 @@ class NamedBaseTest < Rails::Generators::TestCase
assert_name g, 'sheep_index', :index_helper
end
+ def test_hide_namespace
+ g = generator ['Hidden']
+ g.class.stubs(:namespace).returns('hidden')
+
+ assert !Rails::Generators.hidden_namespaces.include?('hidden')
+ g.class.hide!
+ assert Rails::Generators.hidden_namespaces.include?('hidden')
+ end
+
protected
def assert_name(generator, value, method)
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index dd1e4bdac1..5c63b13dce 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -155,11 +155,7 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase
assert_file "app/mailers/test_app/notifier.rb" do |mailer|
assert_match(/module TestApp/, mailer)
assert_match(/class Notifier < ActionMailer::Base/, mailer)
- if RUBY_VERSION < "1.9"
- assert_match(/default :from => "from@example.com"/, mailer)
- else
- assert_match(/default from: "from@example.com"/, mailer)
- end
+ assert_match(/default from: "from@example.com"/, mailer)
end
end
diff --git a/railties/test/generators/orm_test.rb b/railties/test/generators/orm_test.rb
new file mode 100644
index 0000000000..9dd3d3e0ec
--- /dev/null
+++ b/railties/test/generators/orm_test.rb
@@ -0,0 +1,38 @@
+require "generators/generators_test_helper"
+require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
+
+# Mock out two ORMs
+module ORMWithGenerators
+ module Generators
+ class ActiveModel
+ def initialize(name)
+ end
+ end
+ end
+end
+
+module ORMWithoutGenerators
+ # No generators
+end
+
+class OrmTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ tests Rails::Generators::ScaffoldControllerGenerator
+
+ def test_orm_class_returns_custom_generator_if_supported_custom_orm_set
+ g = generator ["Foo"], :orm => "ORMWithGenerators"
+ assert_equal ORMWithGenerators::Generators::ActiveModel, g.send(:orm_class)
+ end
+
+ def test_orm_class_returns_rails_generator_if_unsupported_custom_orm_set
+ g = generator ["Foo"], :orm => "ORMWithoutGenerators"
+ assert_equal Rails::Generators::ActiveModel, g.send(:orm_class)
+ end
+
+ def test_orm_instance_returns_orm_class_instance_with_name
+ g = generator ["Foo"]
+ orm_instance = g.send(:orm_instance)
+ assert g.send(:orm_class) === orm_instance
+ assert_equal "foo", orm_instance.name
+ end
+end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 9183945619..4bb5f04a79 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -1,4 +1,3 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/plugin_new/plugin_new_generator'
require 'generators/shared_generator_tests.rb'
@@ -28,13 +27,20 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
include SharedGeneratorTests
def test_invalid_plugin_name_raises_an_error
- content = capture(:stderr){ run_generator [File.join(destination_root, "43-things")] }
- assert_equal "Invalid plugin name 43-things. Please give a name which does not start with numbers.\n", content
+ content = capture(:stderr){ run_generator [File.join(destination_root, "things-43")] }
+ assert_equal "Invalid plugin name things-43. Please give a name which use only alphabetic or numeric or \"_\" characters.\n", content
+
+ content = capture(:stderr){ run_generator [File.join(destination_root, "things4.3")] }
+ assert_equal "Invalid plugin name things4.3. Please give a name which use only alphabetic or numeric or \"_\" characters.\n", content
+
+ content = capture(:stderr){ run_generator [File.join(destination_root, "43things")] }
+ assert_equal "Invalid plugin name 43things. Please give a name which does not start with numbers.\n", content
end
- def test_invalid_plugin_name_is_fixed
- run_generator [File.join(destination_root, "things-43")]
- assert_file "things-43/lib/things-43.rb", /module Things43/
+ def test_camelcase_plugin_name_underscores_filenames
+ run_generator [File.join(destination_root, "CamelCasedName")]
+ assert_no_file "CamelCasedName/lib/CamelCasedName.rb"
+ assert_file "CamelCasedName/lib/camel_cased_name.rb", /module CamelCasedName/
end
def test_generating_without_options
@@ -237,6 +243,14 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_file "test"
end
+ def test_ensure_that_gitignore_can_be_generated_from_a_template_for_dummy_path
+ FileUtils.cd(Rails.root)
+ run_generator([destination_root, "--dummy_path", "spec/dummy" "--skip-test-unit"])
+ assert_file ".gitignore" do |contents|
+ assert_match(/spec\/dummy/, contents)
+ end
+ end
+
def test_skipping_test_unit
run_generator [destination_root, "--skip-test-unit"]
assert_no_file "test"
@@ -250,6 +264,37 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_file "bukkits.gemspec"
end
+ def test_creating_plugin_in_app_directory_adds_gemfile_entry
+ # simulate application existance
+ gemfile_path = "#{Rails.root}/Gemfile"
+ Object.const_set('APP_PATH', Rails.root)
+ FileUtils.touch gemfile_path
+
+ run_generator [destination_root]
+
+ assert_file gemfile_path, /gem 'bukkits', :path => 'tmp\/bukkits'/
+ ensure
+ Object.send(:remove_const, 'APP_PATH')
+ FileUtils.rm gemfile_path
+ end
+
+ def test_skipping_gemfile_entry
+ # simulate application existance
+ gemfile_path = "#{Rails.root}/Gemfile"
+ Object.const_set('APP_PATH', Rails.root)
+ FileUtils.touch gemfile_path
+
+ run_generator [destination_root, "--skip-gemfile-entry"]
+
+ assert_file gemfile_path do |contents|
+ assert_no_match(/gem 'bukkits', :path => 'tmp\/bukkits'/, contents)
+ end
+ ensure
+ Object.send(:remove_const, 'APP_PATH')
+ FileUtils.rm gemfile_path
+ end
+
+
protected
def action(*args, &block)
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 65b30b9fbd..1382133d7b 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -126,18 +126,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_new_hash_style
run_generator
assert_file "app/controllers/users_controller.rb" do |content|
- if RUBY_VERSION < "1.9"
- assert_match(/\{ render :action => "new" \}/, content)
- else
- assert_match(/\{ render action: "new" \}/, content)
- end
- end
- end
-
- def test_force_old_style_hash
- run_generator ["User", "--old-style-hash"]
- assert_file "app/controllers/users_controller.rb" do |content|
- assert_match(/\{ render :action => "new" \}/, content)
+ assert_match(/\{ render action: "new" \}/, content)
end
end
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 1534f0d828..14a20eddb8 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -125,7 +125,7 @@ module SharedGeneratorTests
def test_edge_option
generator([destination_root], :edge => true).expects(:bundle_command).with('install').once
quietly { generator.invoke_all }
- assert_file 'Gemfile', %r{^gem\s+["']rails["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/rails.git")}["']$}
+ assert_file 'Gemfile', %r{^gem\s+["']rails["'],\s+:git\s+=>\s+["']#{Regexp.escape("https://github.com/rails/rails.git")}["']$}
end
def test_skip_gemfile
diff --git a/railties/test/generators/task_generator_test.rb b/railties/test/generators/task_generator_test.rb
new file mode 100644
index 0000000000..f810a21d1e
--- /dev/null
+++ b/railties/test/generators/task_generator_test.rb
@@ -0,0 +1,12 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/task/task_generator'
+
+class TaskGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(feeds foo bar)
+
+ def test_controller_skeleton_is_created
+ run_generator
+ assert_file "lib/tasks/feeds.rake", /namespace :feeds/
+ end
+end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 56329f3183..60e7e57a91 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -201,4 +201,16 @@ class GeneratorsTest < Rails::Generators::TestCase
mspec = Rails::Generators.find_by_namespace :fixjour
assert mspec.source_paths.include?(File.join(Rails.root, "lib", "templates", "fixjour"))
end
+
+ def test_usage_with_embedded_ruby
+ require File.expand_path("fixtures/lib/generators/usage_template/usage_template_generator", File.dirname(__FILE__))
+ output = capture(:stdout) { Rails::Generators.invoke :usage_template, ['--help'] }
+ assert_match /:: 2 ::/, output
+ end
+
+ def test_hide_namespace
+ assert !Rails::Generators.hidden_namespaces.include?("special:namespace")
+ Rails::Generators.hide_namespace("special:namespace")
+ assert Rails::Generators.hidden_namespaces.include?("special:namespace")
+ end
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 06b658e7bd..4fb5e6a4eb 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -8,8 +8,9 @@
# Rails booted up.
require 'fileutils'
-require 'test/unit'
require 'rubygems'
+require 'minitest/autorun'
+require 'active_support/test_case'
# TODO: Remove setting this magic constant
RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..")
@@ -178,20 +179,6 @@ module TestHelpers
end
end
- def plugin(name, string = "")
- dir = "#{app_path}/vendor/plugins/#{name}"
- FileUtils.mkdir_p(dir)
-
- File.open("#{dir}/init.rb", 'w') do |f|
- f.puts "::#{name.upcase} = 'loaded'"
- f.puts string
- end
-
- Bukkit.new(dir).tap do |bukkit|
- yield bukkit if block_given?
- end
- end
-
def engine(name)
dir = "#{app_path}/random/#{name}"
FileUtils.mkdir_p(dir)
@@ -260,7 +247,7 @@ module TestHelpers
:activemodel,
:activerecord,
:activeresource] - arr
- remove_from_config "config.active_record.identity_map = true" if to_remove.include? :activerecord
+ remove_from_config "config.active_record.dependent_restrict_raises = false" if to_remove.include? :activerecord
$:.reject! {|path| path =~ %r'/(#{to_remove.join('|')})/' }
end
@@ -270,7 +257,7 @@ module TestHelpers
end
end
-class Test::Unit::TestCase
+class ActiveSupport::TestCase
include TestHelpers::Paths
include TestHelpers::Rack
include TestHelpers::Generation
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 22dbcf9644..5ed923a484 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -4,7 +4,7 @@ require "stringio"
require "rack/test"
module RailtiesTest
- class EngineTest < Test::Unit::TestCase
+ class EngineTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
include SharedTests
@@ -151,46 +151,6 @@ module RailtiesTest
assert_equal "foo", last_response.body
end
- test "engine can load its own plugins" do
- @plugin.write "lib/bukkits.rb", <<-RUBY
- module Bukkits
- class Engine < ::Rails::Engine
- end
- end
- RUBY
-
- @plugin.write "vendor/plugins/yaffle/init.rb", <<-RUBY
- config.yaffle_loaded = true
- RUBY
-
- boot_rails
-
- assert Bukkits::Engine.config.yaffle_loaded
- end
-
- test "engine does not load plugins that already exists in application" do
- @plugin.write "lib/bukkits.rb", <<-RUBY
- module Bukkits
- class Engine < ::Rails::Engine
- end
- end
- RUBY
-
- @plugin.write "vendor/plugins/yaffle/init.rb", <<-RUBY
- config.engine_yaffle_loaded = true
- RUBY
-
- app_file "vendor/plugins/yaffle/init.rb", <<-RUBY
- config.app_yaffle_loaded = true
- RUBY
-
- warnings = capture(:stderr) { boot_rails }
-
- assert !warnings.empty?
- assert !Bukkits::Engine.config.respond_to?(:engine_yaffle_loaded)
- assert Rails.application.config.app_yaffle_loaded
- end
-
test "it loads its environment file" do
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
@@ -323,7 +283,7 @@ module RailtiesTest
assert_equal "bukkits_", Bukkits.table_name_prefix
assert_equal "bukkits", Bukkits::Engine.engine_name
- assert_equal Bukkits._railtie, Bukkits::Engine
+ assert_equal Bukkits.railtie_namespace, Bukkits::Engine
assert ::Bukkits::MyMailer.method_defined?(:foo_path)
assert !::Bukkits::MyMailer.method_defined?(:bar_path)
@@ -467,7 +427,7 @@ module RailtiesTest
assert_nil Rails.application.load_seed
end
- test "using namespace more than once on one module should not overwrite _railtie method" do
+ test "using namespace more than once on one module should not overwrite railtie_namespace method" do
@plugin.write "lib/bukkits.rb", <<-RUBY
module AppTemplate
class Engine < ::Rails::Engine
@@ -484,7 +444,7 @@ module RailtiesTest
boot_rails
- assert_equal AppTemplate._railtie, AppTemplate::Engine
+ assert_equal AppTemplate.railtie_namespace, AppTemplate::Engine
end
test "properly reload routes" do
@@ -667,6 +627,132 @@ module RailtiesTest
assert_equal expected, methods
end
+ test "setting priority for engines with config.railties_order" do
+ @blog = engine "blog" do |plugin|
+ plugin.write "lib/blog.rb", <<-RUBY
+ module Blog
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+ end
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ controller "main", <<-RUBY
+ class MainController < ActionController::Base
+ def foo
+ render :inline => '<%= render :partial => "shared/foo" %>'
+ end
+
+ def bar
+ render :inline => '<%= render :partial => "shared/bar" %>'
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ match "/foo" => "main#foo"
+ match "/bar" => "main#bar"
+ end
+ RUBY
+
+ @plugin.write "app/views/shared/_foo.html.erb", <<-RUBY
+ Bukkit's foo partial
+ RUBY
+
+ app_file "app/views/shared/_foo.html.erb", <<-RUBY
+ App's foo partial
+ RUBY
+
+ @blog.write "app/views/shared/_bar.html.erb", <<-RUBY
+ Blog's bar partial
+ RUBY
+
+ app_file "app/views/shared/_bar.html.erb", <<-RUBY
+ App's bar partial
+ RUBY
+
+ @plugin.write "app/assets/javascripts/foo.js", <<-RUBY
+ // Bukkit's foo js
+ RUBY
+
+ app_file "app/assets/javascripts/foo.js", <<-RUBY
+ // App's foo js
+ RUBY
+
+ @blog.write "app/assets/javascripts/bar.js", <<-RUBY
+ // Blog's bar js
+ RUBY
+
+ app_file "app/assets/javascripts/bar.js", <<-RUBY
+ // App's bar js
+ RUBY
+
+ add_to_config("config.railties_order = [:all, :main_app, Blog::Engine]")
+
+ boot_rails
+ require "#{rails_root}/config/environment"
+
+ get("/foo")
+ assert_equal "Bukkit's foo partial", last_response.body.strip
+
+ get("/bar")
+ assert_equal "App's bar partial", last_response.body.strip
+
+ get("/assets/foo.js")
+ assert_equal "// Bukkit's foo js\n;", last_response.body.strip
+
+ get("/assets/bar.js")
+ assert_equal "// App's bar js\n;", last_response.body.strip
+ end
+
+ test "railties_order adds :all with lowest priority if not given" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ controller "main", <<-RUBY
+ class MainController < ActionController::Base
+ def foo
+ render :inline => '<%= render :partial => "shared/foo" %>'
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ match "/foo" => "main#foo"
+ end
+ RUBY
+
+ @plugin.write "app/views/shared/_foo.html.erb", <<-RUBY
+ Bukkit's foo partial
+ RUBY
+
+ app_file "app/views/shared/_foo.html.erb", <<-RUBY
+ App's foo partial
+ RUBY
+
+ add_to_config("config.railties_order = [Bukkits::Engine]")
+
+ boot_rails
+ require "#{rails_root}/config/environment"
+
+ get("/foo")
+ assert_equal "Bukkit's foo partial", last_response.body.strip
+ end
+
private
def app
Rails.application
diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb
index f8540d69d9..6ebbabc0ff 100644
--- a/railties/test/railties/generators_test.rb
+++ b/railties/test/railties/generators_test.rb
@@ -46,10 +46,6 @@ module RailtiesTests
gem 'rails', :path => '#{RAILS_FRAMEWORK_ROOT}'
gem 'sqlite3'
-
- if RUBY_VERSION < '1.9'
- gem "ruby-debug", ">= 0.10.3"
- end
GEMFILE
end
end
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index 0491fc2174..2bb9df6b64 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
module ApplicationTests
- class ApplicationRoutingTest < Test::Unit::TestCase
+ class ApplicationRoutingTest < ActiveSupport::TestCase
require 'rack/test'
include Rack::Test::Methods
include ActiveSupport::Testing::Isolation
diff --git a/railties/test/railties/plugin_ordering_test.rb b/railties/test/railties/plugin_ordering_test.rb
deleted file mode 100644
index 1cfaf557e9..0000000000
--- a/railties/test/railties/plugin_ordering_test.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-require "isolation/abstract_unit"
-
-module RailtiesTest
- class PluginOrderingTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Isolation
-
- def setup
- build_app
- $arr = []
- plugin "a_plugin", "$arr << :a"
- plugin "b_plugin", "$arr << :b"
- plugin "c_plugin", "$arr << :c"
- end
-
- def teardown
- teardown_app
- end
-
- def boot_rails
- super
- require "#{app_path}/config/environment"
- end
-
- test "plugins are loaded alphabetically by default" do
- boot_rails
- assert_equal [:a, :b, :c], $arr
- end
-
- test "if specified, only those plugins are loaded" do
- add_to_config "config.plugins = [:b_plugin]"
- boot_rails
- assert_equal [:b], $arr
- end
-
- test "the plugins are initialized in the order they are specified" do
- add_to_config "config.plugins = [:b_plugin, :a_plugin]"
- boot_rails
- assert_equal [:b, :a], $arr
- end
-
- test "if :all is specified, the remaining plugins are loaded in alphabetical order" do
- add_to_config "config.plugins = [:c_plugin, :all]"
- boot_rails
- assert_equal [:c, :a, :b], $arr
- end
-
- test "if :all is at the beginning, it represents the plugins not otherwise specified" do
- add_to_config "config.plugins = [:all, :b_plugin]"
- boot_rails
- assert_equal [:a, :c, :b], $arr
- end
-
- test "plugin order array is strings" do
- add_to_config "config.plugins = %w( c_plugin all )"
- boot_rails
- assert_equal [:c, :a, :b], $arr
- end
-
- test "can require lib file from a different plugin" do
- plugin "foo", "require 'bar'" do |plugin|
- plugin.write "lib/foo.rb", "$foo = true"
- end
-
- plugin "bar", "require 'foo'" do |plugin|
- plugin.write "lib/bar.rb", "$bar = true"
- end
-
- add_to_config "config.plugins = [:foo, :bar]"
-
- boot_rails
-
- assert $foo
- assert $bar
- end
- end
-end
diff --git a/railties/test/railties/plugin_test.rb b/railties/test/railties/plugin_test.rb
deleted file mode 100644
index f307d53cf7..0000000000
--- a/railties/test/railties/plugin_test.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-require "isolation/abstract_unit"
-require "railties/shared_tests"
-
-module RailtiesTest
- class PluginTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Isolation
- include SharedTests
-
- def setup
- build_app
-
- @plugin = plugin "bukkits", "::LEVEL = config.log_level" do |plugin|
- plugin.write "lib/bukkits.rb", "class Bukkits; end"
- plugin.write "lib/another.rb", "class Another; end"
- end
- end
-
- def teardown
- teardown_app
- end
-
- test "Rails::Plugin itself does not respond to config" do
- boot_rails
- assert !Rails::Plugin.respond_to?(:config)
- end
-
- test "cannot inherit from Rails::Plugin" do
- boot_rails
- assert_raise RuntimeError do
- class Foo < Rails::Plugin; end
- end
- end
-
- test "plugin can load the file with the same name in lib" do
- boot_rails
- require "bukkits"
- assert_equal "Bukkits", Bukkits.name
- end
-
- test "plugin gets added to dependency list" do
- boot_rails
- assert_equal "Another", Another.name
- end
-
- test "plugin constants get reloaded if config.reload_plugins is set to true" do
- add_to_config <<-RUBY
- config.reload_plugins = true
- RUBY
-
- boot_rails
-
- assert_equal "Another", Another.name
- ActiveSupport::Dependencies.clear
- @plugin.delete("lib/another.rb")
- assert_raises(NameError) { Another }
- end
-
- test "plugin constants are not reloaded by default" do
- boot_rails
- assert_equal "Another", Another.name
- ActiveSupport::Dependencies.clear
- @plugin.delete("lib/another.rb")
- assert_nothing_raised { Another }
- end
-
- test "it loads the plugin's init.rb file" do
- boot_rails
- assert_equal "loaded", BUKKITS
- end
-
- test "the init.rb file has access to the config object" do
- boot_rails
- assert_equal :debug, LEVEL
- end
-
- test "plugin_init_is_run_before_application_ones" do
- plugin "foo", "$foo = true" do |plugin|
- plugin.write "lib/foo.rb", "module Foo; end"
- end
-
- app_file 'config/initializers/foo.rb', <<-RUBY
- raise "no $foo" unless $foo
- raise "no Foo" unless Foo
- RUBY
-
- boot_rails
- assert $foo
- end
-
- test "plugin should work without init.rb" do
- @plugin.delete("init.rb")
-
- boot_rails
-
- require "bukkits"
- assert_nothing_raised { Bukkits }
- end
-
- test "plugin cannot declare an engine for it" do
- @plugin.write "lib/bukkits.rb", <<-RUBY
- class Bukkits
- class Engine < Rails::Engine
- end
- end
- RUBY
-
- @plugin.write "init.rb", <<-RUBY
- require "bukkits"
- RUBY
-
- rescued = false
-
- begin
- boot_rails
- rescue Exception => e
- rescued = true
- assert_equal '"bukkits" is a Railtie/Engine and cannot be installed as a plugin', e.message
- end
-
- assert rescued, "Expected boot rails to fail"
- end
- end
-end
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 55f85c7202..cd495320b5 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -1,7 +1,7 @@
require "isolation/abstract_unit"
module RailtiesTest
- class RailtieTest < Test::Unit::TestCase
+ class RailtieTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index 7653e52d26..3630a0937c 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -56,15 +56,8 @@ module RailtiesTest
app_file "db/migrate/1_create_sessions.rb", <<-RUBY
class CreateSessions < ActiveRecord::Migration
- end
- RUBY
-
- yaffle = plugin "acts_as_yaffle", "::LEVEL = config.log_level" do |plugin|
- plugin.write "lib/acts_as_yaffle.rb", "class ActsAsYaffle; end"
- end
-
- yaffle.write "db/migrate/1_create_yaffles.rb", <<-RUBY
- class CreateYaffles < ActiveRecord::Migration
+ def up
+ end
end
RUBY
@@ -76,23 +69,19 @@ module RailtiesTest
Dir.chdir(app_path) do
output = `bundle exec rake bukkits:install:migrations`
- assert File.exists?("#{app_path}/db/migrate/2_create_users.rb")
- assert File.exists?("#{app_path}/db/migrate/3_add_last_name_to_users.rb")
- assert_match(/Copied migration 2_create_users.rb from bukkits/, output)
- assert_match(/Copied migration 3_add_last_name_to_users.rb from bukkits/, output)
+ assert File.exists?("#{app_path}/db/migrate/2_create_users.bukkits.rb")
+ assert File.exists?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb")
+ assert_match(/Copied migration 2_create_users.bukkits.rb from bukkits/, output)
+ assert_match(/Copied migration 3_add_last_name_to_users.bukkits.rb from bukkits/, output)
assert_match(/NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/, output)
assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length
output = `bundle exec rake railties:install:migrations`.split("\n")
- assert File.exists?("#{app_path}/db/migrate/4_create_yaffles.rb")
assert_no_match(/2_create_users/, output.join("\n"))
- yaffle_migration_order = output.index(output.detect{|o| /Copied migration 4_create_yaffles.rb from acts_as_yaffle/ =~ o })
bukkits_migration_order = output.index(output.detect{|o| /NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/ =~ o })
- assert_not_nil yaffle_migration_order, "Expected migration to be copied"
assert_not_nil bukkits_migration_order, "Expected migration to be skipped"
- assert_equal(railties.index('acts_as_yaffle') > railties.index('bukkits'), yaffle_migration_order > bukkits_migration_order)
migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length
output = `bundle exec rake railties:install:migrations`
diff --git a/tasks/release.rb b/tasks/release.rb
index 2422efa786..191c014f9f 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -26,16 +26,16 @@ directory "pkg"
major, minor, tiny, pre = version.split('.')
pre = pre ? pre.inspect : "nil"
- ruby.gsub! /^(\s*)MAJOR = .*?$/, "\\1MAJOR = #{major}"
+ ruby.gsub!(/^(\s*)MAJOR = .*?$/, "\\1MAJOR = #{major}")
raise "Could not insert MAJOR in #{file}" unless $1
- ruby.gsub! /^(\s*)MINOR = .*?$/, "\\1MINOR = #{minor}"
+ ruby.gsub!(/^(\s*)MINOR = .*?$/, "\\1MINOR = #{minor}")
raise "Could not insert MINOR in #{file}" unless $1
- ruby.gsub! /^(\s*)TINY = .*?$/, "\\1TINY = #{tiny}"
+ ruby.gsub!(/^(\s*)TINY = .*?$/, "\\1TINY = #{tiny}")
raise "Could not insert TINY in #{file}" unless $1
- ruby.gsub! /^(\s*)PRE = .*?$/, "\\1PRE = #{pre}"
+ ruby.gsub!(/^(\s*)PRE = .*?$/, "\\1PRE = #{pre}")
raise "Could not insert PRE in #{file}" unless $1
File.open(file, 'w') { |f| f.write ruby }
@@ -66,7 +66,7 @@ namespace :changelog do
FRAMEWORKS.each do |fw|
require 'date'
replace = '\1(' + Date.today.strftime('%B %d, %Y') + ')'
- fname = File.join fw, 'CHANGELOG'
+ fname = File.join fw, 'CHANGELOG.md'
contents = File.read(fname).sub(/^([^(]*)\(unreleased\)/, replace)
File.open(fname, 'wb') { |f| f.write contents }
@@ -76,7 +76,7 @@ namespace :changelog do
task :release_summary do
FRAMEWORKS.each do |fw|
puts "## #{fw}"
- fname = File.join fw, 'CHANGELOG'
+ fname = File.join fw, 'CHANGELOG.md'
contents = File.readlines fname
contents.shift
changes = []
@@ -116,6 +116,7 @@ namespace :all do
task :tag do
sh "git tag #{tag}"
+ sh "git push --tags"
end
task :release => %w(ensure_clean_state build commit tag push)
diff --git a/version.rb b/version.rb
index 254227ecf7..ec878f9dcf 100644
--- a/version.rb
+++ b/version.rb
@@ -1,7 +1,7 @@
module Rails
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"