From bba0cf6b103e16017a3f419cbc137f1fa8356781 Mon Sep 17 00:00:00 2001
From: Jacques Distler
Date: Fri, 9 Feb 2007 17:12:31 -0600
Subject: [PATCH] Ooops! Fixed upgrade of Rails.
---
.../rails/actionmailer/test/abstract_unit.rb | 30 +
.../test/fixtures/first_mailer/share.rhtml | 1 +
...ltipart_with_template_path_with_dots.rhtml | 1 +
.../test/fixtures/raw_email_quoted_with_0d0a | 14 +
.../raw_email_with_partially_quoted_subject | 14 +
.../test/fixtures/second_mailer/share.rhtml | 1 +
.../implicitly_multipart_example.rhtml.bak | 1 +
.../test_mailer/signed_up_with_url.rhtml | 3 +
vendor/rails/actionmailer/test/url_test.rb | 68 ++
.../assertions/deprecated_assertions.rb | 228 ++++
.../assertions/dom_assertions.rb | 25 +
.../assertions/model_assertions.rb | 12 +
.../assertions/response_assertions.rb | 140 +++
.../assertions/routing_assertions.rb | 98 ++
.../assertions/selector_assertions.rb | 571 +++++++++
.../assertions/tag_assertions.rb | 117 ++
.../deprecated_dependencies.rb | 65 ++
.../lib/action_controller/resources.rb | 405 +++++++
.../lib/action_controller/status_codes.rb | 88 ++
.../vendor/html-scanner/html/selector.rb | 823 +++++++++++++
.../action_view/helpers/deprecated_helper.rb | 34 +
.../test/controller/assert_select_test.rb | 576 ++++++++++
.../test/controller/caching_test.rb | 228 ++++
.../test/controller/content_type_test.rb | 139 +++
.../app/controllers/admin/user_controller.rb | 0
.../app/controllers/user_controller.rb | 0
.../bad_plugin/lib/plugin_controller.rb | 0
.../deprecated_instance_variables_test.rb | 48 +
.../deprecated_base_methods_test.rb | 60 +
.../test/controller/integration_test.rb | 154 +++
.../test/controller/resources_test.rb | 482 ++++++++
.../test/controller/selector_test.rb | 628 ++++++++++
...default_content_types_for_respond_to.rhtml | 1 +
.../render_default_for_rhtml.rhtml | 1 +
.../content_type/render_default_for_rjs.rjs | 1 +
.../content_type/render_default_for_rxml.rxml | 1 +
.../_cookies_ivar.rhtml | 1 +
.../_cookies_method.rhtml | 1 +
.../_flash_ivar.rhtml | 1 +
.../_flash_method.rhtml | 1 +
.../_headers_ivar.rhtml | 1 +
.../_headers_method.rhtml | 1 +
.../_params_ivar.rhtml | 1 +
.../_params_method.rhtml | 1 +
.../_request_ivar.rhtml | 1 +
.../_request_method.rhtml | 1 +
.../_response_ivar.rhtml | 1 +
.../_response_method.rhtml | 1 +
.../_session_ivar.rhtml | 1 +
.../_session_method.rhtml | 1 +
.../public/javascripts/application.js | 1 +
.../actionpack/test/fixtures/test/_hello.rxml | 1 +
.../fixtures/test/hello_world_container.rxml | 3 +
.../test/template/deprecated_helper_test.rb | 36 +
.../deprecated_instance_variables_test.rb | 43 +
.../lib/active_record/attribute_methods.rb | 75 ++
.../connection_adapters/frontbase_adapter.rb | 861 ++++++++++++++
.../lib/active_record/locking/optimistic.rb | 106 ++
.../lib/active_record/locking/pessimistic.rb | 77 ++
.../lib/active_record/xml_serialization.rb | 308 +++++
.../test/active_schema_test_mysql.rb | 31 +
.../test/adapter_test_sqlserver.rb | 81 ++
.../test/associations/callbacks_test.rb | 126 ++
.../cascaded_eager_loading_test.rb | 138 +++
.../test/associations/eager_test.rb | 393 +++++++
.../test/associations/extension_test.rb | 42 +
.../test/associations/join_model_test.rb | 480 ++++++++
.../test/attribute_methods_test.rb | 49 +
.../test/connection_test_firebird.rb | 8 +
.../native_frontbase/connection.rb | 27 +
.../test/datatype_test_postgresql.rb | 52 +
.../activerecord/test/empty_date_time_test.rb | 25 +
.../db_definitions/frontbase.drop.sql | 31 +
.../fixtures/db_definitions/frontbase.sql | 262 +++++
.../db_definitions/frontbase2.drop.sql | 1 +
.../fixtures/db_definitions/frontbase2.sql | 4 +
.../rails/activerecord/test/fixtures/edge.rb | 5 +
.../activerecord/test/fixtures/edges.yml | 6 +
.../1_give_me_big_numbers.rb | 15 +
.../1000_people_have_middle_names.rb | 9 +
.../1_people_have_last_names.rb | 9 +
.../3_we_need_reminders.rb | 12 +
.../4_innocent_jointable.rb | 12 +
.../activerecord/test/fixtures/vertex.rb | 9 +
.../activerecord/test/fixtures/vertices.yml | 4 +
.../test/migration_test_firebird.rb | 124 ++
.../schema_authorization_test_postgresql.rb | 75 ++
.../test/table_name_test_sqlserver.rb | 23 +
.../test/xml_serialization_test.rb | 125 ++
vendor/rails/activesupport/MIT-LICENSE | 20 +
.../active_support/core_ext/array/grouping.rb | 55 +
.../lib/active_support/core_ext/bigdecimal.rb | 3 +
.../core_ext/bigdecimal/formatting.rb | 7 +
.../core_ext/module/aliasing.rb | 58 +
.../core_ext/module/attr_internal.rb | 31 +
.../lib/active_support/core_ext/name_error.rb | 17 +
.../active_support/core_ext/string/unicode.rb | 42 +
.../lib/active_support/deprecation.rb | 201 ++++
.../lib/active_support/multibyte.rb | 7 +
.../lib/active_support/multibyte/chars.rb | 129 +++
.../multibyte/generators/generate_tables.rb | 149 +++
.../multibyte/handlers/passthru_handler.rb | 9 +
.../multibyte/handlers/utf8_handler.rb | 454 ++++++++
.../multibyte/handlers/utf8_handler_proc.rb | 43 +
.../active_support/values/unicode_tables.dat | Bin 0 -> 656156 bytes
.../active_support/vendor/builder/xchar.rb | 112 ++
.../lib/active_support/vendor/xml_simple.rb | 1021 +++++++++++++++++
.../rails/activesupport/test/abstract_unit.rb | 7 +
.../test/autoloading_fixtures/application.rb | 1 +
.../test/autoloading_fixtures/class_folder.rb | 3 +
.../class_folder/class_folder_subclass.rb | 3 +
.../class_folder/inline_class.rb | 2 +
.../class_folder/nested_class.rb | 7 +
.../test/autoloading_fixtures/conflict.rb | 1 +
.../autoloading_fixtures/counting_loader.rb | 5 +
.../cross_site_dependency.rb | 2 +
.../module_folder/inline_class.rb | 2 +
.../module_with_custom_const_missing/a/b.rb | 1 +
.../multiple_constant_file.rb | 2 +
.../autoloading_fixtures/raises_name_error.rb | 3 +
.../raises_no_method_error.rb | 3 +
.../core_ext/class/attribute_accessor_test.rb | 31 +
.../class_inheritable_attributes_test.rb | 208 ++++
.../module/attr_accessor_with_default.rb | 30 +
.../core_ext/module/attr_internal_test.rb | 52 +
.../module/attribute_accessor_test.rb | 33 +
.../module/attribute_aliasing_test.rb | 31 +
.../test/core_ext/name_error_test.rb | 24 +
.../test/dependencies/conflict.rb | 1 +
.../test/dependencies/cross_site_depender.rb | 3 +
.../dependencies/requires_nonexistent0.rb | 1 +
.../dependencies/requires_nonexistent1.rb | 1 +
.../activesupport/test/deprecation_test.rb | 151 +++
.../test/multibyte_chars_test.rb | 164 +++
.../test/multibyte_conformance.rb | 141 +++
.../test/multibyte_handler_test.rb | 274 +++++
vendor/rails/railties/bin/process/inspector | 3 +
.../railties/configs/databases/frontbase.yml | 28 +
.../lib/commands/process/inspector.rb | 68 ++
.../railties/lib/commands/servers/base.rb | 19 +
.../railties/lib/commands/servers/mongrel.rb | 65 ++
.../rails_generator/generated_attribute.rb | 42 +
.../generators/components/observer/USAGE | 15 +
.../components/observer/observer_generator.rb | 16 +
.../components/observer/templates/observer.rb | 2 +
.../observer/templates/unit_test.rb | 10 +
.../components/plugin/templates/uninstall.rb | 1 +
.../components/resource/resource_generator.rb | 76 ++
.../components/resource/templates/USAGE | 18 +
.../resource/templates/controller.rb | 2 +
.../resource/templates/fixtures.yml | 11 +
.../resource/templates/functional_test.rb | 20 +
.../components/resource/templates/helper.rb | 2 +
.../resource/templates/migration.rb | 13 +
.../components/resource/templates/model.rb | 2 +
.../resource/templates/unit_test.rb | 10 +
.../components/scaffold_resource/USAGE | 29 +
.../scaffold_resource_generator.rb | 92 ++
.../scaffold_resource/templates/controller.rb | 79 ++
.../scaffold_resource/templates/fixtures.yml | 11 +
.../templates/functional_test.rb | 57 +
.../scaffold_resource/templates/helper.rb | 2 +
.../scaffold_resource/templates/layout.rhtml | 17 +
.../scaffold_resource/templates/migration.rb | 13 +
.../scaffold_resource/templates/model.rb | 2 +
.../scaffold_resource/templates/style.css | 74 ++
.../scaffold_resource/templates/unit_test.rb | 10 +
.../templates/view_edit.rhtml | 19 +
.../templates/view_index.rhtml | 24 +
.../templates/view_new.rhtml | 18 +
.../templates/view_show.rhtml | 10 +
vendor/rails/railties/test/abstract_unit.rb | 20 +
.../missing_class/missing_class_generator.rb | 0
.../generators/working/working_generator.rb | 2 +
vendor/rails/railties/test/mocks/routes.rb | 6 +
.../railties/test/mocks/stubbed_breakpoint.rb | 2 +
.../railties/test/mocks/stubbed_kernel.rb | 5 +
177 files changed, 13221 insertions(+)
create mode 100644 vendor/rails/actionmailer/test/abstract_unit.rb
create mode 100644 vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml
create mode 100644 vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml
create mode 100644 vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a
create mode 100644 vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject
create mode 100644 vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml
create mode 100644 vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak
create mode 100644 vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml
create mode 100644 vendor/rails/actionmailer/test/url_test.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/resources.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/status_codes.rb
create mode 100644 vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
create mode 100644 vendor/rails/actionpack/lib/action_view/helpers/deprecated_helper.rb
create mode 100644 vendor/rails/actionpack/test/controller/assert_select_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/caching_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/content_type_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb
create mode 100644 vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/user_controller.rb
create mode 100644 vendor/rails/actionpack/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb
create mode 100644 vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/integration_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/resources_test.rb
create mode 100644 vendor/rails/actionpack/test/controller/selector_test.rb
create mode 100644 vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
create mode 100644 vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
create mode 100644 vendor/rails/actionpack/test/fixtures/public/javascripts/application.js
create mode 100644 vendor/rails/actionpack/test/fixtures/test/_hello.rxml
create mode 100644 vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
create mode 100644 vendor/rails/actionpack/test/template/deprecated_helper_test.rb
create mode 100644 vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
create mode 100644 vendor/rails/activerecord/lib/active_record/attribute_methods.rb
create mode 100644 vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
create mode 100644 vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
create mode 100644 vendor/rails/activerecord/lib/active_record/locking/pessimistic.rb
create mode 100644 vendor/rails/activerecord/lib/active_record/xml_serialization.rb
create mode 100644 vendor/rails/activerecord/test/active_schema_test_mysql.rb
create mode 100644 vendor/rails/activerecord/test/adapter_test_sqlserver.rb
create mode 100644 vendor/rails/activerecord/test/associations/callbacks_test.rb
create mode 100644 vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
create mode 100644 vendor/rails/activerecord/test/associations/eager_test.rb
create mode 100644 vendor/rails/activerecord/test/associations/extension_test.rb
create mode 100644 vendor/rails/activerecord/test/associations/join_model_test.rb
create mode 100755 vendor/rails/activerecord/test/attribute_methods_test.rb
create mode 100644 vendor/rails/activerecord/test/connection_test_firebird.rb
create mode 100644 vendor/rails/activerecord/test/connections/native_frontbase/connection.rb
create mode 100644 vendor/rails/activerecord/test/datatype_test_postgresql.rb
create mode 100644 vendor/rails/activerecord/test/empty_date_time_test.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
create mode 100644 vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
create mode 100644 vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.drop.sql
create mode 100644 vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.sql
create mode 100644 vendor/rails/activerecord/test/fixtures/edge.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/edges.yml
create mode 100644 vendor/rails/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/vertex.rb
create mode 100644 vendor/rails/activerecord/test/fixtures/vertices.yml
create mode 100644 vendor/rails/activerecord/test/migration_test_firebird.rb
create mode 100644 vendor/rails/activerecord/test/schema_authorization_test_postgresql.rb
create mode 100644 vendor/rails/activerecord/test/table_name_test_sqlserver.rb
create mode 100644 vendor/rails/activerecord/test/xml_serialization_test.rb
create mode 100644 vendor/rails/activesupport/MIT-LICENSE
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/module/attr_internal.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/deprecation.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/multibyte.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/multibyte/generators/generate_tables.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/multibyte/handlers/passthru_handler.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler_proc.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/values/unicode_tables.dat
create mode 100644 vendor/rails/activesupport/lib/active_support/vendor/builder/xchar.rb
create mode 100644 vendor/rails/activesupport/lib/active_support/vendor/xml_simple.rb
create mode 100644 vendor/rails/activesupport/test/abstract_unit.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/application.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/class_folder.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/class_folder/class_folder_subclass.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/class_folder/inline_class.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/class_folder/nested_class.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/conflict.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/counting_loader.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/cross_site_dependency.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/module_folder/inline_class.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/module_with_custom_const_missing/a/b.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/multiple_constant_file.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/raises_name_error.rb
create mode 100644 vendor/rails/activesupport/test/autoloading_fixtures/raises_no_method_error.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/class/attribute_accessor_test.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/module/attr_internal_test.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/module/attribute_accessor_test.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/module/attribute_aliasing_test.rb
create mode 100644 vendor/rails/activesupport/test/core_ext/name_error_test.rb
create mode 100644 vendor/rails/activesupport/test/dependencies/conflict.rb
create mode 100644 vendor/rails/activesupport/test/dependencies/cross_site_depender.rb
create mode 100644 vendor/rails/activesupport/test/dependencies/requires_nonexistent0.rb
create mode 100644 vendor/rails/activesupport/test/dependencies/requires_nonexistent1.rb
create mode 100644 vendor/rails/activesupport/test/deprecation_test.rb
create mode 100644 vendor/rails/activesupport/test/multibyte_chars_test.rb
create mode 100644 vendor/rails/activesupport/test/multibyte_conformance.rb
create mode 100644 vendor/rails/activesupport/test/multibyte_handler_test.rb
create mode 100644 vendor/rails/railties/bin/process/inspector
create mode 100644 vendor/rails/railties/configs/databases/frontbase.yml
create mode 100644 vendor/rails/railties/lib/commands/process/inspector.rb
create mode 100644 vendor/rails/railties/lib/commands/servers/base.rb
create mode 100644 vendor/rails/railties/lib/commands/servers/mongrel.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generated_attribute.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/observer/USAGE
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/observer/observer_generator.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/observer.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/resource_generator.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/USAGE
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/controller.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/fixtures.yml
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/migration.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/model.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/unit_test.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/USAGE
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/scaffold_resource_generator.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/controller.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/fixtures.yml
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/functional_test.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/helper.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/layout.rhtml
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/migration.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/model.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/style.css
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/unit_test.rb
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/view_edit.rhtml
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/view_index.rhtml
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/view_new.rhtml
create mode 100644 vendor/rails/railties/lib/rails_generator/generators/components/scaffold_resource/templates/view_show.rhtml
create mode 100644 vendor/rails/railties/test/abstract_unit.rb
create mode 100644 vendor/rails/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb
create mode 100644 vendor/rails/railties/test/fixtures/lib/generators/working/working_generator.rb
create mode 100644 vendor/rails/railties/test/mocks/routes.rb
create mode 100644 vendor/rails/railties/test/mocks/stubbed_breakpoint.rb
create mode 100644 vendor/rails/railties/test/mocks/stubbed_kernel.rb
diff --git a/vendor/rails/actionmailer/test/abstract_unit.rb b/vendor/rails/actionmailer/test/abstract_unit.rb
new file mode 100644
index 00000000..8a30e39a
--- /dev/null
+++ b/vendor/rails/actionmailer/test/abstract_unit.rb
@@ -0,0 +1,30 @@
+require 'test/unit'
+
+$:.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'action_mailer'
+
+# Show backtraces for deprecated behavior for quicker cleanup.
+ActiveSupport::Deprecation.debug = true
+
+$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers"
+ActionMailer::Base.template_root = "#{File.dirname(__FILE__)}/fixtures"
+
+class MockSMTP
+ def self.deliveries
+ @@deliveries
+ end
+
+ def initialize
+ @@deliveries = []
+ end
+
+ def sendmail(mail, from, to)
+ @@deliveries << [mail, from, to]
+ end
+end
+
+class Net::SMTP
+ def self.start(*args)
+ yield MockSMTP.new
+ end
+end
diff --git a/vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml b/vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml
new file mode 100644
index 00000000..da43638c
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml
@@ -0,0 +1 @@
+first mail
diff --git a/vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml b/vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml
new file mode 100644
index 00000000..897a5065
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml
@@ -0,0 +1 @@
+Have a lovely picture, from me. Enjoy!
\ No newline at end of file
diff --git a/vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a b/vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a
new file mode 100644
index 00000000..8a2c25a5
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a
@@ -0,0 +1,14 @@
+Mime-Version: 1.0 (Apple Message framework v730)
+Message-Id: <9169D984-4E0B-45EF-82D4-8F5E53AD7012@example.com>
+From: foo@example.com
+Subject: testing
+Date: Mon, 6 Jun 2005 22:21:22 +0200
+To: blah@example.com
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain
+
+A fax has arrived from remote ID ''.=0D=0A-----------------------=
+-------------------------------------=0D=0ATime: 3/9/2006 3:50:52=
+ PM=0D=0AReceived from remote ID: =0D=0AInbound user ID XXXXXXXXXX, r=
+outing code XXXXXXXXX=0D=0AResult: (0/352;0/0) Successful Send=0D=0AP=
+age record: 1 - 1=0D=0AElapsed time: 00:58 on channel 11=0D=0A
diff --git a/vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject b/vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject
new file mode 100644
index 00000000..e86108da
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject
@@ -0,0 +1,14 @@
+From jamis@37signals.com Mon May 2 16:07:05 2005
+Mime-Version: 1.0 (Apple Message framework v622)
+Content-Transfer-Encoding: base64
+Message-Id:
+Content-Type: text/plain;
+ charset=EUC-KR;
+ format=flowed
+To: jamis@37signals.com
+From: Jamis Buck
+Subject: Re: Test: =?UTF-8?B?Iua8ouWtlyI=?= mid =?UTF-8?B?Iua8ouWtlyI=?= tail
+Date: Mon, 2 May 2005 16:07:05 -0600
+
+tOu6zrrQwMcguLbC+bChwfa3ziwgv+y4rrTCIMfPs6q01MC7ILnPvcC0z7TZLg0KDQrBpiDAzLin
+wLogSmFtaXPA1LTPtNku
diff --git a/vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml b/vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml
new file mode 100644
index 00000000..9a540106
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml
@@ -0,0 +1 @@
+second mail
diff --git a/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak b/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak
new file mode 100644
index 00000000..6940419d
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak
@@ -0,0 +1 @@
+Ignored when searching for implicitly multipart parts.
diff --git a/vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml b/vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml
new file mode 100644
index 00000000..e8fb65d4
--- /dev/null
+++ b/vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml
@@ -0,0 +1,3 @@
+Hello there,
+
+Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %>
\ No newline at end of file
diff --git a/vendor/rails/actionmailer/test/url_test.rb b/vendor/rails/actionmailer/test/url_test.rb
new file mode 100644
index 00000000..ded343cf
--- /dev/null
+++ b/vendor/rails/actionmailer/test/url_test.rb
@@ -0,0 +1,68 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+
+class TestMailer < ActionMailer::Base
+ def signed_up_with_url(recipient)
+ @recipients = recipient
+ @subject = "[Signed up] Welcome #{recipient}"
+ @from = "system@loudthinking.com"
+ @sent_on = Time.local(2004, 12, 12)
+
+ @body["recipient"] = recipient
+ @body["welcome_url"] = url_for :host => "example.com", :controller => "welcome", :action => "greeting"
+ end
+
+ class < charset }
+ end
+ mail
+ end
+
+ def setup
+ ActionMailer::Base.delivery_method = :test
+ ActionMailer::Base.perform_deliveries = true
+ ActionMailer::Base.deliveries = []
+
+ @recipient = 'test@localhost'
+ end
+
+ def test_signed_up_with_url
+ ActionController::Routing::Routes.draw do |map|
+ map.connect ':controller/:action/:id'
+ end
+
+ expected = new_mail
+ expected.to = @recipient
+ expected.subject = "[Signed up] Welcome #{@recipient}"
+ expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting"
+ expected.from = "system@loudthinking.com"
+ expected.date = Time.local(2004, 12, 12)
+
+ created = nil
+ assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) }
+ assert_not_nil created
+ assert_equal expected.encoded, created.encoded
+
+ assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) }
+ assert_not_nil ActionMailer::Base.deliveries.first
+ assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb
new file mode 100644
index 00000000..5fd18793
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb
@@ -0,0 +1,228 @@
+require 'rexml/document'
+
+module ActionController #:nodoc:
+ module Assertions #:nodoc:
+ module DeprecatedAssertions #:nodoc:
+ def assert_success(message=nil) #:nodoc:
+ assert_response(:success, message)
+ end
+ deprecate :assert_success => "use assert_response(:success)"
+
+ def assert_redirect(message=nil) #:nodoc:
+ assert_response(:redirect, message)
+ end
+ deprecate :assert_redirect => "use assert_response(:redirect)"
+
+ def assert_rendered_file(expected=nil, message=nil) #:nodoc:
+ assert_template(expected, message)
+ end
+ deprecate :assert_rendered_file => :assert_template
+
+ # ensure that the session has an object with the specified name
+ def assert_session_has(key=nil, message=nil) #:nodoc:
+ msg = build_message(message, "> is not in the session >", key, @response.session)
+ assert_block(msg) { @response.has_session_object?(key) }
+ end
+ deprecate :assert_session_has => "use assert(@response.has_session_object?(key))"
+
+ # ensure that the session has no object with the specified name
+ def assert_session_has_no(key=nil, message=nil) #:nodoc:
+ msg = build_message(message, "> is in the session >", key, @response.session)
+ assert_block(msg) { !@response.has_session_object?(key) }
+ end
+ deprecate :assert_session_has_no => "use assert(!@response.has_session_object?(key))"
+
+ def assert_session_equal(expected = nil, key = nil, message = nil) #:nodoc:
+ msg = build_message(message, "> expected in session['?'] but was >", expected, key, @response.session[key])
+ assert_block(msg) { expected == @response.session[key] }
+ end
+ deprecate :assert_session_equal => "use assert_equal(expected, @response[key])"
+
+ # -- cookie assertions ---------------------------------------------------
+
+ def assert_no_cookie(key = nil, message = nil) #:nodoc:
+ actual = @response.cookies[key]
+ msg = build_message(message, "> not expected in cookies['?']", actual, key)
+ assert_block(msg) { actual.nil? or actual.empty? }
+ end
+ deprecate :assert_no_cookie => "use assert(!@response.cookies.key?(key))"
+
+ def assert_cookie_equal(expected = nil, key = nil, message = nil) #:nodoc:
+ actual = @response.cookies[key]
+ actual = actual.first if actual
+ msg = build_message(message, "> expected in cookies['?'] but was >", expected, key, actual)
+ assert_block(msg) { expected == actual }
+ end
+ deprecate :assert_cookie_equal => "use assert(@response.cookies.key?(key))"
+
+ # -- flash assertions ---------------------------------------------------
+
+ # ensure that the flash has an object with the specified name
+ def assert_flash_has(key=nil, message=nil) #:nodoc:
+ msg = build_message(message, "> is not in the flash >", key, @response.flash)
+ assert_block(msg) { @response.has_flash_object?(key) }
+ end
+ deprecate :assert_flash_has => "use assert(@response.has_flash_object?(key))"
+
+ # ensure that the flash has no object with the specified name
+ def assert_flash_has_no(key=nil, message=nil) #:nodoc:
+ msg = build_message(message, "> is in the flash >", key, @response.flash)
+ assert_block(msg) { !@response.has_flash_object?(key) }
+ end
+ deprecate :assert_flash_has_no => "use assert(!@response.has_flash_object?(key))"
+
+ # ensure the flash exists
+ def assert_flash_exists(message=nil) #:nodoc:
+ msg = build_message(message, "the flash does not exist >", @response.session['flash'] )
+ assert_block(msg) { @response.has_flash? }
+ end
+ deprecate :assert_flash_exists => "use assert(@response.has_flash?)"
+
+ # ensure the flash does not exist
+ def assert_flash_not_exists(message=nil) #:nodoc:
+ msg = build_message(message, "the flash exists >", @response.flash)
+ assert_block(msg) { !@response.has_flash? }
+ end
+ deprecate :assert_flash_not_exists => "use assert(!@response.has_flash?)"
+
+ # ensure the flash is empty but existent
+ def assert_flash_empty(message=nil) #:nodoc:
+ msg = build_message(message, "the flash is not empty >", @response.flash)
+ assert_block(msg) { !@response.has_flash_with_contents? }
+ end
+ deprecate :assert_flash_empty => "use assert(!@response.has_flash_with_contents?)"
+
+ # ensure the flash is not empty
+ def assert_flash_not_empty(message=nil) #:nodoc:
+ msg = build_message(message, "the flash is empty")
+ assert_block(msg) { @response.has_flash_with_contents? }
+ end
+ deprecate :assert_flash_not_empty => "use assert(@response.has_flash_with_contents?)"
+
+ def assert_flash_equal(expected = nil, key = nil, message = nil) #:nodoc:
+ msg = build_message(message, "> expected in flash['?'] but was >", expected, key, @response.flash[key])
+ assert_block(msg) { expected == @response.flash[key] }
+ end
+ deprecate :assert_flash_equal => "use assert_equal(expected, @response.flash[key])"
+
+
+ # ensure our redirection url is an exact match
+ def assert_redirect_url(url=nil, message=nil) #:nodoc:
+ assert_redirect(message)
+ msg = build_message(message, "> is not the redirected location >", url, @response.redirect_url)
+ assert_block(msg) { @response.redirect_url == url }
+ end
+ deprecate :assert_redirect_url => "use assert_equal(url, @response.redirect_url)"
+
+ # ensure our redirection url matches a pattern
+ def assert_redirect_url_match(pattern=nil, message=nil) #:nodoc:
+ assert_redirect(message)
+ msg = build_message(message, "> was not found in the location: >", pattern, @response.redirect_url)
+ assert_block(msg) { @response.redirect_url_match?(pattern) }
+ end
+ deprecate :assert_redirect_url_match => "use assert(@response.redirect_url_match?(pattern))"
+
+
+ # -- template assertions ------------------------------------------------
+
+ # ensure that a template object with the given name exists
+ def assert_template_has(key=nil, message=nil) #:nodoc:
+ msg = build_message(message, "> is not a template object", key )
+ assert_block(msg) { @response.has_template_object?(key) }
+ end
+ deprecate :assert_template_has => "use assert(@response.has_template_object?(key))"
+
+ # ensure that a template object with the given name does not exist
+ def assert_template_has_no(key=nil,message=nil) #:nodoc:
+ msg = build_message(message, "> is a template object >", key, @response.template_objects[key])
+ assert_block(msg) { !@response.has_template_object?(key) }
+ end
+ deprecate :assert_template_has_no => "use assert(!@response.has_template_object?(key))"
+
+ # ensures that the object assigned to the template on +key+ is equal to +expected+ object.
+ def assert_template_equal(expected = nil, key = nil, message = nil) #:nodoc:
+ msg = build_message(message, "> expected in assigns['?'] but was >", expected, key, @response.template.assigns[key.to_s])
+ assert_block(msg) { expected == @response.template.assigns[key.to_s] }
+ end
+ alias_method :assert_assigned_equal, :assert_template_equal
+ deprecate :assert_assigned_equal => "use assert_equal(expected, @response.template.assigns[key.to_s])"
+ deprecate :assert_template_equal => "use assert_equal(expected, @response.template.assigns[key.to_s])"
+
+ # Asserts that the template returns the +expected+ string or array based on the XPath +expression+.
+ # This will only work if the template rendered a valid XML document.
+ def assert_template_xpath_match(expression=nil, expected=nil, message=nil) #:nodoc:
+ xml, matches = REXML::Document.new(@response.body), []
+ xml.elements.each(expression) { |e| matches << e.text }
+ if matches.empty? then
+ msg = build_message(message, "> not found in document", expression)
+ flunk(msg)
+ return
+ elsif matches.length < 2 then
+ matches = matches.first
+ end
+
+ msg = build_message(message, "> found >, not >", expression, matches, expected)
+ assert_block(msg) { matches == expected }
+ end
+ deprecate :assert_template_xpath_match => "you should use assert_tag, instead"
+
+ # Assert the template object with the given name is an Active Record descendant and is valid.
+ def assert_valid_record(key = nil, message = nil) #:nodoc:
+ record = find_record_in_template(key)
+ msg = build_message(message, "Active Record is invalid >)", record.errors.full_messages)
+ assert_block(msg) { record.valid? }
+ end
+ deprecate :assert_valid_record => "use assert(assigns(key).valid?)"
+
+ # Assert the template object with the given name is an Active Record descendant and is invalid.
+ def assert_invalid_record(key = nil, message = nil) #:nodoc:
+ record = find_record_in_template(key)
+ msg = build_message(message, "Active Record is valid)")
+ assert_block(msg) { !record.valid? }
+ end
+ deprecate :assert_invalid_record => "use assert(!assigns(key).valid?)"
+
+ # Assert the template object with the given name is an Active Record descendant and the specified column(s) are valid.
+ def assert_valid_column_on_record(key = nil, columns = "", message = nil) #:nodoc:
+ record = find_record_in_template(key)
+ record.send(:validate)
+
+ cols = glue_columns(columns)
+ cols.delete_if { |col| !record.errors.invalid?(col) }
+ msg = build_message(message, "Active Record has invalid columns >)", cols.join(",") )
+ assert_block(msg) { cols.empty? }
+ end
+ deprecate :assert_valid_column_on_record => "use assert(!record.errors.invalid?(column)) instead"
+
+ # Assert the template object with the given name is an Active Record descendant and the specified column(s) are invalid.
+ def assert_invalid_column_on_record(key = nil, columns = "", message = nil) #:nodoc:
+ record = find_record_in_template(key)
+ record.send(:validate)
+
+ cols = glue_columns(columns)
+ cols.delete_if { |col| record.errors.invalid?(col) }
+ msg = build_message(message, "Active Record has valid columns >)", cols.join(",") )
+ assert_block(msg) { cols.empty? }
+ end
+ deprecate :assert_invalid_column_on_record => "use assert(record.errors.invalid?(column)) instead"
+
+ private
+ def glue_columns(columns)
+ cols = []
+ cols << columns if columns.class == String
+ cols += columns if columns.class == Array
+ cols
+ end
+
+ def find_record_in_template(key = nil)
+ assert_not_nil assigns(key)
+ record = @response.template_objects[key]
+
+ assert_not_nil(record)
+ assert_kind_of ActiveRecord::Base, record
+
+ return record
+ end
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb
new file mode 100644
index 00000000..d1eea59e
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb
@@ -0,0 +1,25 @@
+module ActionController
+ module Assertions
+ module DomAssertions
+ # test 2 html strings to be equivalent, i.e. identical up to reordering of attributes
+ def assert_dom_equal(expected, actual, message="")
+ clean_backtrace do
+ expected_dom = HTML::Document.new(expected).root
+ actual_dom = HTML::Document.new(actual).root
+ full_message = build_message(message, "> expected to be == to\n>.", expected_dom.to_s, actual_dom.to_s)
+ assert_block(full_message) { expected_dom == actual_dom }
+ end
+ end
+
+ # negated form of +assert_dom_equivalent+
+ def assert_dom_not_equal(expected, actual, message="")
+ clean_backtrace do
+ expected_dom = HTML::Document.new(expected).root
+ actual_dom = HTML::Document.new(actual).root
+ full_message = build_message(message, "> expected to be != to\n>.", expected_dom.to_s, actual_dom.to_s)
+ assert_block(full_message) { expected_dom != actual_dom }
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb
new file mode 100644
index 00000000..a9bcd7db
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb
@@ -0,0 +1,12 @@
+module ActionController
+ module Assertions
+ module ModelAssertions
+ # ensures that the passed record is valid by active record standards. returns the error messages if not
+ def assert_valid(record)
+ clean_backtrace do
+ assert record.valid?, record.errors.full_messages.join("\n")
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb
new file mode 100644
index 00000000..bcb07e67
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb
@@ -0,0 +1,140 @@
+require 'rexml/document'
+require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
+
+module ActionController
+ module Assertions
+ module ResponseAssertions
+ # Asserts that the response is one of the following types:
+ #
+ # * :success: Status code was 200
+ # * :redirect: Status code was in the 300-399 range
+ # * :missing: Status code was 404
+ # * :error: Status code was in the 500-599 range
+ #
+ # You can also pass an explicit status number like assert_response(501)
+ # or its symbolic equivalent assert_response(:not_implemented).
+ # See ActionController::StatusCodes for a full list.
+ def assert_response(type, message = nil)
+ clean_backtrace do
+ if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
+ assert_block("") { true } # to count the assertion
+ elsif type.is_a?(Fixnum) && @response.response_code == type
+ assert_block("") { true } # to count the assertion
+ elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
+ assert_block("") { true } # to count the assertion
+ else
+ assert_block(build_message(message, "Expected response to be a >, but was >", type, @response.response_code)) { false }
+ end
+ end
+ end
+
+ # Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial,
+ # such that assert_redirected_to(:controller => "weblog") will also match the redirection of
+ # redirect_to(:controller => "weblog", :action => "show") and so on.
+ def assert_redirected_to(options = {}, message=nil)
+ clean_backtrace do
+ assert_response(:redirect, message)
+ return true if options == @response.redirected_to
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+
+ begin
+ url = {}
+ original = { :expected => options, :actual => @response.redirected_to.is_a?(Symbol) ? @response.redirected_to : @response.redirected_to.dup }
+ original.each do |key, value|
+ if value.is_a?(Symbol)
+ value = @controller.respond_to?(value, true) ? @controller.send(value) : @controller.send("hash_for_#{value}_url")
+ end
+
+ unless value.is_a?(Hash)
+ request = case value
+ when NilClass then nil
+ when /^\w+:\/\// then recognized_request_for(%r{^(\w+://.*?(/|$|\?))(.*)$} =~ value ? $3 : nil)
+ else recognized_request_for(value)
+ end
+ value = request.path_parameters if request
+ end
+
+ if value.is_a?(Hash) # stringify 2 levels of hash keys
+ if name = value.delete(:use_route)
+ route = ActionController::Routing::Routes.named_routes[name]
+ value.update(route.parameter_shell)
+ end
+
+ value.stringify_keys!
+ value.values.select { |v| v.is_a?(Hash) }.collect { |v| v.stringify_keys! }
+ if key == :expected && value['controller'] == @controller.controller_name && original[:actual].is_a?(Hash)
+ original[:actual].stringify_keys!
+ value.delete('controller') if original[:actual]['controller'].nil? || original[:actual]['controller'] == value['controller']
+ end
+ end
+
+ if value.respond_to?(:[]) && value['controller']
+ if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
+ new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
+ value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
+ end
+ value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
+ end
+ url[key] = value
+ end
+
+
+ @response_diff = url[:expected].diff(url[:actual]) if url[:actual]
+ msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is >), difference: >",
+ url[:actual], @response_diff)
+
+ assert_block(msg) do
+ url[:expected].keys.all? do |k|
+ if k == :controller then url[:expected][k] == ActionController::Routing.controller_relative_to(url[:actual][k], @controller.class.controller_path)
+ else parameterize(url[:expected][k]) == parameterize(url[:actual][k])
+ end
+ end
+ end
+ rescue ActionController::RoutingError # routing failed us, so match the strings only.
+ msg = build_message(message, "expected a redirect to >, found one to >", options, @response.redirect_url)
+ url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$}
+ eurl, epath, url, path = [options, @response.redirect_url].collect do |url|
+ u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url]
+ [u, (p.first == '/') ? p : '/' + p]
+ end.flatten
+
+ assert_equal(eurl, url, msg) if eurl && url
+ assert_equal(epath, path, msg) if epath && path
+ end
+ end
+ end
+
+ # Asserts that the request was rendered with the appropriate template file.
+ def assert_template(expected = nil, message=nil)
+ clean_backtrace do
+ rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file
+ msg = build_message(message, "expecting > but rendering with >", expected, rendered)
+ assert_block(msg) do
+ if expected.nil?
+ !@response.rendered_with_file?
+ else
+ expected == rendered
+ end
+ end
+ end
+ end
+
+ private
+ def recognized_request_for(path, request_method = nil)
+ path = "/#{path}" unless path.first == '/'
+
+ # Assume given controller
+ request = ActionController::TestRequest.new({}, {}, nil)
+ request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
+ request.path = path
+
+ ActionController::Routing::Routes.recognize(request)
+ request
+ end
+
+ def parameterize(value)
+ value.respond_to?(:to_param) ? value.to_param : value
+ end
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb
new file mode 100644
index 00000000..11a649c4
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb
@@ -0,0 +1,98 @@
+module ActionController
+ module Assertions
+ module RoutingAssertions
+ # Asserts that the routing of the given path was handled correctly and that the parsed options match.
+ #
+ # assert_recognizes({:controller => 'items', :action => 'index'}, 'items') # check the default action
+ # assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list') # check a specific action
+ # assert_recognizes({:controller => 'items', :action => 'list', :id => '1'}, 'items/list/1') # check an action with a parameter
+ #
+ # Pass a hash in the second argument to specify the request method. This is useful for routes
+ # requiring a specific HTTP method. The hash should contain a :path with the incoming request path
+ # and a :method containing the required HTTP verb.
+ #
+ # # assert that POSTing to /items will call the create action on ItemsController
+ # assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post})
+ #
+ # You can also pass in "extras" with a hash containing URL parameters that would normally be in the query string. This can be used
+ # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
+ # extras argument, appending the query string on the path directly will not work. For example:
+ #
+ # # assert that a path of '/items/list/1?view=print' returns the correct options
+ # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
+ def assert_recognizes(expected_options, path, extras={}, message=nil)
+ if path.is_a? Hash
+ request_method = path[:method]
+ path = path[:path]
+ else
+ request_method = nil
+ end
+
+ clean_backtrace do
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+ request = recognized_request_for(path, request_method)
+
+ expected_options = expected_options.clone
+ extras.each_key { |key| expected_options.delete key } unless extras.nil?
+
+ expected_options.stringify_keys!
+ routing_diff = expected_options.diff(request.path_parameters)
+ msg = build_message(message, "The recognized options > did not match >, difference: >",
+ request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
+ assert_block(msg) { request.path_parameters == expected_options }
+ end
+ end
+
+ # Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes.
+ # For example:
+ #
+ # assert_generates("/items", :controller => "items", :action => "index")
+ # assert_generates("/items/list", :controller => "items", :action => "list")
+ # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" })
+ def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
+ clean_backtrace do
+ expected_path = "/#{expected_path}" unless expected_path[0] == ?/
+ # Load routes.rb if it hasn't been loaded.
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+
+ generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
+ found_extras = options.reject {|k, v| ! extra_keys.include? k}
+
+ msg = build_message(message, "found extras >, not >", found_extras, extras)
+ assert_block(msg) { found_extras == extras }
+
+ msg = build_message(message, "The generated path > did not match >", generated_path,
+ expected_path)
+ assert_block(msg) { expected_path == generated_path }
+ end
+ end
+
+ # Asserts that path and options match both ways; in other words, the URL generated from
+ # options is the same as path, and also that the options recognized from path are the same as options. This
+ # essentially combines assert_recognizes and assert_generates into one step.
+ def assert_routing(path, options, defaults={}, extras={}, message=nil)
+ assert_recognizes(options, path, extras, message)
+
+ controller, default_controller = options[:controller], defaults[:controller]
+ if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
+ options[:controller] = "/#{controller}"
+ end
+
+ assert_generates(path, options, defaults, extras, message)
+ end
+
+ private
+ def recognized_request_for(path, request_method = nil)
+ path = "/#{path}" unless path.first == '/'
+
+ # Assume given controller
+ request = ActionController::TestRequest.new({}, {}, nil)
+ request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
+ request.path = path
+
+ ActionController::Routing::Routes.recognize(request)
+ request
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb
new file mode 100644
index 00000000..a5267e20
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb
@@ -0,0 +1,571 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require 'rexml/document'
+require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
+
+module ActionController
+ module Assertions
+ unless const_defined?(:NO_STRIP)
+ NO_STRIP = %w{pre script style textarea}
+ end
+
+ # Adds the #assert_select method for use in Rails functional
+ # test cases.
+ #
+ # Use #assert_select to make assertions on the response HTML of a controller
+ # action. You can also call #assert_select within another #assert_select to
+ # make assertions on elements selected by the enclosing assertion.
+ #
+ # Use #css_select to select elements without making an assertions, either
+ # from the response HTML or elements selected by the enclosing assertion.
+ #
+ # In addition to HTML responses, you can make the following assertions:
+ # * #assert_select_rjs -- Assertions on HTML content of RJS update and
+ # insertion operations.
+ # * #assert_select_encoded -- Assertions on HTML encoded inside XML,
+ # for example for dealing with feed item descriptions.
+ # * #assert_select_email -- Assertions on the HTML body of an e-mail.
+ #
+ # Also see HTML::Selector for learning how to use selectors.
+ module SelectorAssertions
+ # :call-seq:
+ # css_select(selector) => array
+ # css_select(element, selector) => array
+ #
+ # Select and return all matching elements.
+ #
+ # If called with a single argument, uses that argument as a selector
+ # to match all elements of the current page. Returns an empty array
+ # if no match is found.
+ #
+ # If called with two arguments, uses the first argument as the base
+ # element and the second argument as the selector. Attempts to match the
+ # base element and any of its children. Returns an empty array if no
+ # match is found.
+ #
+ # The selector may be a CSS selector expression (+String+), an expression
+ # with substitution values (+Array+) or an HTML::Selector object.
+ #
+ # For example:
+ # forms = css_select("form")
+ # forms.each do |form|
+ # inputs = css_select(form, "input")
+ # ...
+ # end
+ def css_select(*args)
+ # See assert_select to understand what's going on here.
+ arg = args.shift
+
+ if arg.is_a?(HTML::Node)
+ root = arg
+ arg = args.shift
+ elsif arg == nil
+ raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
+ elsif @selected
+ matches = []
+ @selected.each do |selected|
+ subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
+ subset.each do |match|
+ matches << match unless matches.any? { |m| m.equal?(match) }
+ end
+ end
+
+ return matches
+ else
+ root = response_from_page_or_rjs
+ end
+
+ case arg
+ when String
+ selector = HTML::Selector.new(arg, args)
+ when Array
+ selector = HTML::Selector.new(*arg)
+ when HTML::Selector
+ selector = arg
+ else raise ArgumentError, "Expecting a selector as the first argument"
+ end
+
+ selector.select(root)
+ end
+
+ # :call-seq:
+ # assert_select(selector, equality?, message?)
+ # assert_select(element, selector, equality?, message?)
+ #
+ # An assertion that selects elements and makes one or more equality tests.
+ #
+ # If the first argument is an element, selects all matching elements
+ # starting from (and including) that element and all its children in
+ # depth-first order.
+ #
+ # If no element if specified, calling #assert_select will select from the
+ # response HTML. Calling #assert_select inside an #assert_select block will
+ # run the assertion for each element selected by the enclosing assertion.
+ #
+ # For example:
+ # assert_select "ol>li" do |elements|
+ # elements.each do |element|
+ # assert_select element, "li"
+ # end
+ # end
+ # Or for short:
+ # assert_select "ol>li" do
+ # assert_select "li"
+ # end
+ #
+ # The selector may be a CSS selector expression (+String+), an expression
+ # with substitution values, or an HTML::Selector object.
+ #
+ # === Equality Tests
+ #
+ # The equality test may be one of the following:
+ # * true -- Assertion is true if at least one element selected.
+ # * false -- Assertion is true if no element selected.
+ # * String/Regexp -- Assertion is true if the text value of at least
+ # one element matches the string or regular expression.
+ # * Integer -- Assertion is true if exactly that number of
+ # elements are selected.
+ # * Range -- Assertion is true if the number of selected
+ # elements fit the range.
+ # If no equality test specified, the assertion is true if at least one
+ # element selected.
+ #
+ # To perform more than one equality tests, use a hash with the following keys:
+ # * :text -- Narrow the selection to elements that have this text
+ # value (string or regexp).
+ # * :html -- Narrow the selection to elements that have this HTML
+ # content (string or regexp).
+ # * :count -- Assertion is true if the number of selected elements
+ # is equal to this value.
+ # * :minimum -- Assertion is true if the number of selected
+ # elements is at least this value.
+ # * :maximum -- Assertion is true if the number of selected
+ # elements is at most this value.
+ #
+ # If the method is called with a block, once all equality tests are
+ # evaluated the block is called with an array of all matched elements.
+ #
+ # === Examples
+ #
+ # # At least one form element
+ # assert_select "form"
+ #
+ # # Form element includes four input fields
+ # assert_select "form input", 4
+ #
+ # # Page title is "Welcome"
+ # assert_select "title", "Welcome"
+ #
+ # # Page title is "Welcome" and there is only one title element
+ # assert_select "title", {:count=>1, :text=>"Welcome"},
+ # "Wrong title or more than one title element"
+ #
+ # # Page contains no forms
+ # assert_select "form", false, "This page must contain no forms"
+ #
+ # # Test the content and style
+ # assert_select "body div.header ul.menu"
+ #
+ # # Use substitution values
+ # assert_select "ol>li#?", /item-\d+/
+ #
+ # # All input fields in the form have a name
+ # assert_select "form input" do
+ # assert_select "[name=?]", /.+/ # Not empty
+ # end
+ def assert_select(*args, &block)
+ # Start with optional element followed by mandatory selector.
+ arg = args.shift
+
+ if arg.is_a?(HTML::Node)
+ # First argument is a node (tag or text, but also HTML root),
+ # so we know what we're selecting from.
+ root = arg
+ arg = args.shift
+ elsif arg == nil
+ # This usually happens when passing a node/element that
+ # happens to be nil.
+ raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
+ elsif @selected
+ root = HTML::Node.new(nil)
+ root.children.concat @selected
+ else
+ # Otherwise just operate on the response document.
+ root = response_from_page_or_rjs
+ end
+
+ # First or second argument is the selector: string and we pass
+ # all remaining arguments. Array and we pass the argument. Also
+ # accepts selector itself.
+ case arg
+ when String
+ selector = HTML::Selector.new(arg, args)
+ when Array
+ selector = HTML::Selector.new(*arg)
+ when HTML::Selector
+ selector = arg
+ else raise ArgumentError, "Expecting a selector as the first argument"
+ end
+
+ # Next argument is used for equality tests.
+ equals = {}
+ case arg = args.shift
+ when Hash
+ equals = arg
+ when String, Regexp
+ equals[:text] = arg
+ when Integer
+ equals[:count] = arg
+ when Range
+ equals[:minimum] = arg.begin
+ equals[:maximum] = arg.end
+ when FalseClass
+ equals[:count] = 0
+ when NilClass, TrueClass
+ equals[:minimum] = 1
+ else raise ArgumentError, "I don't understand what you're trying to match"
+ end
+
+ # By default we're looking for at least one match.
+ if equals[:count]
+ equals[:minimum] = equals[:maximum] = equals[:count]
+ else
+ equals[:minimum] = 1 unless equals[:minimum]
+ end
+
+ # Last argument is the message we use if the assertion fails.
+ message = args.shift
+ #- message = "No match made with selector #{selector.inspect}" unless message
+ if args.shift
+ raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
+ end
+
+ matches = selector.select(root)
+ # If text/html, narrow down to those elements that match it.
+ content_mismatch = nil
+ if match_with = equals[:text]
+ matches.delete_if do |match|
+ text = ""
+ stack = match.children.reverse
+ while node = stack.pop
+ if node.tag?
+ stack.concat node.children.reverse
+ else
+ text << node.content
+ end
+ 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)
+ true
+ end
+ end
+ elsif match_with = equals[:html]
+ matches.delete_if do |match|
+ 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)
+ true
+ end
+ end
+ end
+ # Expecting foo found bar element only if found zero, not if
+ # found one but expecting two.
+ message ||= content_mismatch if matches.empty?
+ # Test minimum/maximum occurrence.
+ if equals[:minimum]
+ assert matches.size >= equals[:minimum], message ||
+ "Expected at least #{equals[:minimum]} elements, found #{matches.size}."
+ end
+ if equals[:maximum]
+ assert matches.size <= equals[:maximum], message ||
+ "Expected at most #{equals[:maximum]} elements, found #{matches.size}."
+ end
+
+ # If a block is given call that block. Set @selected to allow
+ # nested assert_select, which can be nested several levels deep.
+ if block_given? && !matches.empty?
+ begin
+ in_scope, @selected = @selected, matches
+ yield matches
+ ensure
+ @selected = in_scope
+ end
+ end
+
+ # Returns all matches elements.
+ matches
+ end
+
+ # :call-seq:
+ # assert_select_rjs(id?) { |elements| ... }
+ # assert_select_rjs(statement, id?) { |elements| ... }
+ # assert_select_rjs(:insert, position, id?) { |elements| ... }
+ #
+ # Selects content from the RJS response.
+ #
+ # === Narrowing down
+ #
+ # With no arguments, asserts that one or more elements are updated or
+ # inserted by RJS statements.
+ #
+ # Use the +id+ argument to narrow down the assertion to only statements
+ # that update or insert an element with that identifier.
+ #
+ # Use the first argument to narrow down assertions to only statements
+ # of that type. Possible values are +:replace+, +:replace_html+ and
+ # +:insert_html+.
+ #
+ # Use the argument +:insert+ followed by an insertion position to narrow
+ # down the assertion to only statements that insert elements in that
+ # position. Possible values are +:top+, +:bottom+, +:before+ and +:after+.
+ #
+ # === Using blocks
+ #
+ # Without a block, #assert_select_rjs merely asserts that the response
+ # contains one or more RJS statements that replace or update content.
+ #
+ # With a block, #assert_select_rjs also selects all elements used in
+ # these statements and passes them to the block. Nested assertions are
+ # supported.
+ #
+ # Calling #assert_select_rjs with no arguments and using nested asserts
+ # asserts that the HTML content is returned by one or more RJS statements.
+ # Using #assert_select directly makes the same assertion on the content,
+ # but without distinguishing whether the content is returned in an HTML
+ # or JavaScript.
+ #
+ # === Examples
+ #
+ # # Replacing the element foo.
+ # # page.replace 'foo', ...
+ # assert_select_rjs :replace, "foo"
+ #
+ # # Replacing with the chained RJS proxy.
+ # # page[:foo].replace ...
+ # assert_select_rjs :chained_replace, 'foo'
+ #
+ # # Inserting into the element bar, top position.
+ # assert_select_rjs :insert, :top, "bar"
+ #
+ # # Changing the element foo, with an image.
+ # assert_select_rjs "foo" do
+ # assert_select "img[src=/images/logo.gif""
+ # end
+ #
+ # # RJS inserts or updates a list with four items.
+ # assert_select_rjs do
+ # assert_select "ol>li", 4
+ # end
+ #
+ # # The same, but shorter.
+ # assert_select "ol>li", 4
+ def assert_select_rjs(*args, &block)
+ rjs_type = nil
+ arg = args.shift
+
+ # If the first argument is a symbol, it's the type of RJS statement we're looking
+ # for (update, replace, insertion, etc). Otherwise, we're looking for just about
+ # any RJS statement.
+ if arg.is_a?(Symbol)
+ rjs_type = arg
+ if rjs_type == :insert
+ arg = args.shift
+ insertion = "insert_#{arg}".to_sym
+ raise ArgumentError, "Unknown RJS insertion type #{arg}" unless RJS_STATEMENTS[insertion]
+ statement = "(#{RJS_STATEMENTS[insertion]})"
+ else
+ raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
+ statement = "(#{RJS_STATEMENTS[rjs_type]})"
+ end
+ arg = args.shift
+ else
+ statement = "#{RJS_STATEMENTS[:any]}"
+ end
+
+ # Next argument we're looking for is the element identifier. If missing, we pick
+ # any element.
+ if arg.is_a?(String)
+ id = Regexp.quote(arg)
+ arg = args.shift
+ else
+ id = "[^\"]*"
+ end
+
+ pattern =
+ case rjs_type
+ when :chained_replace, :chained_replace_html
+ Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
+ else
+ Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
+ end
+
+ # Duplicate the body since the next step involves destroying it.
+ matches = nil
+ @response.body.gsub(pattern) do |match|
+ html = unescape_rjs($2)
+ matches ||= []
+ matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
+ ""
+ end
+ if matches
+ if block_given?
+ begin
+ in_scope, @selected = @selected, matches
+ yield matches
+ ensure
+ @selected = in_scope
+ end
+ end
+ matches
+ else
+ # RJS statement not found.
+ flunk args.shift || "No RJS statement that replaces or inserts HTML content."
+ end
+ end
+
+ # :call-seq:
+ # assert_select_encoded(element?) { |elements| ... }
+ #
+ # Extracts the content of an element, treats it as encoded HTML and runs
+ # nested assertion on it.
+ #
+ # You typically call this method within another assertion to operate on
+ # all currently selected elements. You can also pass an element or array
+ # of elements.
+ #
+ # The content of each element is un-encoded, and wrapped in the root
+ # element +encoded+. It then calls the block with all un-encoded elements.
+ #
+ # === Example
+ #
+ # assert_select_feed :rss, 2.0 do
+ # # Select description element of each feed item.
+ # assert_select "channel>item>description" do
+ # # Run assertions on the encoded elements.
+ # assert_select_encoded do
+ # assert_select "p"
+ # end
+ # end
+ # end
+ def assert_select_encoded(element = nil, &block)
+ case element
+ when Array
+ elements = element
+ when HTML::Node
+ elements = [element]
+ when nil
+ unless elements = @selected
+ raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
+ end
+ else
+ raise ArgumentError, "Argument is optional, and may be node or array of nodes"
+ end
+
+ fix_content = lambda do |node|
+ # Gets around a bug in the Rails 1.1 HTML parser.
+ node.content.gsub(/)?/m) { CGI.escapeHTML($1) }
+ end
+
+ selected = elements.map do |element|
+ text = element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
+ root = HTML::Document.new(CGI.unescapeHTML("#{text}")).root
+ css_select(root, "encoded:root", &block)[0]
+ end
+
+ begin
+ old_selected, @selected = @selected, selected
+ assert_select ":root", &block
+ ensure
+ @selected = old_selected
+ end
+ end
+
+ # :call-seq:
+ # assert_select_email { }
+ #
+ # Extracts the body of an email and runs nested assertions on it.
+ #
+ # You must enable deliveries for this assertion to work, use:
+ # ActionMailer::Base.perform_deliveries = true
+ #
+ # === Example
+ #
+ # assert_select_email do
+ # assert_select "h1", "Email alert"
+ # end
+ def assert_select_email(&block)
+ deliveries = ActionMailer::Base.deliveries
+ assert !deliveries.empty?, "No e-mail in delivery list"
+
+ for delivery in deliveries
+ for part in delivery.parts
+ if part["Content-Type"].to_s =~ /^text\/html\W/
+ root = HTML::Document.new(part.body).root
+ assert_select root, ":root", &block
+ end
+ end
+ end
+ end
+
+ protected
+ unless const_defined?(:RJS_STATEMENTS)
+ RJS_STATEMENTS = {
+ :replace => /Element\.replace/,
+ :replace_html => /Element\.update/,
+ :chained_replace => /\.replace/,
+ :chained_replace_html => /\.update/,
+ }
+ RJS_INSERTIONS = [:top, :bottom, :before, :after]
+ RJS_INSERTIONS.each do |insertion|
+ RJS_STATEMENTS["insert_#{insertion}".to_sym] = Regexp.new(Regexp.quote("new Insertion.#{insertion.to_s.camelize}"))
+ end
+ RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
+ RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion|
+ Regexp.quote("new Insertion.#{insertion.to_s.camelize}")
+ end.join('|'))
+ RJS_PATTERN_HTML = /"((\\"|[^"])*)"/
+ RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)",
+ Regexp::MULTILINE)
+ RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
+ end
+
+ # #assert_select and #css_select call this to obtain the content in the HTML
+ # page, or from all the RJS statements, depending on the type of response.
+ def response_from_page_or_rjs()
+ content_type = @response.headers["Content-Type"]
+ if content_type && content_type =~ /text\/javascript/
+ body = @response.body.dup
+ root = HTML::Node.new(nil)
+ while true
+ next if body.sub!(RJS_PATTERN_EVERYTHING) do |match|
+ html = unescape_rjs($3)
+ matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
+ root.children.concat matches
+ ""
+ end
+ break
+ end
+ root
+ else
+ html_document.root
+ end
+ end
+
+ # Unescapes a RJS string.
+ def unescape_rjs(rjs_string)
+ # RJS encodes double quotes and line breaks.
+ unescaped= rjs_string.gsub('\"', '"')
+ unescaped.gsub!('\n', "\n")
+ # RJS encodes non-ascii characters.
+ unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
+ unescaped
+ end
+
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb
new file mode 100644
index 00000000..f5f7a23e
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb
@@ -0,0 +1,117 @@
+require 'rexml/document'
+require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
+
+module ActionController
+ module Assertions
+ module TagAssertions
+ # Asserts that there is a tag/node/element in the body of the response
+ # that meets all of the given conditions. The +conditions+ parameter must
+ # be a hash of any of the following keys (all are optional):
+ #
+ # * :tag: the node type must match the corresponding value
+ # * :attributes: a hash. The node's attributes must match the
+ # corresponding values in the hash.
+ # * :parent: a hash. The node's parent must match the
+ # corresponding hash.
+ # * :child: a hash. At least one of the node's immediate children
+ # must meet the criteria described by the hash.
+ # * :ancestor: a hash. At least one of the node's ancestors must
+ # meet the criteria described by the hash.
+ # * :descendant: a hash. At least one of the node's descendants
+ # must meet the criteria described by the hash.
+ # * :sibling: a hash. At least one of the node's siblings must
+ # meet the criteria described by the hash.
+ # * :after: a hash. The node must be after any sibling meeting
+ # the criteria described by the hash, and at least one sibling must match.
+ # * :before: a hash. The node must be before any sibling meeting
+ # the criteria described by the hash, and at least one sibling must match.
+ # * :children: a hash, for counting children of a node. Accepts
+ # the keys:
+ # * :count: either a number or a range which must equal (or
+ # include) the number of children that match.
+ # * :less_than: the number of matching children must be less
+ # than this number.
+ # * :greater_than: the number of matching children must be
+ # greater than this number.
+ # * :only: another hash consisting of the keys to use
+ # to match on the children, and only matching children will be
+ # counted.
+ # * :content: the textual content of the node must match the
+ # given value. This will not match HTML tags in the body of a
+ # tag--only text.
+ #
+ # Conditions are matched using the following algorithm:
+ #
+ # * if the condition is a string, it must be a substring of the value.
+ # * if the condition is a regexp, it must match the value.
+ # * if the condition is a number, the value must match number.to_s.
+ # * if the condition is +true+, the value must not be +nil+.
+ # * if the condition is +false+ or +nil+, the value must be +nil+.
+ #
+ # Usage:
+ #
+ # # assert that there is a "span" tag
+ # assert_tag :tag => "span"
+ #
+ # # assert that there is a "span" tag with id="x"
+ # assert_tag :tag => "span", :attributes => { :id => "x" }
+ #
+ # # assert that there is a "span" tag using the short-hand
+ # assert_tag :span
+ #
+ # # assert that there is a "span" tag with id="x" using the short-hand
+ # assert_tag :span, :attributes => { :id => "x" }
+ #
+ # # assert that there is a "span" inside of a "div"
+ # assert_tag :tag => "span", :parent => { :tag => "div" }
+ #
+ # # assert that there is a "span" somewhere inside a table
+ # assert_tag :tag => "span", :ancestor => { :tag => "table" }
+ #
+ # # assert that there is a "span" with at least one "em" child
+ # assert_tag :tag => "span", :child => { :tag => "em" }
+ #
+ # # assert that there is a "span" containing a (possibly nested)
+ # # "strong" tag.
+ # assert_tag :tag => "span", :descendant => { :tag => "strong" }
+ #
+ # # assert that there is a "span" containing between 2 and 4 "em" tags
+ # # as immediate children
+ # assert_tag :tag => "span",
+ # :children => { :count => 2..4, :only => { :tag => "em" } }
+ #
+ # # get funky: assert that there is a "div", with an "ul" ancestor
+ # # and an "li" parent (with "class" = "enum"), and containing a
+ # # "span" descendant that contains text matching /hello world/
+ # assert_tag :tag => "div",
+ # :ancestor => { :tag => "ul" },
+ # :parent => { :tag => "li",
+ # :attributes => { :class => "enum" } },
+ # :descendant => { :tag => "span",
+ # :child => /hello world/ }
+ #
+ # Please noteYou must explicitly
+ # close all of your tags to use these assertions.
+ def assert_tag(*opts)
+ clean_backtrace do
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
+ tag = find_tag(opts)
+ assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
+ end
+ end
+
+ # Identical to #assert_tag, but asserts that a matching tag does _not_
+ # exist. (See #assert_tag for a full discussion of the syntax.)
+ def assert_no_tag(*opts)
+ clean_backtrace do
+ opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
+ tag = find_tag(opts)
+ assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb b/vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb
new file mode 100644
index 00000000..433b9e5a
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb
@@ -0,0 +1,65 @@
+module ActionController #:nodoc:
+ module Dependencies #:nodoc:
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ # Deprecated module. The responsibility of loading dependencies belong with Active Support now.
+ module ClassMethods #:nodoc:
+ # Specifies a variable number of models that this controller depends on. Models are normally Active Record classes or a similar
+ # backend for modelling entity classes.
+ def model(*models)
+ require_dependencies(:model, models)
+ depend_on(:model, models)
+ end
+ deprecate :model
+
+ # Specifies a variable number of services that this controller depends on. Services are normally singletons or factories, like
+ # Action Mailer service or a Payment Gateway service.
+ def service(*services)
+ require_dependencies(:service, services)
+ depend_on(:service, services)
+ end
+ deprecate :service
+
+ # Specifies a variable number of observers that are to govern when this controller is handling actions. The observers will
+ # automatically have .instance called on them to make them active on assignment.
+ def observer(*observers)
+ require_dependencies(:observer, observers)
+ depend_on(:observer, observers)
+ instantiate_observers(observers)
+ end
+ deprecate :observer
+
+ # Returns an array of symbols that specify the dependencies on a given layer. For the example at the top, calling
+ # ApplicationController.dependencies_on(:model) would return [:account, :company, :person, :project, :category]
+ def dependencies_on(layer)
+ read_inheritable_attribute("#{layer}_dependencies")
+ end
+ deprecate :dependencies_on
+
+ def depend_on(layer, dependencies) #:nodoc:
+ write_inheritable_array("#{layer}_dependencies", dependencies)
+ end
+ deprecate :depend_on
+
+ private
+ def instantiate_observers(observers)
+ observers.flatten.each { |observer| Object.const_get(Inflector.classify(observer.to_s)).instance }
+ end
+
+ def require_dependencies(layer, dependencies)
+ dependencies.flatten.each do |dependency|
+ begin
+ require_dependency(dependency.to_s)
+ rescue LoadError => e
+ raise LoadError.new("Missing #{layer} #{dependency}.rb").copy_blame!(e)
+ rescue Exception => exception # error from loaded file
+ exception.blame_file! "=> #{layer} #{dependency}.rb"
+ raise
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_controller/resources.rb b/vendor/rails/actionpack/lib/action_controller/resources.rb
new file mode 100644
index 00000000..d55e7f02
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/resources.rb
@@ -0,0 +1,405 @@
+module ActionController
+ module Resources
+ class Resource #:nodoc:
+ attr_reader :collection_methods, :member_methods, :new_methods
+ attr_reader :path_prefix, :name_prefix
+ attr_reader :plural, :singular
+ attr_reader :options
+
+ def initialize(entities, options)
+ @plural = entities
+ @singular = options[:singular] || plural.to_s.singularize
+
+ @options = options
+
+ arrange_actions
+ add_default_actions
+ set_prefixes
+ end
+
+ def controller
+ @controller ||= (options[:controller] || plural).to_s
+ end
+
+ def path
+ @path ||= "#{path_prefix}/#{plural}"
+ end
+
+ def new_path
+ @new_path ||= "#{path}/new"
+ end
+
+ def member_path
+ @member_path ||= "#{path}/:id"
+ end
+
+ def nesting_path_prefix
+ @nesting_path_prefix ||= "#{path}/:#{singular}_id"
+ end
+
+ protected
+ def arrange_actions
+ @collection_methods = arrange_actions_by_methods(options.delete(:collection))
+ @member_methods = arrange_actions_by_methods(options.delete(:member))
+ @new_methods = arrange_actions_by_methods(options.delete(:new))
+ end
+
+ def add_default_actions
+ add_default_action(member_methods, :get, :edit)
+ add_default_action(new_methods, :get, :new)
+ end
+
+ def set_prefixes
+ @path_prefix = options.delete(:path_prefix)
+ @name_prefix = options.delete(:name_prefix)
+ end
+
+ def arrange_actions_by_methods(actions)
+ (actions || {}).inject({}) do |flipped_hash, (key, value)|
+ (flipped_hash[value] ||= []) << key
+ flipped_hash
+ end
+ end
+
+ def add_default_action(collection, method, action)
+ (collection[method] ||= []).unshift(action)
+ end
+ end
+
+ class SingletonResource < Resource #:nodoc:
+ def initialize(entity, options)
+ @plural = @singular = entity
+ @options = options
+ arrange_actions
+ add_default_actions
+ set_prefixes
+ end
+
+ alias_method :member_path, :path
+ alias_method :nesting_path_prefix, :path
+ end
+
+ # Creates named routes for implementing verb-oriented controllers. This is
+ # useful for implementing REST API's, where a single resource has different
+ # behavior based on the HTTP verb (method) used to access it.
+ #
+ # Example:
+ #
+ # map.resources :messages
+ #
+ # class MessagesController < ActionController::Base
+ # # GET messages_url
+ # def index
+ # # return all messages
+ # end
+ #
+ # # GET new_message_url
+ # def new
+ # # return an HTML form for describing a new message
+ # end
+ #
+ # # POST messages_url
+ # def create
+ # # create a new message
+ # end
+ #
+ # # GET message_url(:id => 1)
+ # def show
+ # # find and return a specific message
+ # end
+ #
+ # # GET edit_message_url(:id => 1)
+ # def edit
+ # # return an HTML form for editing a specific message
+ # end
+ #
+ # # PUT message_url(:id => 1)
+ # def update
+ # # find and update a specific message
+ # end
+ #
+ # # DELETE message_url(:id => 1)
+ # def destroy
+ # # delete a specific message
+ # end
+ # end
+ #
+ # The #resources method sets HTTP method restrictions on the routes it generates. For example, making an
+ # HTTP POST on new_message_url will raise a RoutingError exception. The default route in
+ # config/routes.rb overrides this and allows invalid HTTP methods for resource routes.
+ #
+ # Along with the routes themselves, #resources generates named routes for use in
+ # controllers and views. map.resources :messages produces the following named routes and helpers:
+ #
+ # Named Route Helpers
+ # messages messages_url, hash_for_messages_url,
+ # messages_path, hash_for_messages_path
+ # message message_url(id), hash_for_message_url(id),
+ # message_path(id), hash_for_message_path(id)
+ # new_message new_message_url, hash_for_new_message_url,
+ # new_message_path, hash_for_new_message_path
+ # edit_message edit_message_url(id), hash_for_edit_message_url(id),
+ # edit_message_path(id), hash_for_edit_message_path(id)
+ #
+ # You can use these helpers instead of #url_for or methods that take #url_for parameters:
+ #
+ # redirect_to :controller => 'messages', :action => 'index'
+ # # becomes
+ # redirect_to messages_url
+ #
+ # <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
+ # # becomes
+ # <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
+ #
+ # Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
+ # form tags. The form helpers make this a little easier. For an update form with a @message object:
+ #
+ # <%= form_tag message_path(@message), :method => :put %>
+ #
+ # or
+ #
+ # <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
+ #
+ # The #resources method accepts various options, too, to customize the resulting
+ # routes:
+ # * :controller -- specify the controller name for the routes.
+ # * :singular -- specify the singular name used in the member routes.
+ # * :path_prefix -- set a prefix to the routes with required route variables.
+ # Weblog comments usually belong to a post, so you might use resources like:
+ #
+ # map.resources :articles
+ # map.resources :comments, :path_prefix => '/articles/:article_id'
+ #
+ # You can nest resources calls to set this automatically:
+ #
+ # map.resources :articles do |article|
+ # article.resources :comments
+ # end
+ #
+ # The comment resources work the same, but must now include a value for :article_id.
+ #
+ # comments_url(@article)
+ # comment_url(@article, @comment)
+ #
+ # comments_url(:article_id => @article)
+ # comment_url(:article_id => @article, :id => @comment)
+ #
+ # * :name_prefix -- define a prefix for all generated routes, usually ending in an underscore.
+ # Use this if you have named routes that may clash.
+ #
+ # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
+ # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
+ #
+ # * :collection -- add named routes for other actions that operate on the collection.
+ # Takes a hash of #{action} => #{method}, where method is :get/:post/:put/:delete
+ # or :any if the method does not matter. These routes map to a URL like /messages;rss, with a route of rss_messages_url.
+ # * :member -- same as :collection, but for actions that operate on a specific member.
+ # * :new -- same as :collection, but for actions that operate on the new resource action.
+ #
+ # If map.resources is called with multiple resources, they all get the same options applied.
+ #
+ # Examples:
+ #
+ # map.resources :messages, :path_prefix => "/thread/:thread_id"
+ # # --> GET /thread/7/messages/1
+ #
+ # map.resources :messages, :collection => { :rss => :get }
+ # # --> GET /messages;rss (maps to the #rss action)
+ # # also adds a named route called "rss_messages"
+ #
+ # map.resources :messages, :member => { :mark => :post }
+ # # --> POST /messages/1;mark (maps to the #mark action)
+ # # also adds a named route called "mark_message"
+ #
+ # map.resources :messages, :new => { :preview => :post }
+ # # --> POST /messages/new;preview (maps to the #preview action)
+ # # also adds a named route called "preview_new_message"
+ #
+ # map.resources :messages, :new => { :new => :any, :preview => :post }
+ # # --> POST /messages/new;preview (maps to the #preview action)
+ # # also adds a named route called "preview_new_message"
+ # # --> /messages/new can be invoked via any request method
+ #
+ # map.resources :messages, :controller => "categories",
+ # :path_prefix => "/category/:category_id",
+ # :name_prefix => "category_"
+ # # --> GET /categories/7/messages/1
+ # # has named route "category_message"
+ def resources(*entities, &block)
+ options = entities.last.is_a?(Hash) ? entities.pop : { }
+ entities.each { |entity| map_resource entity, options.dup, &block }
+ end
+
+ # Creates named routes for implementing verb-oriented controllers for a singleton resource.
+ # A singleton resource is global to the current user visiting the application, such as a user's
+ # /account profile.
+ #
+ # See map.resources for general conventions. These are the main differences:
+ # - a singular name is given to map.resource. The default controller name is taken from the singular name.
+ # - To specify a custom plural name, use the :plural option. There is no :singular option
+ # - No default index, new, or create routes are created for the singleton resource controller.
+ # - When nesting singleton resources, only the singular name is used as the path prefix (example: 'account/messages/1')
+ #
+ # Example:
+ #
+ # map.resource :account
+ #
+ # class AccountController < ActionController::Base
+ # # POST account_url
+ # def create
+ # # create an account
+ # end
+ #
+ # # GET new_account_url
+ # def new
+ # # return an HTML form for describing the new account
+ # end
+ #
+ # # GET account_url
+ # def show
+ # # find and return the account
+ # end
+ #
+ # # GET edit_account_url
+ # def edit
+ # # return an HTML form for editing the account
+ # end
+ #
+ # # PUT account_url
+ # def update
+ # # find and update the account
+ # end
+ #
+ # # DELETE account_url
+ # def destroy
+ # # delete the account
+ # end
+ # end
+ #
+ # Along with the routes themselves, #resource generates named routes for use in
+ # controllers and views. map.resource :account produces the following named routes and helpers:
+ #
+ # Named Route Helpers
+ # account account_url, hash_for_account_url,
+ # account_path, hash_for_account_path
+ # edit_account edit_account_url, hash_for_edit_account_url,
+ # edit_account_path, hash_for_edit_account_path
+ def resource(*entities, &block)
+ options = entities.last.is_a?(Hash) ? entities.pop : { }
+ entities.each { |entity| map_singleton_resource entity, options.dup, &block }
+ end
+
+ private
+ def map_resource(entities, options = {}, &block)
+ resource = Resource.new(entities, options)
+
+ with_options :controller => resource.controller do |map|
+ map_collection_actions(map, resource)
+ map_default_collection_actions(map, resource)
+ map_new_actions(map, resource)
+ map_member_actions(map, resource)
+
+ if block_given?
+ with_options(:path_prefix => resource.nesting_path_prefix, &block)
+ end
+ end
+ end
+
+ def map_singleton_resource(entities, options = {}, &block)
+ resource = SingletonResource.new(entities, options)
+
+ with_options :controller => resource.controller do |map|
+ map_collection_actions(map, resource)
+ map_default_singleton_actions(map, resource)
+ map_new_actions(map, resource)
+ map_member_actions(map, resource)
+
+ if block_given?
+ with_options(:path_prefix => resource.nesting_path_prefix, &block)
+ end
+ end
+ end
+
+ def map_collection_actions(map, resource)
+ resource.collection_methods.each do |method, actions|
+ actions.each do |action|
+ action_options = action_options_for(action, resource, method)
+ map.named_route("#{resource.name_prefix}#{action}_#{resource.plural}", "#{resource.path};#{action}", action_options)
+ map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.plural}", "#{resource.path}.:format;#{action}", action_options)
+ end
+ end
+ end
+
+ def map_default_collection_actions(map, resource)
+ index_action_options = action_options_for("index", resource)
+ map.named_route("#{resource.name_prefix}#{resource.plural}", resource.path, index_action_options)
+ map.named_route("formatted_#{resource.name_prefix}#{resource.plural}", "#{resource.path}.:format", index_action_options)
+
+ create_action_options = action_options_for("create", resource)
+ map.connect(resource.path, create_action_options)
+ map.connect("#{resource.path}.:format", create_action_options)
+ end
+
+ def map_default_singleton_actions(map, resource)
+ create_action_options = action_options_for("create", resource)
+ map.connect(resource.path, create_action_options)
+ map.connect("#{resource.path}.:format", create_action_options)
+ end
+
+ def map_new_actions(map, resource)
+ resource.new_methods.each do |method, actions|
+ actions.each do |action|
+ action_options = action_options_for(action, resource, method)
+ if action == :new
+ map.named_route("#{resource.name_prefix}new_#{resource.singular}", resource.new_path, action_options)
+ map.named_route("formatted_#{resource.name_prefix}new_#{resource.singular}", "#{resource.new_path}.:format", action_options)
+ else
+ map.named_route("#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path};#{action}", action_options)
+ map.named_route("formatted_#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path}.:format;#{action}", action_options)
+ end
+ end
+ end
+ end
+
+ def map_member_actions(map, resource)
+ resource.member_methods.each do |method, actions|
+ actions.each do |action|
+ action_options = action_options_for(action, resource, method)
+ map.named_route("#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path};#{action}", action_options)
+ map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path}.:format;#{action}",action_options)
+ end
+ end
+
+ show_action_options = action_options_for("show", resource)
+ map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
+ map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)
+
+ update_action_options = action_options_for("update", resource)
+ map.connect(resource.member_path, update_action_options)
+ map.connect("#{resource.member_path}.:format", update_action_options)
+
+ destroy_action_options = action_options_for("destroy", resource)
+ map.connect(resource.member_path, destroy_action_options)
+ map.connect("#{resource.member_path}.:format", destroy_action_options)
+ end
+
+ def conditions_for(method)
+ { :conditions => method == :any ? {} : { :method => method } }
+ end
+
+ def action_options_for(action, resource, method = nil)
+ default_options = { :action => action.to_s }
+ require_id = resource.kind_of?(SingletonResource) ? {} : { :requirements => { :id => Regexp.new("[^#{Routing::SEPARATORS.join}]+") } }
+ case default_options[:action]
+ when "index", "new" : default_options.merge(conditions_for(method || :get))
+ when "create" : default_options.merge(conditions_for(method || :post))
+ when "show", "edit" : default_options.merge(conditions_for(method || :get)).merge(require_id)
+ when "update" : default_options.merge(conditions_for(method || :put)).merge(require_id)
+ when "destroy" : default_options.merge(conditions_for(method || :delete)).merge(require_id)
+ else default_options.merge(conditions_for(method))
+ end
+ end
+ end
+end
+
+ActionController::Routing::RouteSet::Mapper.send :include, ActionController::Resources
diff --git a/vendor/rails/actionpack/lib/action_controller/status_codes.rb b/vendor/rails/actionpack/lib/action_controller/status_codes.rb
new file mode 100644
index 00000000..4977c794
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/status_codes.rb
@@ -0,0 +1,88 @@
+module ActionController
+ module StatusCodes #:nodoc:
+ # Defines the standard HTTP status codes, by integer, with their
+ # corresponding default message texts.
+ # Source: http://www.iana.org/assignments/http-status-codes
+ STATUS_CODES = {
+ 100 => "Continue",
+ 101 => "Switching Protocols",
+ 102 => "Processing",
+
+ 200 => "OK",
+ 201 => "Created",
+ 202 => "Accepted",
+ 203 => "Non-Authoritative Information",
+ 204 => "No Content",
+ 205 => "Reset Content",
+ 206 => "Partial Content",
+ 207 => "Multi-Status",
+ 226 => "IM Used",
+
+ 300 => "Multiple Choices",
+ 301 => "Moved Permanently",
+ 302 => "Found",
+ 303 => "See Other",
+ 304 => "Not Modified",
+ 305 => "Use Proxy",
+ 307 => "Temporary Redirect",
+
+ 400 => "Bad Request",
+ 401 => "Unauthorized",
+ 402 => "Payment Required",
+ 403 => "Forbidden",
+ 404 => "Not Found",
+ 405 => "Method Not Allowed",
+ 406 => "Not Acceptable",
+ 407 => "Proxy Authentication Required",
+ 408 => "Request Timeout",
+ 409 => "Conflict",
+ 410 => "Gone",
+ 411 => "Length Required",
+ 412 => "Precondition Failed",
+ 413 => "Request Entity Too Large",
+ 414 => "Request-URI Too Long",
+ 415 => "Unsupported Media Type",
+ 416 => "Requested Range Not Satisfiable",
+ 417 => "Expectation Failed",
+ 422 => "Unprocessable Entity",
+ 423 => "Locked",
+ 424 => "Failed Dependency",
+ 426 => "Upgrade Required",
+
+ 500 => "Internal Server Error",
+ 501 => "Not Implemented",
+ 502 => "Bad Gateway",
+ 503 => "Service Unavailable",
+ 504 => "Gateway Timeout",
+ 505 => "HTTP Version Not Supported",
+ 507 => "Insufficient Storage",
+ 510 => "Not Extended"
+ }
+
+ # Provides a symbol-to-fixnum lookup for converting a symbol (like
+ # :created or :not_implemented) into its corresponding HTTP status
+ # code (like 200 or 501).
+ SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) do |hash, (code, message)|
+ hash[message.gsub(/ /, "").underscore.to_sym] = code
+ hash
+ end
+
+ # Given a status parameter, determine whether it needs to be converted
+ # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
+ # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
+ # hash to convert it.
+ def interpret_status(status)
+ case status
+ when Fixnum then
+ "#{status} #{STATUS_CODES[status]}".strip
+ when Symbol then
+ interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
+ "500 Unknown Status #{status.inspect}")
+ else
+ status.to_s
+ end
+ end
+ private :interpret_status
+
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
new file mode 100644
index 00000000..287afcc3
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -0,0 +1,823 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+module HTML
+
+ # Selects HTML elements using CSS 2 selectors.
+ #
+ # The +Selector+ class uses CSS selector expressions to match and select
+ # HTML elements.
+ #
+ # For example:
+ # selector = HTML::Selector.new "form.login[action=/login]"
+ # creates a new selector that matches any +form+ element with the class
+ # +login+ and an attribute +action+ with the value /login.
+ #
+ # === Matching Elements
+ #
+ # Use the #match method to determine if an element matches the selector.
+ #
+ # For simple selectors, the method returns an array with that element,
+ # or +nil+ if the element does not match. For complex selectors (see below)
+ # the method returns an array with all matched elements, of +nil+ if no
+ # match found.
+ #
+ # For example:
+ # if selector.match(element)
+ # puts "Element is a login form"
+ # end
+ #
+ # === Selecting Elements
+ #
+ # Use the #select method to select all matching elements starting with
+ # one element and going through all children in depth-first order.
+ #
+ # This method returns an array of all matching elements, an empty array
+ # if no match is found
+ #
+ # For example:
+ # selector = HTML::Selector.new "input[type=text]"
+ # matches = selector.select(element)
+ # matches.each do |match|
+ # puts "Found text field with name #{match.attributes['name']}"
+ # end
+ #
+ # === Expressions
+ #
+ # Selectors can match elements using any of the following criteria:
+ # * name -- Match an element based on its name (tag name).
+ # For example, p to match a paragraph. You can use *
+ # to match any element.
+ # * #id -- Match an element based on its identifier (the
+ # id attribute). For example, #page.
+ # * .class -- Match an element based on its class name, all
+ # class names if more than one specified.
+ # * [attr] -- Match an element that has the specified attribute.
+ # * [attr=value] -- Match an element that has the specified
+ # attribute and value. (More operators are supported see below)
+ # * :pseudo-class -- Match an element based on a pseudo class,
+ # such as :nth-child and :empty.
+ # * :not(expr) -- Match an element that does not match the
+ # negation expression.
+ #
+ # When using a combination of the above, the element name comes first
+ # followed by identifier, class names, attributes, pseudo classes and
+ # negation in any order. Do not seprate these parts with spaces!
+ # Space separation is used for descendant selectors.
+ #
+ # For example:
+ # selector = HTML::Selector.new "form.login[action=/login]"
+ # The matched element must be of type +form+ and have the class +login+.
+ # It may have other classes, but the class +login+ is required to match.
+ # It must also have an attribute called +action+ with the value
+ # /login.
+ #
+ # This selector will match the following element:
+ #
+ ]]>
+
+
+ -
+
+ Test 2
+ ]]>
+
+
+
+
+EOF
+ assert_select "channel item description" do
+ # Test element regardless of wrapper.
+ assert_select_encoded do
+ assert_select "p", :count=>2, :text=>/Test/
+ end
+ # Test through encoded wrapper.
+ assert_select_encoded do
+ assert_select "encoded p", :count=>2, :text=>/Test/
+ end
+ # Use :root instead (recommended)
+ assert_select_encoded do
+ assert_select ":root p", :count=>2, :text=>/Test/
+ end
+ # Test individually.
+ assert_select "description" do |elements|
+ assert_select_encoded elements[0] do
+ assert_select "p", "Test 1"
+ end
+ assert_select_encoded elements[1] do
+ assert_select "p", "Test 2"
+ end
+ end
+ end
+
+ # Test that we only un-encode element itself.
+ assert_select "channel item" do
+ assert_select_encoded do
+ assert_select "p", 0
+ end
+ end
+ end
+
+
+ #
+ # Test assert_select_email
+ #
+
+ def test_assert_select_email
+ assert_raises(AssertionFailedError) { assert_select_email {} }
+ AssertSelectMailer.deliver_test ""
+ assert_select_email do
+ assert_select "div:root" do
+ assert_select "p:first-child", "foo"
+ assert_select "p:last-child", "bar"
+ end
+ end
+ end
+
+
+ protected
+ def render_html(html)
+ @controller.response_with = html
+ get :html
+ end
+
+ def render_rjs(&block)
+ @controller.response_with &block
+ get :rjs
+ end
+
+ def render_xml(xml)
+ @controller.response_with = xml
+ get :xml
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/caching_test.rb b/vendor/rails/actionpack/test/controller/caching_test.rb
new file mode 100644
index 00000000..07e16896
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/caching_test.rb
@@ -0,0 +1,228 @@
+require 'fileutils'
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+CACHE_DIR = 'test_cache'
+# Don't change '/../temp/' cavalierly or you might hoze something you don't want hozed
+FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
+ActionController::Base.perform_caching = true
+ActionController::Base.page_cache_directory = FILE_STORE_PATH
+ActionController::Base.fragment_cache_store = :file_store, FILE_STORE_PATH
+
+class PageCachingTestController < ActionController::Base
+ caches_page :ok, :no_content, :found, :not_found
+
+ def ok
+ head :ok
+ end
+
+ def no_content
+ head :no_content
+ end
+
+ def found
+ redirect_to :action => 'ok'
+ end
+
+ def not_found
+ head :not_found
+ end
+end
+
+class PageCachingTest < Test::Unit::TestCase
+ def setup
+ ActionController::Routing::Routes.draw do |map|
+ map.main '', :controller => 'posts'
+ map.resources :posts
+ map.connect ':controller/:action/:id'
+ end
+
+ @request = ActionController::TestRequest.new
+ @request.host = 'hostname.com'
+
+ @response = ActionController::TestResponse.new
+ @controller = PageCachingTestController.new
+
+ @params = {:controller => 'posts', :action => 'index', :only_path => true, :skip_relative_url_root => true}
+ @rewriter = ActionController::UrlRewriter.new(@request, @params)
+
+ FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+ FileUtils.mkdir_p(FILE_STORE_PATH)
+ end
+
+ def teardown
+ FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+ end
+
+ def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
+ @params[:format] = 'rss'
+ assert_equal '/posts.rss', @rewriter.rewrite(@params)
+ @params[:format] = nil
+ assert_equal '/', @rewriter.rewrite(@params)
+ end
+
+ def test_should_cache_get_with_ok_status
+ get :ok
+ assert_response :ok
+ assert_page_cached :ok, "get with ok status should have been cached"
+ end
+
+ [:ok, :no_content, :found, :not_found].each do |status|
+ [:get, :post, :put, :delete].each do |method|
+ unless method == :get and status == :ok
+ define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
+ @request.env['REQUEST_METHOD'] = method.to_s.upcase
+ process status
+ assert_response status
+ assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
+ end
+ end
+ end
+ end
+
+ private
+ def assert_page_cached(action, message = "#{action} should have been cached")
+ assert page_cached?(action), message
+ end
+
+ def assert_page_not_cached(action, message = "#{action} shouldn't have been cached")
+ assert !page_cached?(action), message
+ end
+
+ def page_cached?(action)
+ File.exist? "#{FILE_STORE_PATH}/page_caching_test/#{action}.html"
+ end
+end
+
+class ActionCachingTestController < ActionController::Base
+ caches_action :index
+
+ def index
+ @cache_this = Time.now.to_f.to_s
+ render :text => @cache_this
+ end
+
+ def expire
+ expire_action :controller => 'action_caching_test', :action => 'index'
+ render :nothing => true
+ end
+
+end
+
+class ActionCachingMockController
+ attr_accessor :mock_url_for
+ attr_accessor :mock_path
+
+ def initialize
+ yield self if block_given?
+ end
+
+ def url_for(*args)
+ @mock_url_for
+ end
+
+ def request
+ mocked_path = @mock_path
+ Object.new.instance_eval(<<-EVAL)
+ def path; '#{@mock_path}' end
+ self
+ EVAL
+ end
+end
+
+class ActionCacheTest < Test::Unit::TestCase
+ def setup
+ reset!
+ FileUtils.mkdir_p(FILE_STORE_PATH)
+ @path_class = ActionController::Caching::Actions::ActionCachePath
+ @mock_controller = ActionCachingMockController.new
+ end
+
+ def teardown
+ FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+ end
+
+ def test_simple_action_cache
+ get :index
+ cached_time = content_to_cache
+ assert_equal cached_time, @response.body
+ reset!
+
+ get :index
+ assert_equal cached_time, @response.body
+ end
+
+ def test_cache_expiration
+ get :index
+ cached_time = content_to_cache
+ reset!
+
+ get :index
+ assert_equal cached_time, @response.body
+ reset!
+
+ get :expire
+ reset!
+
+ get :index
+ new_cached_time = content_to_cache
+ assert_not_equal cached_time, @response.body
+ reset!
+
+ get :index
+ assert_response :success
+ assert_equal new_cached_time, @response.body
+ end
+
+ def test_cache_is_scoped_by_subdomain
+ @request.host = 'jamis.hostname.com'
+ get :index
+ jamis_cache = content_to_cache
+
+ @request.host = 'david.hostname.com'
+ get :index
+ david_cache = content_to_cache
+ assert_not_equal jamis_cache, @response.body
+
+ @request.host = 'jamis.hostname.com'
+ get :index
+ assert_equal jamis_cache, @response.body
+
+ @request.host = 'david.hostname.com'
+ get :index
+ assert_equal david_cache, @response.body
+ end
+
+ def test_xml_version_of_resource_is_treated_as_different_cache
+ @mock_controller.mock_url_for = 'http://example.org/posts/'
+ @mock_controller.mock_path = '/posts/index.xml'
+ path_object = @path_class.new(@mock_controller)
+ assert_equal 'xml', path_object.extension
+ assert_equal 'example.org/posts/index.xml', path_object.path
+ end
+
+ def test_empty_path_is_normalized
+ @mock_controller.mock_url_for = 'http://example.org/'
+ @mock_controller.mock_path = '/'
+
+ assert_equal 'example.org/index', @path_class.path_for(@mock_controller)
+ end
+
+ def test_file_extensions
+ get :index, :id => 'kitten.jpg'
+ get :index, :id => 'kitten.jpg'
+
+ assert_response :success
+ end
+
+ private
+ def content_to_cache
+ assigns(:cache_this)
+ end
+
+ def reset!
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = ActionCachingTestController.new
+ @request.host = 'hostname.com'
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/content_type_test.rb b/vendor/rails/actionpack/test/controller/content_type_test.rb
new file mode 100644
index 00000000..6f0618da
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/content_type_test.rb
@@ -0,0 +1,139 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class ContentTypeController < ActionController::Base
+ def render_content_type_from_body
+ response.content_type = Mime::RSS
+ render :text => "hello world!"
+ end
+
+ def render_defaults
+ render :text => "hello world!"
+ end
+
+ def render_content_type_from_render
+ render :text => "hello world!", :content_type => Mime::RSS
+ end
+
+ def render_charset_from_body
+ response.charset = "utf-16"
+ render :text => "hello world!"
+ end
+
+ def render_default_for_rhtml
+ end
+
+ def render_default_for_rxml
+ end
+
+ def render_default_for_rjs
+ end
+
+ def render_change_for_rxml
+ response.content_type = Mime::HTML
+ render :action => "render_default_for_rxml"
+ end
+
+ def render_default_content_types_for_respond_to
+ respond_to do |format|
+ format.html { render :text => "hello world!" }
+ format.xml { render :action => "render_default_content_types_for_respond_to.rhtml" }
+ format.js { render :text => "hello world!" }
+ format.rss { render :text => "hello world!", :content_type => Mime::XML }
+ end
+ end
+
+ def rescue_action(e) raise end
+end
+
+ContentTypeController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class ContentTypeTest < Test::Unit::TestCase
+ def setup
+ @controller = ContentTypeController.new
+
+ # 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 = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_render_defaults
+ get :render_defaults
+ assert_equal "utf-8", @response.charset
+ assert_equal Mime::HTML, @response.content_type
+ end
+
+ def test_render_changed_charset_default
+ ContentTypeController.default_charset = "utf-16"
+ get :render_defaults
+ assert_equal "utf-16", @response.charset
+ assert_equal Mime::HTML, @response.content_type
+ ContentTypeController.default_charset = "utf-8"
+ end
+
+ def test_content_type_from_body
+ get :render_content_type_from_body
+ assert_equal "application/rss+xml", @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_content_type_from_render
+ get :render_content_type_from_render
+ assert_equal "application/rss+xml", @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_charset_from_body
+ get :render_charset_from_body
+ assert_equal "utf-16", @response.charset
+ assert_equal Mime::HTML, @response.content_type
+ end
+
+ def test_default_for_rhtml
+ get :render_default_for_rhtml
+ assert_equal Mime::HTML, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_default_for_rxml
+ get :render_default_for_rxml
+ assert_equal Mime::XML, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_default_for_rjs
+ xhr :post, :render_default_for_rjs
+ assert_equal Mime::JS, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_change_for_rxml
+ get :render_change_for_rxml
+ assert_equal Mime::HTML, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_render_default_content_types_for_respond_to
+ @request.env["HTTP_ACCEPT"] = Mime::HTML.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::HTML, @response.content_type
+
+ @request.env["HTTP_ACCEPT"] = Mime::JS.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::JS, @response.content_type
+ end
+
+ def test_render_default_content_types_for_respond_to_with_template
+ @request.env["HTTP_ACCEPT"] = Mime::XML.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::XML, @response.content_type
+ end
+
+ def test_render_default_content_types_for_respond_to_with_overwrite
+ @request.env["HTTP_ACCEPT"] = Mime::RSS.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::XML, @response.content_type
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb b/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/user_controller.rb b/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/user_controller.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/rails/actionpack/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb b/vendor/rails/actionpack/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb b/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
new file mode 100644
index 00000000..7865a69e
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
@@ -0,0 +1,48 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DeprecatedControllerInstanceVariablesTest < Test::Unit::TestCase
+ class Target < ActionController::Base
+ def initialize(run = nil)
+ instance_eval(run) if run
+ super()
+ end
+
+ def noop
+ render :nothing => true
+ end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval "def old_#{var}; render :text => @#{var}.to_s end"
+ class_eval "def new_#{var}; render :text => #{var}.to_s end"
+ class_eval "def internal_#{var}; render :text => @_#{var}.to_s end"
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = Target.new
+ end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval <<-end_eval, __FILE__, __LINE__
+ def test_old_#{var}_is_deprecated
+ assert_deprecated('@#{var}') { get :old_#{var} }
+ end
+ def test_new_#{var}_isnt_deprecated
+ assert_not_deprecated { get :new_#{var} }
+ end
+ def test_internal_#{var}_isnt_deprecated
+ assert_not_deprecated { get :internal_#{var} }
+ end
+ def test_#{var}_raises_if_already_set
+ assert_raise(RuntimeError) do
+ @controller = Target.new '@#{var} = Object.new'
+ get :noop
+ end
+ end
+ end_eval
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb b/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
new file mode 100644
index 00000000..d8da676f
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
@@ -0,0 +1,60 @@
+require File.dirname(__FILE__) + '/../../abstract_unit'
+
+class DeprecatedBaseMethodsTest < Test::Unit::TestCase
+ class Target < ActionController::Base
+ def deprecated_symbol_parameter_to_url_for
+ redirect_to(url_for(:home_url, "superstars"))
+ end
+
+ def deprecated_render_parameters
+ render "fun/games/hello_world"
+ end
+
+ def home_url(greeting)
+ "http://example.com/#{greeting}"
+ end
+
+ def raises_name_error
+ this_method_doesnt_exist
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ Target.template_root = File.dirname(__FILE__) + "/../../fixtures"
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = Target.new
+ end
+
+ def test_deprecated_symbol_parameter_to_url_for
+ assert_deprecated("url_for(:home_url)") do
+ get :deprecated_symbol_parameter_to_url_for
+ end
+
+ assert_redirected_to "http://example.com/superstars"
+ end
+
+ def test_deprecated_render_parameters
+ assert_deprecated("render('fun/games/hello_world')") do
+ get :deprecated_render_parameters
+ end
+
+ assert_equal "Living in a nested world", @response.body
+ end
+
+ def test_log_error_silences_deprecation_warnings
+ get :raises_name_error
+ rescue => e
+ assert_not_deprecated { @controller.send :log_error, e }
+ end
+
+ def test_assertion_failed_error_silences_deprecation_warnings
+ get :raises_name_error
+ rescue => e
+ error = Test::Unit::Error.new('testing ur doodz', e)
+ assert_not_deprecated { error.message }
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/integration_test.rb b/vendor/rails/actionpack/test/controller/integration_test.rb
new file mode 100644
index 00000000..665c5901
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/integration_test.rb
@@ -0,0 +1,154 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+$:.unshift File.dirname(__FILE__) + '/../../../railties/lib'
+require 'action_controller/integration'
+
+begin # rescue LoadError
+require 'mocha'
+require 'stubba'
+
+# Stub process for testing.
+module ActionController
+ module Integration
+ class Session
+ def process
+ end
+
+ def generic_url_rewriter
+ end
+ end
+ end
+end
+
+class SessionTest < Test::Unit::TestCase
+ def setup
+ @session = ActionController::Integration::Session.new
+ end
+ def test_https_bang_works_and_sets_truth_by_default
+ assert !@session.https?
+ @session.https!
+ assert @session.https?
+ @session.https! false
+ assert !@session.https?
+ end
+
+ def test_host!
+ assert_not_equal "glu.ttono.us", @session.host
+ @session.host! "rubyonrails.com"
+ assert_equal "rubyonrails.com", @session.host
+ end
+
+ def test_follow_redirect_raises_when_no_redirect
+ @session.stubs(:redirect?).returns(false)
+ assert_raise(RuntimeError) { @session.follow_redirect! }
+ end
+
+ def test_follow_redirect_calls_get_and_returns_status
+ @session.stubs(:redirect?).returns(true)
+ @session.stubs(:headers).returns({"location" => ["www.google.com"]})
+ @session.stubs(:status).returns(200)
+ @session.expects(:get)
+ assert_equal 200, @session.follow_redirect!
+ end
+
+ def test_get_via_redirect
+ path = "/somepath"; args = {:id => '1'}
+
+ @session.expects(:get).with(path,args)
+
+ redirects = [true, true, false]
+ @session.stubs(:redirect?).returns(lambda { redirects.shift })
+ @session.expects(:follow_redirect!).times(2)
+
+ @session.stubs(:status).returns(200)
+ assert_equal 200, @session.get_via_redirect(path, args)
+ end
+
+ def test_post_via_redirect
+ path = "/somepath"; args = {:id => '1'}
+
+ @session.expects(:post).with(path,args)
+
+ redirects = [true, true, false]
+ @session.stubs(:redirect?).returns(lambda { redirects.shift })
+ @session.expects(:follow_redirect!).times(2)
+
+ @session.stubs(:status).returns(200)
+ assert_equal 200, @session.post_via_redirect(path, args)
+ end
+
+ def test_url_for_with_controller
+ options = {:action => 'show'}
+ mock_controller = mock()
+ mock_controller.expects(:url_for).with(options).returns('/show')
+ @session.stubs(:controller).returns(mock_controller)
+ assert_equal '/show', @session.url_for(options)
+ end
+
+ def test_url_for_without_controller
+ options = {:action => 'show'}
+ mock_rewriter = mock()
+ mock_rewriter.expects(:rewrite).with(options).returns('/show')
+ @session.stubs(:generic_url_rewriter).returns(mock_rewriter)
+ @session.stubs(:controller).returns(nil)
+ assert_equal '/show', @session.url_for(options)
+ end
+
+ def test_redirect_bool_with_status_in_300s
+ @session.stubs(:status).returns 301
+ assert @session.redirect?
+ end
+
+ def test_redirect_bool_with_status_in_200s
+ @session.stubs(:status).returns 200
+ assert !@session.redirect?
+ end
+
+ def test_get
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:get,path,params,headers)
+ @session.get(path,params,headers)
+ end
+
+ def test_post
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:post,path,params,headers)
+ @session.post(path,params,headers)
+ end
+
+ def test_put
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:put,path,params,headers)
+ @session.put(path,params,headers)
+ end
+
+ def test_delete
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:delete,path,params,headers)
+ @session.delete(path,params,headers)
+ end
+
+ def test_head
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:head,path,params,headers)
+ @session.head(path,params,headers)
+ end
+
+ def test_xml_http_request
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ headers_after_xhr = headers.merge(
+ "X-Requested-With" => "XMLHttpRequest",
+ "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ )
+ @session.expects(:post).with(path,params,headers_after_xhr)
+ @session.xml_http_request(path,params,headers)
+ end
+end
+
+# TODO
+# class MockCGITest < Test::Unit::TestCase
+# end
+
+rescue LoadError
+ $stderr.puts "Skipping integration tests. `gem install mocha` and try again."
+end
diff --git a/vendor/rails/actionpack/test/controller/resources_test.rb b/vendor/rails/actionpack/test/controller/resources_test.rb
new file mode 100644
index 00000000..e5605dff
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/resources_test.rb
@@ -0,0 +1,482 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+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
+class MessagesController < ResourcesController; end
+class CommentsController < ResourcesController; end
+
+class AccountController < ResourcesController; end
+class AdminController < ResourcesController; end
+
+class ResourcesTest < Test::Unit::TestCase
+ def test_should_arrange_actions
+ resource = ActionController::Resources::Resource.new(:messages,
+ :collection => { :rss => :get, :reorder => :post, :csv => :post },
+ :member => { :rss => :get, :atom => :get, :upload => :post, :fix => :post },
+ :new => { :preview => :get, :draft => :get })
+
+ assert_resource_methods [:rss], resource, :collection, :get
+ assert_resource_methods [:csv, :reorder], resource, :collection, :post
+ assert_resource_methods [:edit, :rss, :atom], resource, :member, :get
+ assert_resource_methods [:upload, :fix], resource, :member, :post
+ assert_resource_methods [:new, :preview, :draft], resource, :new, :get
+ end
+
+ def test_should_resource_controller_name_equal_resource_name_by_default
+ resource = ActionController::Resources::Resource.new(:messages, {})
+ assert_equal 'messages', resource.controller
+ end
+
+ def test_should_resource_controller_name_equal_controller_option
+ resource = ActionController::Resources::Resource.new(:messages, :controller => 'posts')
+ assert_equal 'posts', resource.controller
+ end
+
+ def test_should_all_singleton_paths_be_the_same
+ [ :path, :nesting_path_prefix, :member_path ].each do |method|
+ resource = ActionController::Resources::SingletonResource.new(:messages, :path_prefix => 'admin')
+ assert_equal 'admin/messages', resource.send(method)
+ end
+ end
+
+ def test_default_restful_routes
+ with_restful_routing :messages do
+ assert_simply_restful_for :messages
+ end
+ end
+
+ def test_multiple_default_restful_routes
+ with_restful_routing :messages, :comments do
+ assert_simply_restful_for :messages
+ assert_simply_restful_for :comments
+ end
+ end
+
+ def test_with_path_prefix
+ with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do
+ assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+ end
+ end
+
+ def test_multile_with_path_prefix
+ with_restful_routing :messages, :comments, :path_prefix => '/thread/:thread_id' do
+ assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+ assert_simply_restful_for :comments, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+ end
+ end
+
+ def test_with_name_prefix
+ with_restful_routing :messages, :name_prefix => 'post_' do
+ assert_simply_restful_for :messages, :name_prefix => 'post_'
+ end
+ end
+
+ def test_with_collection_action
+ rss_options = {:action => 'rss'}
+ rss_path = "/messages;rss"
+ actions = { 'a' => :put, 'b' => :post, 'c' => :delete }
+
+ with_restful_routing :messages, :collection => { :rss => :get }.merge(actions) do
+ assert_restful_routes_for :messages do |options|
+ assert_routing rss_path, options.merge(rss_options)
+
+ actions.each do |action, method|
+ assert_recognizes(options.merge(:action => action), :path => "/messages;#{action}", :method => method)
+ end
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route rss_path, :rss_messages_path, rss_options
+ actions.keys.each do |action|
+ assert_named_route "/messages;#{action}", "#{action}_messages_path", :action => action
+ end
+ end
+ end
+ end
+
+ def test_with_member_action
+ [:put, :post].each do |method|
+ with_restful_routing :messages, :member => { :mark => method } do
+ mark_options = {:action => 'mark', :id => '1'}
+ mark_path = "/messages/1;mark"
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method)
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route mark_path, :mark_message_path, mark_options
+ end
+ end
+ end
+ end
+
+ def test_with_two_member_actions_with_same_method
+ [:put, :post].each do |method|
+ with_restful_routing :messages, :member => { :mark => method, :unmark => method } do
+ %w(mark unmark).each do |action|
+ action_options = {:action => action, :id => '1'}
+ action_path = "/messages/1;#{action}"
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route action_path, "#{action}_message_path".to_sym, action_options
+ end
+ end
+ end
+ end
+ end
+
+ def test_with_new_action
+ with_restful_routing :messages, :new => { :preview => :post } do
+ preview_options = {:action => 'preview'}
+ preview_path = "/messages/new;preview"
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route preview_path, :preview_new_message_path, preview_options
+ end
+ end
+ end
+
+ def test_override_new_method
+ with_restful_routing :messages do
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
+ assert_raises(ActionController::RoutingError) do
+ ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post)
+ end
+ end
+ end
+
+ with_restful_routing :messages, :new => { :new => :any } do
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :post)
+ assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
+ end
+ end
+ end
+
+ def test_nested_restful_routes
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :threads do |map|
+ map.resources :messages do |map|
+ map.resources :comments
+ end
+ end
+ end
+
+ assert_simply_restful_for :threads
+ assert_simply_restful_for :messages,
+ :path_prefix => 'threads/1/',
+ :options => { :thread_id => '1' }
+ assert_simply_restful_for :comments,
+ :path_prefix => 'threads/1/messages/2/',
+ :options => { :thread_id => '1', :message_id => '2' }
+ end
+ end
+
+ def test_restful_routes_dont_generate_duplicates
+ with_restful_routing :messages do
+ routes = ActionController::Routing::Routes.routes
+ routes.each do |route|
+ routes.each do |r|
+ next if route === r # skip the comparison instance
+ assert distinct_routes?(route, r), "Duplicate Route: #{route}"
+ end
+ end
+ end
+ end
+
+ def test_should_create_singleton_resource_routes
+ with_singleton_resources :account do
+ assert_singleton_restful_for :account
+ end
+ end
+
+ def test_should_create_multiple_singleton_resource_routes
+ with_singleton_resources :account, :admin do
+ assert_singleton_restful_for :account
+ assert_singleton_restful_for :admin
+ end
+ end
+
+ def test_should_create_nested_singleton_resource_routes
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :admin do |admin|
+ admin.resource :account
+ end
+ end
+
+ assert_singleton_restful_for :admin
+ assert_singleton_restful_for :account, :path_prefix => 'admin/'
+ end
+ end
+
+ def test_singleton_resource_with_member_action
+ [:put, :post].each do |method|
+ with_singleton_resources :account, :member => { :reset => method } do
+ reset_options = {:action => 'reset'}
+ reset_path = "/account;reset"
+ assert_singleton_routes_for :account do |options|
+ assert_recognizes(options.merge(reset_options), :path => reset_path, :method => method)
+ end
+
+ assert_singleton_named_routes_for :account do |options|
+ assert_named_route reset_path, :reset_account_path, reset_options
+ end
+ end
+ end
+ end
+
+ def test_singleton_resource_with_two_member_actions_with_same_method
+ [:put, :post].each do |method|
+ with_singleton_resources :account, :member => { :reset => method, :disable => method } do
+ %w(reset disable).each do |action|
+ action_options = {:action => action}
+ action_path = "/account;#{action}"
+ assert_singleton_routes_for :account do |options|
+ assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
+ end
+
+ assert_singleton_named_routes_for :account do |options|
+ assert_named_route action_path, "#{action}_account_path".to_sym, action_options
+ end
+ end
+ end
+ end
+ end
+
+ def test_should_nest_resources_in_singleton_resource
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account do |account|
+ account.resources :messages
+ end
+ end
+
+ assert_singleton_restful_for :account
+ assert_simply_restful_for :messages, :path_prefix => 'account/'
+ end
+ end
+
+ def test_should_nest_resources_in_singleton_resource_with_path_prefix
+ with_routing do |set|
+ set.draw do |map|
+ map.resource(:account, :path_prefix => ':site_id') do |account|
+ account.resources :messages
+ end
+ end
+
+ assert_singleton_restful_for :account, :path_prefix => '7/', :options => { :site_id => '7' }
+ assert_simply_restful_for :messages, :path_prefix => '7/account/', :options => { :site_id => '7' }
+ end
+ end
+
+ def test_should_nest_singleton_resource_in_resources
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :threads do |thread|
+ thread.resource :admin
+ end
+ end
+
+ assert_simply_restful_for :threads
+ assert_singleton_restful_for :admin, :path_prefix => 'threads/5/', :options => { :thread_id => '5' }
+ end
+ end
+
+ def test_should_not_allow_delete_or_put_on_collection_path
+ controller_name = :messages
+ with_restful_routing controller_name do
+ options = { :controller => controller_name.to_s }
+ collection_path = "/#{controller_name}"
+
+ assert_raises(ActionController::RoutingError) do
+ assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put)
+ end
+
+ assert_raises(ActionController::RoutingError) do
+ assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete)
+ end
+ end
+ end
+
+ protected
+ def with_restful_routing(*args)
+ with_routing do |set|
+ set.draw { |map| map.resources(*args) }
+ yield
+ end
+ end
+
+ def with_singleton_resources(*args)
+ with_routing do |set|
+ set.draw { |map| map.resource(*args) }
+ yield
+ end
+ end
+
+ # runs assert_restful_routes_for and assert_restful_named_routes for on the controller_name and options, without passing a block.
+ def assert_simply_restful_for(controller_name, options = {})
+ assert_restful_routes_for controller_name, options
+ assert_restful_named_routes_for controller_name, options
+ end
+
+ def assert_singleton_restful_for(singleton_name, options = {})
+ assert_singleton_routes_for singleton_name, options
+ assert_singleton_named_routes_for singleton_name, options
+ end
+
+ def assert_restful_routes_for(controller_name, options = {})
+ (options[:options] ||= {})[:controller] = controller_name.to_s
+
+ collection_path = "/#{options[:path_prefix]}#{controller_name}"
+ member_path = "#{collection_path}/1"
+ new_path = "#{collection_path}/new"
+ edit_member_path = "#{member_path};edit"
+ formatted_edit_member_path = "#{member_path}.xml;edit"
+
+ with_options(options[:options]) do |controller|
+ controller.assert_routing collection_path, :action => 'index'
+ controller.assert_routing new_path, :action => 'new'
+ controller.assert_routing member_path, :action => 'show', :id => '1'
+ controller.assert_routing edit_member_path, :action => 'edit', :id => '1'
+ controller.assert_routing "#{collection_path}.xml", :action => 'index', :format => 'xml'
+ controller.assert_routing "#{new_path}.xml", :action => 'new', :format => 'xml'
+ controller.assert_routing "#{member_path}.xml", :action => 'show', :id => '1', :format => 'xml'
+ controller.assert_routing formatted_edit_member_path, :action => 'edit', :id => '1', :format => 'xml'
+ end
+
+ assert_recognizes(options[:options].merge(:action => 'index'), :path => collection_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'create'), :path => collection_path, :method => :post)
+ assert_recognizes(options[:options].merge(:action => 'show', :id => '1'), :path => member_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'edit', :id => '1'), :path => edit_member_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'update', :id => '1'), :path => member_path, :method => :put)
+ assert_recognizes(options[:options].merge(:action => 'destroy', :id => '1'), :path => member_path, :method => :delete)
+
+ assert_recognizes(options[:options].merge(:action => 'index', :format => 'xml'), :path => "#{collection_path}.xml", :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{collection_path}.xml", :method => :post)
+ assert_recognizes(options[:options].merge(:action => 'show', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'edit', :id => '1', :format => 'xml'), :path => formatted_edit_member_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'update', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :put)
+ assert_recognizes(options[:options].merge(:action => 'destroy', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :delete)
+
+ yield options[:options] if block_given?
+ end
+
+ # test named routes like foo_path and foos_path map to the correct options.
+ def assert_restful_named_routes_for(controller_name, singular_name = nil, options = {})
+ if singular_name.is_a?(Hash)
+ options = singular_name
+ singular_name = nil
+ end
+ singular_name ||= controller_name.to_s.singularize
+ (options[:options] ||= {})[:controller] = controller_name.to_s
+ @controller = "#{controller_name.to_s.camelize}Controller".constantize.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ get :index, options[:options]
+ options[:options].delete :action
+
+ full_prefix = "/#{options[:path_prefix]}#{controller_name}"
+ name_prefix = options[:name_prefix]
+
+ assert_named_route "#{full_prefix}", "#{name_prefix}#{controller_name}_path", options[:options]
+ assert_named_route "#{full_prefix}/new", "#{name_prefix}new_#{singular_name}_path", options[:options]
+ assert_named_route "#{full_prefix}/1", "#{name_prefix}#{singular_name}_path", options[:options].merge(:id => '1')
+ assert_named_route "#{full_prefix}/1;edit", "#{name_prefix}edit_#{singular_name}_path", options[:options].merge(:id => '1')
+ assert_named_route "#{full_prefix}.xml", "formatted_#{name_prefix}#{controller_name}_path", options[:options].merge( :format => 'xml')
+ assert_named_route "#{full_prefix}/new.xml", "formatted_#{name_prefix}new_#{singular_name}_path", options[:options].merge( :format => 'xml')
+ assert_named_route "#{full_prefix}/1.xml", "formatted_#{name_prefix}#{singular_name}_path", options[:options].merge(:id => '1', :format => 'xml')
+ assert_named_route "#{full_prefix}/1.xml;edit", "formatted_#{name_prefix}edit_#{singular_name}_path", options[:options].merge(:id => '1', :format => 'xml')
+ yield options[:options] if block_given?
+ end
+
+ def assert_singleton_routes_for(singleton_name, options = {})
+ (options[:options] ||= {})[:controller] ||= singleton_name.to_s
+
+ full_path = "/#{options[:path_prefix]}#{singleton_name}"
+ new_path = "#{full_path}/new"
+ edit_path = "#{full_path};edit"
+ formatted_edit_path = "#{full_path}.xml;edit"
+
+ with_options options[:options] do |controller|
+ controller.assert_routing full_path, :action => 'show'
+ controller.assert_routing new_path, :action => 'new'
+ controller.assert_routing edit_path, :action => 'edit'
+ controller.assert_routing "#{full_path}.xml", :action => 'show', :format => 'xml'
+ controller.assert_routing "#{new_path}.xml", :action => 'new', :format => 'xml'
+ controller.assert_routing formatted_edit_path, :action => 'edit', :format => 'xml'
+ end
+
+ assert_recognizes(options[:options].merge(:action => 'show'), :path => full_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'edit'), :path => edit_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'create'), :path => full_path, :method => :post)
+ assert_recognizes(options[:options].merge(:action => 'update'), :path => full_path, :method => :put)
+ assert_recognizes(options[:options].merge(:action => 'destroy'), :path => full_path, :method => :delete)
+
+ assert_recognizes(options[:options].merge(:action => 'show', :format => 'xml'), :path => "#{full_path}.xml", :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'edit', :format => 'xml'), :path => formatted_edit_path, :method => :get)
+ assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{full_path}.xml", :method => :post)
+ assert_recognizes(options[:options].merge(:action => 'update', :format => 'xml'), :path => "#{full_path}.xml", :method => :put)
+ assert_recognizes(options[:options].merge(:action => 'destroy', :format => 'xml'), :path => "#{full_path}.xml", :method => :delete)
+
+ yield options[:options] if block_given?
+ end
+
+ def assert_singleton_named_routes_for(singleton_name, options = {})
+ (options[:options] ||= {})[:controller] ||= singleton_name.to_s
+ @controller = "#{options[:options][:controller].camelize}Controller".constantize.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ get :show, options[:options]
+ options[:options].delete :action
+
+ full_path = "/#{options[:path_prefix]}#{singleton_name}"
+
+ assert_named_route "#{full_path}", "#{singleton_name}_path", options[:options]
+ assert_named_route "#{full_path}/new", "new_#{singleton_name}_path", options[:options]
+ assert_named_route "#{full_path};edit", "edit_#{singleton_name}_path", options[:options]
+ assert_named_route "#{full_path}.xml", "formatted_#{singleton_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_path}/new.xml", "formatted_new_#{singleton_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_path}.xml;edit", "formatted_edit_#{singleton_name}_path", options[:options].merge(:format => 'xml')
+ end
+
+ def assert_named_route(expected, route, options)
+ actual = @controller.send(route, options) rescue $!.class.name
+ assert_equal expected, actual, "Error on route: #{route}(#{options.inspect})"
+ end
+
+ def assert_resource_methods(expected, resource, action_method, method)
+ assert_equal expected.length, resource.send("#{action_method}_methods")[method].size, "#{resource.send("#{action_method}_methods")[method].inspect}"
+ expected.each do |action|
+ assert resource.send("#{action_method}_methods")[method].include?(action),
+ "#{method} not in #{action_method} methods: #{resource.send("#{action_method}_methods")[method].inspect}"
+ end
+ end
+
+ def distinct_routes? (r1, r2)
+ if r1.conditions == r2.conditions and r1.requirements == r2.requirements then
+ if r1.segments.collect(&:to_s) == r2.segments.collect(&:to_s) then
+ return false
+ end
+ end
+ true
+ end
+
+end
diff --git a/vendor/rails/actionpack/test/controller/selector_test.rb b/vendor/rails/actionpack/test/controller/selector_test.rb
new file mode 100644
index 00000000..d0042900
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/selector_test.rb
@@ -0,0 +1,628 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require File.dirname(__FILE__) + '/../abstract_unit'
+require File.dirname(__FILE__) + '/fake_controllers'
+
+class SelectorTest < Test::Unit::TestCase
+ #
+ # Basic selector: element, id, class, attributes.
+ #
+
+ def test_element
+ parse(%Q{})
+ # Match element by name.
+ select("div")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Not case sensitive.
+ select("DIV")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Universal match (all elements).
+ select("*")
+ assert_equal 3, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal nil, @matches[1].attributes["id"]
+ assert_equal "2", @matches[2].attributes["id"]
+ end
+
+
+ def test_identifier
+ parse(%Q{})
+ # Match element by ID.
+ select("div#1")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match element by ID, substitute value.
+ select("div#?", 2)
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Element name does not match ID.
+ select("p#?", 2)
+ assert_equal 0, @matches.size
+ # Use regular expression.
+ select("#?", /\d/)
+ assert_equal 2, @matches.size
+ end
+
+
+ def test_class_name
+ parse(%Q{})
+ # Match element with specified class.
+ select("div.foo")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match any element with specified class.
+ select("*.foo")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Match elements with other class.
+ select("*.bar")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Match only element with both class names.
+ select("*.bar.foo")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ end
+
+
+ def test_attribute
+ parse(%Q{})
+ # Match element with attribute.
+ select("div[title]")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Match any element with attribute.
+ select("*[title]")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Match alement with attribute value.
+ select("*[title=foo]")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Match alement with attribute and attribute value.
+ select("[bar=foo][title]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Not case sensitive.
+ select("[BAR=foo][TiTle]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ end
+
+
+ def test_attribute_quoted
+ parse(%Q{})
+ # Match without quotes.
+ select("[title = bar]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match with single quotes.
+ select("[title = 'bar' ]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match with double quotes.
+ select("[title = \"bar\" ]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match with spaces.
+ select("[title = \" bar \" ]")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ end
+
+
+ def test_attribute_equality
+ parse(%Q{})
+ # Match (fail) complete value.
+ select("[title=bar]")
+ assert_equal 0, @matches.size
+ # Match space-separate word.
+ select("[title~=foo]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("[title~=bar]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match beginning of value.
+ select("[title^=ba]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match end of value.
+ select("[title$=ar]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match text in value.
+ select("[title*=bar]")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Match first space separated word.
+ select("[title|=foo]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("[title|=bar]")
+ assert_equal 0, @matches.size
+ end
+
+
+ #
+ # Selector composition: groups, sibling, children
+ #
+
+
+ def test_selector_group
+ parse(%Q{})
+ # Simple group selector.
+ select("h1,h3")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("h1 , h3")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Complex group selector.
+ parse(%Q{
})
+ select("h1 a, h3 a")
+ assert_equal 2, @matches.size
+ assert_equal "foo", @matches[0].attributes["href"]
+ assert_equal "baz", @matches[1].attributes["href"]
+ # And now for the three selector challange.
+ parse(%Q{
})
+ select("h1 a, h2 a, h3 a")
+ assert_equal 3, @matches.size
+ assert_equal "foo", @matches[0].attributes["href"]
+ assert_equal "bar", @matches[1].attributes["href"]
+ assert_equal "baz", @matches[2].attributes["href"]
+ end
+
+
+ def test_sibling_selector
+ parse(%Q{})
+ # Test next sibling.
+ select("h1+*")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select("h1+h2")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select("h1+h3")
+ assert_equal 0, @matches.size
+ select("*+h3")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Test any sibling.
+ select("h1~*")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("h2~*")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ end
+
+
+ def test_children_selector
+ parse(%Q{})
+ # Test child selector.
+ select("div>p")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("div>span")
+ assert_equal 0, @matches.size
+ select("div>p#3")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ select("div>p>span")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ # Test descendant selector.
+ select("div p")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("div span")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ select("div *#3")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ select("div *#4")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ # This is here because it failed before when whitespaces
+ # were not properly stripped.
+ select("div .foo")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ end
+
+
+ #
+ # Pseudo selectors: root, nth-child, empty, content, etc
+ #
+
+
+ def test_root_selector
+ parse(%Q{})
+ # Can only find element if it's root.
+ select(":root")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("#1:root")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("#2:root")
+ assert_equal 0, @matches.size
+ # Opposite for nth-child.
+ select("#1:nth-child(1)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_odd_even
+ parse(%Q{})
+ # Test odd nth children.
+ select("tr:nth-child(odd)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Test even nth children.
+ select("tr:nth-child(even)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ end
+
+
+ def test_nth_child_a_is_zero
+ parse(%Q{})
+ # Test the third child.
+ select("tr:nth-child(0n+3)")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Same but an can be omitted when zero.
+ select("tr:nth-child(3)")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Second element (but not every second element).
+ select("tr:nth-child(0n+2)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Before first and past last returns nothing.:
+ assert_raises(ArgumentError) { select("tr:nth-child(-1)") }
+ select("tr:nth-child(0)")
+ assert_equal 0, @matches.size
+ select("tr:nth-child(5)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_a_is_one
+ parse(%Q{})
+ # a is group of one, pick every element in group.
+ select("tr:nth-child(1n+0)")
+ assert_equal 4, @matches.size
+ # Same but a can be omitted when one.
+ select("tr:nth-child(n+0)")
+ assert_equal 4, @matches.size
+ # Same but b can be omitted when zero.
+ select("tr:nth-child(n)")
+ assert_equal 4, @matches.size
+ end
+
+
+ def test_nth_child_b_is_zero
+ parse(%Q{})
+ # If b is zero, pick the n-th element (here each one).
+ select("tr:nth-child(n+0)")
+ assert_equal 4, @matches.size
+ # If b is zero, pick the n-th element (here every second).
+ select("tr:nth-child(2n+0)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # If a and b are both zero, no element selected.
+ select("tr:nth-child(0n+0)")
+ assert_equal 0, @matches.size
+ select("tr:nth-child(0)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_a_is_negative
+ parse(%Q{})
+ # Since a is -1, picks the first three elements.
+ select("tr:nth-child(-n+3)")
+ assert_equal 3, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ assert_equal "3", @matches[2].attributes["id"]
+ # Since a is -2, picks the first in every second of first four elements.
+ select("tr:nth-child(-2n+3)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Since a is -2, picks the first in every second of first three elements.
+ select("tr:nth-child(-2n+2)")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ end
+
+
+ def test_nth_child_b_is_negative
+ parse(%Q{})
+ # Select last of four.
+ select("tr:nth-child(4n-1)")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ # Select first of four.
+ select("tr:nth-child(4n-4)")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Select last of every second.
+ select("tr:nth-child(2n-1)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ # Select nothing since an+b always < 0
+ select("tr:nth-child(-1n-1)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_substitution_values
+ parse(%Q{})
+ # Test with ?n?.
+ select("tr:nth-child(?n?)", 2, 1)
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("tr:nth-child(?n?)", 2, 2)
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ select("tr:nth-child(?n?)", 4, 2)
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Test with ? (b only).
+ select("tr:nth-child(?)", 3)
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ select("tr:nth-child(?)", 5)
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_last_child
+ parse(%Q{})
+ # Last two elements.
+ select("tr:nth-last-child(-n+2)")
+ assert_equal 2, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ # All old elements counting from last one.
+ select("tr:nth-last-child(odd)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ end
+
+
+ def test_nth_of_type
+ parse(%Q{})
+ # First two elements.
+ select("tr:nth-of-type(-n+2)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # All old elements counting from last one.
+ select("tr:nth-last-of-type(odd)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ end
+
+
+ def test_first_and_last
+ parse(%Q{})
+ # First child.
+ select("tr:first-child")
+ assert_equal 0, @matches.size
+ select(":first-child")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ # First of type.
+ select("tr:first-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("thead:first-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ select("div:first-of-type")
+ assert_equal 0, @matches.size
+ # Last child.
+ select("tr:last-child")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ # Last of type.
+ select("tr:last-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ select("thead:last-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ select("div:last-of-type")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_first_and_last
+ # Only child.
+ parse(%Q{})
+ select("table:only-child")
+ assert_equal 0, @matches.size
+ select("tr:only-child")
+ assert_equal 1, @matches.size
+ assert_equal "tr", @matches[0].name
+ parse(%Q{})
+ select("tr:only-child")
+ assert_equal 0, @matches.size
+ # Only of type.
+ parse(%Q{})
+ select("thead:only-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ select("td:only-of-type")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_empty
+ parse(%Q{})
+ select("table:empty")
+ assert_equal 0, @matches.size
+ select("tr:empty")
+ assert_equal 1, @matches.size
+ parse(%Q{
})
+ select("div:empty")
+ assert_equal 1, @matches.size
+ end
+
+
+ def test_content
+ parse(%Q{
})
+ select("div:content()")
+ assert_equal 1, @matches.size
+ parse(%Q{something
})
+ select("div:content()")
+ assert_equal 0, @matches.size
+ select("div:content(something)")
+ assert_equal 1, @matches.size
+ select("div:content( 'something' )")
+ assert_equal 1, @matches.size
+ select("div:content( \"something\" )")
+ assert_equal 1, @matches.size
+ select("div:content(?)", "something")
+ assert_equal 1, @matches.size
+ select("div:content(?)", /something/)
+ assert_equal 1, @matches.size
+ end
+
+
+ #
+ # Test negation.
+ #
+
+
+ def test_element_negation
+ parse(%Q{})
+ select("*")
+ assert_equal 2, @matches.size
+ select("*:not(p)")
+ assert_equal 1, @matches.size
+ assert_equal "div", @matches[0].name
+ select("*:not(div)")
+ assert_equal 1, @matches.size
+ assert_equal "p", @matches[0].name
+ select("*:not(span)")
+ assert_equal 2, @matches.size
+ end
+
+
+ def test_id_negation
+ parse(%Q{})
+ select("p")
+ assert_equal 2, @matches.size
+ select(":not(#1)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select(":not(#2)")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ end
+
+
+ def test_class_name_negation
+ parse(%Q{})
+ select("p")
+ assert_equal 2, @matches.size
+ select(":not(.foo)")
+ assert_equal 1, @matches.size
+ assert_equal "bar", @matches[0].attributes["class"]
+ select(":not(.bar)")
+ assert_equal 1, @matches.size
+ assert_equal "foo", @matches[0].attributes["class"]
+ end
+
+
+ def test_attribute_negation
+ parse(%Q{})
+ select("p")
+ assert_equal 2, @matches.size
+ select(":not([title=foo])")
+ assert_equal 1, @matches.size
+ assert_equal "bar", @matches[0].attributes["title"]
+ select(":not([title=bar])")
+ assert_equal 1, @matches.size
+ assert_equal "foo", @matches[0].attributes["title"]
+ end
+
+
+ def test_pseudo_class_negation
+ parse(%Q{})
+ select("p")
+ assert_equal 2, @matches.size
+ select("p:not(:first-child)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select("p:not(:nth-child(2))")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ end
+
+
+ def test_negation_details
+ parse(%Q{})
+ assert_raises(ArgumentError) { select(":not(") }
+ assert_raises(ArgumentError) { select(":not(:not())") }
+ select("p:not(#1):not(#3)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ end
+
+
+ def test_select_from_element
+ parse(%Q{})
+ select("div")
+ @matches = @matches[0].select("p")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ end
+
+
+protected
+
+ def parse(html)
+ @html = HTML::Document.new(html).root
+ end
+
+ def select(*selector)
+ @matches = HTML.selector(*selector).select(@html)
+ end
+
+end
diff --git a/vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml b/vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
new file mode 100644
index 00000000..25dc7468
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
@@ -0,0 +1 @@
+world
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml b/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
new file mode 100644
index 00000000..c7926d48
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
@@ -0,0 +1 @@
+<%= 'hello world!' %>
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs b/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
new file mode 100644
index 00000000..8d614d04
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
@@ -0,0 +1 @@
+page.alert 'hello world!'
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml b/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
new file mode 100644
index 00000000..598d62e2
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
new file mode 100644
index 00000000..4e8a2d80
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
@@ -0,0 +1 @@
+<%= @cookies[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
new file mode 100644
index 00000000..68e88bb7
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
@@ -0,0 +1 @@
+<%= cookies[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
new file mode 100644
index 00000000..4b4782b2
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
@@ -0,0 +1 @@
+<%= @flash[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
new file mode 100644
index 00000000..f7f9d091
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
@@ -0,0 +1 @@
+<%= flash[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
new file mode 100644
index 00000000..1176c93a
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
@@ -0,0 +1 @@
+<%= @headers[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
new file mode 100644
index 00000000..308c4eb6
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
@@ -0,0 +1 @@
+<%= headers[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
new file mode 100644
index 00000000..1eea6875
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
@@ -0,0 +1 @@
+<%= @params[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
new file mode 100644
index 00000000..7e349b4c
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
@@ -0,0 +1 @@
+<%= params[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
new file mode 100644
index 00000000..a1680c23
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
@@ -0,0 +1 @@
+<%= @request.method %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
new file mode 100644
index 00000000..0c74cf1c
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
@@ -0,0 +1 @@
+<%= request.method %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
new file mode 100644
index 00000000..2f12d2ce
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
@@ -0,0 +1 @@
+<%= @response.body %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
new file mode 100644
index 00000000..948c7592
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
@@ -0,0 +1 @@
+<%= response.body %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
new file mode 100644
index 00000000..3acc1b85
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
@@ -0,0 +1 @@
+<%= @session[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
new file mode 100644
index 00000000..a899387c
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
@@ -0,0 +1 @@
+<%= session[:test] %>
diff --git a/vendor/rails/actionpack/test/fixtures/public/javascripts/application.js b/vendor/rails/actionpack/test/fixtures/public/javascripts/application.js
new file mode 100644
index 00000000..c6b73743
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/public/javascripts/application.js
@@ -0,0 +1 @@
+# Test file for javascript_include_tag
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/fixtures/test/_hello.rxml b/vendor/rails/actionpack/test/fixtures/test/_hello.rxml
new file mode 100644
index 00000000..ef52f632
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/test/_hello.rxml
@@ -0,0 +1 @@
+xm.hello
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml b/vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
new file mode 100644
index 00000000..e48d75c4
--- /dev/null
+++ b/vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
@@ -0,0 +1,3 @@
+xml.test do
+ render :partial => 'hello', :locals => { :xm => xml }
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/template/deprecated_helper_test.rb b/vendor/rails/actionpack/test/template/deprecated_helper_test.rb
new file mode 100644
index 00000000..836bb035
--- /dev/null
+++ b/vendor/rails/actionpack/test/template/deprecated_helper_test.rb
@@ -0,0 +1,36 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DeprecatedHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::CaptureHelper
+
+ def test_update_element_function
+ assert_deprecated 'update_element_function' do
+
+ assert_equal %($('myelement').innerHTML = 'blub';\n),
+ update_element_function('myelement', :content => 'blub')
+ assert_equal %($('myelement').innerHTML = 'blub';\n),
+ update_element_function('myelement', :action => :update, :content => 'blub')
+ assert_equal %($('myelement').innerHTML = '';\n),
+ update_element_function('myelement', :action => :empty)
+ assert_equal %(Element.remove('myelement');\n),
+ update_element_function('myelement', :action => :remove)
+
+ assert_equal %(new Insertion.Bottom('myelement','blub');\n),
+ update_element_function('myelement', :position => 'bottom', :content => 'blub')
+ assert_equal %(new Insertion.Bottom('myelement','blub');\n),
+ update_element_function('myelement', :action => :update, :position => :bottom, :content => 'blub')
+
+ _erbout = ""
+ assert_equal %($('myelement').innerHTML = 'test';\n),
+ update_element_function('myelement') { _erbout << "test" }
+
+ _erbout = ""
+ assert_equal %($('myelement').innerHTML = 'blockstuff';\n),
+ update_element_function('myelement', :content => 'paramstuff') { _erbout << "blockstuff" }
+
+ end
+ end
+
+end
+
diff --git a/vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb b/vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
new file mode 100644
index 00000000..c6931ae9
--- /dev/null
+++ b/vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
@@ -0,0 +1,43 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DeprecatedViewInstanceVariablesTest < Test::Unit::TestCase
+ class DeprecatedInstanceVariablesController < ActionController::Base
+ self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
+
+ def self.controller_path; 'deprecated_instance_variables' end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval <<-end_eval
+ def old_#{var}_inline; render :inline => '<%= @#{var}.to_s %>' end
+ def new_#{var}_inline; render :inline => '<%= #{var}.to_s %>' end
+ def old_#{var}_partial; render :partial => '#{var}_ivar' end
+ def new_#{var}_partial; render :partial => '#{var}_method' end
+ end_eval
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = DeprecatedInstanceVariablesController.new
+ end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval <<-end_eval, __FILE__, __LINE__
+ def test_old_#{var}_is_deprecated
+ assert_deprecated('@#{var}') { get :old_#{var}_inline }
+ end
+ def test_new_#{var}_isnt_deprecated
+ assert_not_deprecated { get :new_#{var}_inline }
+ end
+ def test_old_#{var}_partial_is_deprecated
+ assert_deprecated('@#{var}') { get :old_#{var}_partial }
+ end
+ def test_new_#{var}_partial_isnt_deprecated
+ assert_not_deprecated { get :new_#{var}_partial }
+ end
+ end_eval
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/attribute_methods.rb b/vendor/rails/activerecord/lib/active_record/attribute_methods.rb
new file mode 100644
index 00000000..adc6eb65
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/attribute_methods.rb
@@ -0,0 +1,75 @@
+module ActiveRecord
+ module AttributeMethods #:nodoc:
+ DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
+
+ def self.included(base)
+ base.extend ClassMethods
+ base.attribute_method_suffix *DEFAULT_SUFFIXES
+ end
+
+ # Declare and check for suffixed attribute methods.
+ module ClassMethods
+ # Declare a method available for all attributes with the given suffix.
+ # Uses method_missing and respond_to? to rewrite the method
+ # #{attr}#{suffix}(*args, &block)
+ # to
+ # attribute#{suffix}(#{attr}, *args, &block)
+ #
+ # An attribute#{suffix} instance method must exist and accept at least
+ # the attr argument.
+ #
+ # For example:
+ # class Person < ActiveRecord::Base
+ # attribute_method_suffix '_changed?'
+ #
+ # private
+ # def attribute_changed?(attr)
+ # ...
+ # end
+ # end
+ #
+ # person = Person.find(1)
+ # person.name_changed? # => false
+ # person.name = 'Hubert'
+ # person.name_changed? # => true
+ def attribute_method_suffix(*suffixes)
+ attribute_method_suffixes.concat suffixes
+ rebuild_attribute_method_regexp
+ end
+
+ # Returns MatchData if method_name is an attribute method.
+ def match_attribute_method?(method_name)
+ rebuild_attribute_method_regexp unless defined?(@@attribute_method_regexp) && @@attribute_method_regexp
+ @@attribute_method_regexp.match(method_name)
+ end
+
+ private
+ # Suffixes a, ?, c become regexp /(a|\?|c)$/
+ def rebuild_attribute_method_regexp
+ suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
+ @@attribute_method_regexp = /(#{suffixes.join('|')})$/.freeze
+ end
+
+ # Default to =, ?, _before_type_cast
+ def attribute_method_suffixes
+ @@attribute_method_suffixes ||= []
+ end
+ end
+
+ private
+ # Handle *? for method_missing.
+ def attribute?(attribute_name)
+ query_attribute(attribute_name)
+ end
+
+ # Handle *= for method_missing.
+ def attribute=(attribute_name, value)
+ write_attribute(attribute_name, value)
+ end
+
+ # Handle *_before_type_cast for method_missing.
+ def attribute_before_type_cast(attribute_name)
+ read_attribute_before_type_cast(attribute_name)
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
new file mode 100644
index 00000000..e6dbbd7e
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
@@ -0,0 +1,861 @@
+# Requires FrontBase Ruby bindings (gem install ruby-frontbase)
+
+require 'active_record/connection_adapters/abstract_adapter'
+
+FB_TRACE = false
+
+module ActiveRecord
+
+ class Base
+ class << self
+ # Establishes a connection to the database that's used by all Active Record objects.
+ def frontbase_connection(config) # :nodoc:
+ # FrontBase only supports one unnamed sequence per table
+ define_attr_method(:set_sequence_name, :sequence_name, &Proc.new {|*args| nil})
+
+ config = config.symbolize_keys
+ database = config[:database]
+ port = config[:port]
+ host = config[:host]
+ username = config[:username]
+ password = config[:password]
+ dbpassword = config[:dbpassword]
+ session_name = config[:session_name]
+
+ dbpassword = '' if dbpassword.nil?
+
+ # Turn off colorization since it makes tail/less output difficult
+ self.colorize_logging = false
+
+ require_library_or_gem 'frontbase' unless self.class.const_defined? :FBSQL_Connect
+
+ # Check bindings version
+ version = "0.0.0"
+ version = FBSQL_Connect::FB_BINDINGS_VERSION if defined? FBSQL_Connect::FB_BINDINGS_VERSION
+
+ if ActiveRecord::ConnectionAdapters::FrontBaseAdapter.compare_versions(version,"1.0.0") == -1
+ raise AdapterNotFound,
+ 'The FrontBase adapter requires ruby-frontbase version 1.0.0 or greater; you appear ' <<
+ "to be running an older version (#{version}) -- please update ruby-frontbase (gem install ruby-frontbase)."
+ end
+ connection = FBSQL_Connect.connect(host, port, database, username, password, dbpassword, session_name)
+ ConnectionAdapters::FrontBaseAdapter.new(connection, logger, [host, port, database, username, password, dbpassword, session_name], config)
+ end
+ end
+ end
+
+ module ConnectionAdapters
+
+ # From EOF Documentation....
+ # buffer should have space for EOUniqueBinaryKeyLength (12) bytes.
+ # Assigns a world-wide unique ID made up of:
+ # < Sequence [2], ProcessID [2] , Time [4], IP Addr [4] >
+
+ class TwelveByteKey < String #:nodoc:
+ @@mutex = Mutex.new
+ @@sequence_number = rand(65536)
+ @@key_cached_pid_component = nil
+ @@key_cached_ip_component = nil
+
+ def initialize(string = nil)
+ # Generate a unique key
+ if string.nil?
+ new_key = replace('_' * 12)
+
+ new_key[0..1] = self.class.key_sequence_component
+ new_key[2..3] = self.class.key_pid_component
+ new_key[4..7] = self.class.key_time_component
+ new_key[8..11] = self.class.key_ip_component
+ new_key
+ else
+ if string.size == 24
+ string.gsub!(/[[:xdigit:]]{2}/) { |x| x.hex.chr }
+ end
+ raise "string is not 12 bytes long" unless string.size == 12
+ super(string)
+ end
+ end
+
+ def inspect
+ unpack("H*").first.upcase
+ end
+
+ alias_method :to_s, :inspect
+
+ private
+
+ class << self
+ def key_sequence_component
+ seq = nil
+ @@mutex.synchronize do
+ seq = @@sequence_number
+ @@sequence_number = (@@sequence_number + 1) % 65536
+ end
+
+ sequence_component = "__"
+ sequence_component[0] = seq >> 8
+ sequence_component[1] = seq
+ sequence_component
+ end
+
+ def key_pid_component
+ if @@key_cached_pid_component.nil?
+ @@mutex.synchronize do
+ pid = $$
+ pid_component = "__"
+ pid_component[0] = pid >> 8
+ pid_component[1] = pid
+ @@key_cached_pid_component = pid_component
+ end
+ end
+ @@key_cached_pid_component
+ end
+
+ def key_time_component
+ time = Time.new.to_i
+ time_component = "____"
+ time_component[0] = (time & 0xFF000000) >> 24
+ time_component[1] = (time & 0x00FF0000) >> 16
+ time_component[2] = (time & 0x0000FF00) >> 8
+ time_component[3] = (time & 0x000000FF)
+ time_component
+ end
+
+ def key_ip_component
+ if @@key_cached_ip_component.nil?
+ @@mutex.synchronize do
+ old_lookup_flag = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = true
+ udpsocket = UDPSocket.new
+ udpsocket.connect("17.112.152.32",1)
+ ip_string = udpsocket.addr[3]
+ BasicSocket.do_not_reverse_lookup = old_lookup_flag
+ packed = Socket.pack_sockaddr_in(0,ip_string)
+ addr_subset = packed[4..7]
+ ip = addr_subset[0] << 24 | addr_subset[1] << 16 | addr_subset[2] << 8 | addr_subset[3]
+ ip_component = "____"
+ ip_component[0] = (ip & 0xFF000000) >> 24
+ ip_component[1] = (ip & 0x00FF0000) >> 16
+ ip_component[2] = (ip & 0x0000FF00) >> 8
+ ip_component[3] = (ip & 0x000000FF)
+ @@key_cached_ip_component = ip_component
+ end
+ end
+ @@key_cached_ip_component
+ end
+ end
+ end
+
+ class FrontBaseColumn < Column #:nodoc:
+ attr_reader :fb_autogen
+
+ def initialize(base, name, type, typename, limit, precision, scale, default, nullable)
+
+ @base = base
+ @name = name
+ @type = simplified_type(type,typename,limit)
+ @limit = limit
+ @precision = precision
+ @scale = scale
+ @default = default
+ @null = nullable == "YES"
+ @text = [:string, :text].include? @type
+ @number = [:float, :integer, :decimal].include? @type
+ @fb_autogen = false
+
+ if @default
+ @default.gsub!(/^'(.*)'$/,'\1') if @text
+ @fb_autogen = @default.include?("SELECT UNIQUE FROM")
+ case @type
+ when :boolean
+ @default = @default == "TRUE"
+ when :binary
+ if @default != "X''"
+ buffer = ""
+ @default.scan(/../) { |h| buffer << h.hex.chr }
+ @default = buffer
+ else
+ @default = ""
+ end
+ else
+ @default = type_cast(@default)
+ end
+ end
+ end
+
+ # Casts value (which is a String) to an appropriate instance.
+ def type_cast(value)
+ if type == :twelvebytekey
+ ActiveRecord::ConnectionAdapters::TwelveByteKey.new(value)
+ else
+ super(value)
+ end
+ end
+
+ def type_cast_code(var_name)
+ if type == :twelvebytekey
+ "ActiveRecord::ConnectionAdapters::TwelveByteKey.new(#{var_name})"
+ else
+ super(var_name)
+ end
+ end
+
+ private
+ def simplified_type(field_type, type_name,limit)
+ ret_type = :string
+ puts "typecode: [#{field_type}] [#{type_name}]" if FB_TRACE
+
+ # 12 byte primary keys are a special case that Apple's EOF
+ # used heavily. Optimize for this case
+ if field_type == 11 && limit == 96
+ ret_type = :twelvebytekey # BIT(96)
+ else
+ ret_type = case field_type
+ when 1 then :boolean # BOOLEAN
+ when 2 then :integer # INTEGER
+ when 4 then :float # FLOAT
+ when 10 then :string # CHARACTER VARYING
+ when 11 then :bitfield # BIT
+ when 13 then :date # DATE
+ when 14 then :time # TIME
+ when 16 then :timestamp # TIMESTAMP
+ when 20 then :text # CLOB
+ when 21 then :binary # BLOB
+ when 22 then :integer # TINYINT
+ else
+ puts "ERROR: Unknown typecode: [#{field_type}] [#{type_name}]"
+ end
+ end
+ puts "ret_type: #{ret_type.inspect}" if FB_TRACE
+ ret_type
+ end
+ end
+
+ class FrontBaseAdapter < AbstractAdapter
+
+ class << self
+ def compare_versions(v1, v2)
+ v1_seg = v1.split(".")
+ v2_seg = v2.split(".")
+ 0.upto([v1_seg.length,v2_seg.length].min) do |i|
+ step = (v1_seg[i].to_i <=> v2_seg[i].to_i)
+ return step unless step == 0
+ end
+ return v1_seg.length <=> v2_seg.length
+ end
+ end
+
+ def initialize(connection, logger, connection_options, config)
+ super(connection, logger)
+ @connection_options, @config = connection_options, config
+ @transaction_mode = :pessimistic
+
+ # Start out in auto-commit mode
+ self.rollback_db_transaction
+
+ # threaded_connections_test.rb will fail unless we set the session
+ # to optimistic locking mode
+# set_pessimistic_transactions
+# execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
+ end
+
+ # Returns the human-readable name of the adapter. Use mixed case - one
+ # can always use downcase if needed.
+ def adapter_name #:nodoc:
+ 'FrontBase'
+ end
+
+ # Does this adapter support migrations? Backend specific, as the
+ # abstract adapter always returns +false+.
+ def supports_migrations? #:nodoc:
+ true
+ end
+
+ def native_database_types #:nodoc:
+ {
+ :primary_key => "INTEGER DEFAULT UNIQUE PRIMARY KEY",
+ :string => { :name => "VARCHAR", :limit => 255 },
+ :text => { :name => "CLOB" },
+ :integer => { :name => "INTEGER" },
+ :float => { :name => "FLOAT" },
+ :decimal => { :name => "DECIMAL" },
+ :datetime => { :name => "TIMESTAMP" },
+ :timestamp => { :name => "TIMESTAMP" },
+ :time => { :name => "TIME" },
+ :date => { :name => "DATE" },
+ :binary => { :name => "BLOB" },
+ :boolean => { :name => "BOOLEAN" },
+ :twelvebytekey => { :name => "BYTE", :limit => 12}
+ }
+ end
+
+
+ # QUOTING ==================================================
+
+ # Quotes the column value to help prevent
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
+ def quote(value, column = nil)
+ return value.quoted_id if value.respond_to?(:quoted_id)
+
+ retvalue = ""
+
+ puts "quote(#{value.inspect}(#{value.class}),#{column.type.inspect})" if FB_TRACE
+ # If a column was passed in, use column type information
+ unless value.nil?
+ if column
+ retvalue = case column.type
+ when :string
+ if value.kind_of?(String)
+ "'#{quote_string(value.to_s)}'" # ' (for ruby-mode)
+ else
+ "'#{quote_string(value.to_yaml)}'"
+ end
+ when :integer
+ if value.kind_of?(TrueClass)
+ '1'
+ elsif value.kind_of?(FalseClass)
+ '0'
+ else
+ value.to_i.to_s
+ end
+ when :float
+ value.to_f.to_s
+ when :decimal
+ value.to_d.to_s("F")
+ when :datetime, :timestamp
+ "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
+ when :time
+ "TIME '#{value.strftime("%H:%M:%S")}'"
+ when :date
+ "DATE '#{value.strftime("%Y-%m-%d")}'"
+ when :twelvebytekey
+ value = value.to_s.unpack("H*").first unless value.kind_of?(TwelveByteKey)
+ "X'#{value.to_s}'"
+ when :boolean
+ value = quoted_true if value.kind_of?(TrueClass)
+ value = quoted_false if value.kind_of?(FalseClass)
+ value
+ when :binary
+ blob_handle = @connection.create_blob(value.to_s)
+ puts "SQL -> Insert #{value.to_s.length} byte blob as #{retvalue}" if FB_TRACE
+ blob_handle.handle
+ when :text
+ if value.kind_of?(String)
+ clobdata = value.to_s # ' (for ruby-mode)
+ else
+ clobdata = value.to_yaml
+ end
+ clob_handle = @connection.create_clob(clobdata)
+ puts "SQL -> Insert #{value.to_s.length} byte clob as #{retvalue}" if FB_TRACE
+ clob_handle.handle
+ else
+ raise "*** UNKNOWN TYPE: #{column.type.inspect}"
+ end # case
+ # Since we don't have column type info, make a best guess based
+ # on the Ruby class of the value
+ else
+ retvalue = case value
+ when ActiveRecord::ConnectionAdapters::TwelveByteKey
+ s = value.unpack("H*").first
+ "X'#{s}'"
+ when String
+ if column && column.type == :binary
+ s = value.unpack("H*").first
+ "X'#{s}'"
+ elsif column && [:integer, :float, :decimal].include?(column.type)
+ value.to_s
+ else
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
+ end
+ when NilClass
+ "NULL"
+ when TrueClass
+ (column && column.type == :integer ? '1' : quoted_true)
+ when FalseClass
+ (column && column.type == :integer ? '0' : quoted_false)
+ when Float, Fixnum, Bignum, BigDecimal
+ value.to_s
+ when Time, Date, DateTime
+ if column
+ case column.type
+ when :date
+ "DATE '#{value.strftime("%Y-%m-%d")}'"
+ when :time
+ "TIME '#{value.strftime("%H:%M:%S")}'"
+ when :timestamp
+ "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
+ else
+ raise NotImplementedError, "Unknown column type!"
+ end # case
+ else # Column wasn't passed in, so try to guess the right type
+ if value.kind_of? Date
+ "DATE '#{value.strftime("%Y-%m-%d")}'"
+ else
+ if [:hour, :min, :sec].all? {|part| value.send(:part).zero? }
+ "TIME '#{value.strftime("%H:%M:%S")}'"
+ else
+ "TIMESTAMP '#{quoted_date(value)}'"
+ end
+ end
+ end #if column
+ else
+ "'#{quote_string(value.to_yaml)}'"
+ end #case
+ end
+ else
+ retvalue = "NULL"
+ end
+
+ retvalue
+ end # def
+
+ # Quotes a string, escaping any ' (single quote) characters.
+ def quote_string(s)
+ s.gsub(/'/, "''") # ' (for ruby-mode)
+ end
+
+ def quote_column_name(name) #:nodoc:
+ %( "#{name}" )
+ end
+
+ def quoted_true
+ "true"
+ end
+
+ def quoted_false
+ "false"
+ end
+
+
+ # CONNECTION MANAGEMENT ====================================
+
+ def active?
+ true if @connection.status == 1
+ rescue => e
+ false
+ end
+
+ def reconnect!
+ @connection.close rescue nil
+ @connection = FBSQL_Connect.connect(*@connection_options.first(7))
+ end
+
+ # Close this connection
+ def disconnect!
+ @connection.close rescue nil
+ @active = false
+ end
+
+ # DATABASE STATEMENTS ======================================
+
+ # Returns an array of record hashes with the column names as keys and
+ # column values as values.
+ def select_all(sql, name = nil) #:nodoc:
+ fbsql = cleanup_fb_sql(sql)
+ return_value = []
+ fbresult = execute(sql, name)
+ puts "select_all SQL -> #{fbsql}" if FB_TRACE
+ columns = fbresult.columns
+
+ fbresult.each do |row|
+ puts "SQL <- #{row.inspect}" if FB_TRACE
+ hashed_row = {}
+ colnum = 0
+ row.each do |col|
+ hashed_row[columns[colnum]] = col
+ if col.kind_of?(FBSQL_LOB)
+ hashed_row[columns[colnum]] = col.read
+ end
+ colnum += 1
+ end
+ puts "raw row: #{hashed_row.inspect}" if FB_TRACE
+ return_value << hashed_row
+ end
+ return_value
+ end
+
+ def select_one(sql, name = nil) #:nodoc:
+ fbsql = cleanup_fb_sql(sql)
+ return_value = []
+ fbresult = execute(fbsql, name)
+ puts "SQL -> #{fbsql}" if FB_TRACE
+ columns = fbresult.columns
+
+ fbresult.each do |row|
+ puts "SQL <- #{row.inspect}" if FB_TRACE
+ hashed_row = {}
+ colnum = 0
+ row.each do |col|
+ hashed_row[columns[colnum]] = col
+ if col.kind_of?(FBSQL_LOB)
+ hashed_row[columns[colnum]] = col.read
+ end
+ colnum += 1
+ end
+ return_value << hashed_row
+ break
+ end
+ fbresult.clear
+ return_value.first
+ end
+
+ def query(sql, name = nil) #:nodoc:
+ fbsql = cleanup_fb_sql(sql)
+ puts "SQL(query) -> #{fbsql}" if FB_TRACE
+ log(fbsql, name) { @connection.query(fbsql) }
+ rescue => e
+ puts "FB Exception: #{e.inspect}" if FB_TRACE
+ raise e
+ end
+
+ def execute(sql, name = nil) #:nodoc:
+ fbsql = cleanup_fb_sql(sql)
+ puts "SQL(execute) -> #{fbsql}" if FB_TRACE
+ log(fbsql, name) { @connection.query(fbsql) }
+ rescue ActiveRecord::StatementInvalid => e
+ if e.message.scan(/Table name - \w* - exists/).empty?
+ puts "FB Exception: #{e.inspect}" if FB_TRACE
+ raise e
+ end
+ end
+
+ # Returns the last auto-generated ID from the affected table.
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+ puts "SQL -> #{sql.inspect}" if FB_TRACE
+ execute(sql, name)
+ id_value || pk
+ end
+
+ # Executes the update statement and returns the number of rows affected.
+ def update(sql, name = nil) #:nodoc:
+ puts "SQL -> #{sql.inspect}" if FB_TRACE
+ execute(sql, name).num_rows
+ end
+
+ alias_method :delete, :update #:nodoc:
+
+ def set_pessimistic_transactions
+ if @transaction_mode == :optimistic
+ execute "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, LOCKING PESSIMISTIC, READ WRITE"
+ @transaction_mode = :pessimistic
+ end
+ end
+
+ def set_optimistic_transactions
+ if @transaction_mode == :pessimistic
+ execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
+ @transaction_mode = :optimistic
+ end
+ end
+
+ def begin_db_transaction #:nodoc:
+ execute "SET COMMIT FALSE" rescue nil
+ end
+
+ def commit_db_transaction #:nodoc:
+ execute "COMMIT"
+ ensure
+ execute "SET COMMIT TRUE"
+ end
+
+ def rollback_db_transaction #:nodoc:
+ execute "ROLLBACK"
+ ensure
+ execute "SET COMMIT TRUE"
+ end
+
+ def add_limit_offset!(sql, options) #:nodoc:
+ if limit = options[:limit]
+ offset = options[:offset] || 0
+
+# Here is the full syntax FrontBase supports:
+# (from gclem@frontbase.com)
+#
+# TOP
+# TOP ( , )
+
+ # "TOP 0" is not allowed, so we have
+ # to use a cheap trick.
+ if limit.zero?
+ case sql
+ when /WHERE/i
+ sql.sub!(/WHERE/i, 'WHERE 0 = 1 AND ')
+ when /ORDER\s+BY/i
+ sql.sub!(/ORDER\s+BY/i, 'WHERE 0 = 1 ORDER BY')
+ else
+ sql << 'WHERE 0 = 1'
+ end
+ else
+ if offset.zero?
+ sql.replace sql.gsub("SELECT ","SELECT TOP #{limit} ")
+ else
+ sql.replace sql.gsub("SELECT ","SELECT TOP(#{offset},#{limit}) ")
+ end
+ end
+ end
+ end
+
+ def prefetch_primary_key?(table_name = nil)
+ true
+ end
+
+ # Returns the next sequence value from a sequence generator. Not generally
+ # called directly; used by ActiveRecord to get the next primary key value
+ # when inserting a new database record (see #prefetch_primary_key?).
+ def next_sequence_value(sequence_name)
+ unique = select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value")
+ # The test cases cannot handle a zero primary key
+ unique.zero? ? select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value") : unique
+ end
+
+ def default_sequence_name(table, column)
+ table
+ end
+
+ # Set the sequence to the max value of the table's column.
+ def reset_sequence!(table, column, sequence = nil)
+ klasses = classes_for_table_name(table)
+ klass = klasses.nil? ? nil : klasses.first
+ pk = klass.primary_key unless klass.nil?
+ if pk && klass.columns_hash[pk].type == :integer
+ execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
+ end
+ end
+
+ def classes_for_table_name(table)
+ ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
+ end
+
+ def reset_pk_sequence!(table, pk = nil, sequence = nil)
+ klasses = classes_for_table_name(table)
+ klass = klasses.nil? ? nil : klasses.first
+ pk = klass.primary_key unless klass.nil?
+ if pk && klass.columns_hash[pk].type == :integer
+ mpk = select_value("SELECT MAX(#{pk}) FROM #{table}")
+ execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
+ end
+ end
+
+ # SCHEMA STATEMENTS ========================================
+
+ def structure_dump #:nodoc:
+ select_all("SHOW TABLES").inject('') do |structure, table|
+ structure << select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] << ";\n\n"
+ end
+ end
+
+ def recreate_database(name) #:nodoc:
+ drop_database(name)
+ create_database(name)
+ end
+
+ def create_database(name) #:nodoc:
+ execute "CREATE DATABASE #{name}"
+ end
+
+ def drop_database(name) #:nodoc:
+ execute "DROP DATABASE #{name}"
+ end
+
+ def current_database
+ select_value('SELECT "CATALOG_NAME" FROM INFORMATION_SCHEMA.CATALOGS').downcase
+ end
+
+ def tables(name = nil) #:nodoc:
+ select_values(<<-SQL, nil)
+ SELECT "TABLE_NAME"
+ FROM INFORMATION_SCHEMA.TABLES AS T0,
+ INFORMATION_SCHEMA.SCHEMATA AS T1
+ WHERE T0.SCHEMA_PK = T1.SCHEMA_PK
+ AND "SCHEMA_NAME" = CURRENT_SCHEMA
+ SQL
+ end
+
+ def indexes(table_name, name = nil)#:nodoc:
+ indexes = []
+ current_index = nil
+ sql = <<-SQL
+ SELECT INDEX_NAME, T2.ORDINAL_POSITION, INDEX_COLUMN_COUNT, INDEX_TYPE,
+ "COLUMN_NAME", IS_NULLABLE
+ FROM INFORMATION_SCHEMA.TABLES AS T0,
+ INFORMATION_SCHEMA.INDEXES AS T1,
+ INFORMATION_SCHEMA.INDEX_COLUMN_USAGE AS T2,
+ INFORMATION_SCHEMA.COLUMNS AS T3
+ WHERE T0."TABLE_NAME" = '#{table_name}'
+ AND INDEX_TYPE <> 0
+ AND T0.TABLE_PK = T1.TABLE_PK
+ AND T0.TABLE_PK = T2.TABLE_PK
+ AND T0.TABLE_PK = T3.TABLE_PK
+ AND T1.INDEXES_PK = T2.INDEX_PK
+ AND T2.COLUMN_PK = T3.COLUMN_PK
+ ORDER BY INDEX_NAME, T2.ORDINAL_POSITION
+ SQL
+
+ columns = []
+ query(sql).each do |row|
+ index_name = row[0]
+ ord_position = row[1]
+ ndx_colcount = row[2]
+ index_type = row[3]
+ column_name = row[4]
+
+ is_unique = index_type == 1
+
+ columns << column_name
+ if ord_position == ndx_colcount
+ indexes << IndexDefinition.new(table_name, index_name, is_unique , columns)
+ columns = []
+ end
+ end
+ indexes
+ end
+
+ def columns(table_name, name = nil)#:nodoc:
+ sql = <<-SQL
+ SELECT "TABLE_NAME", "COLUMN_NAME", ORDINAL_POSITION, IS_NULLABLE, COLUMN_DEFAULT,
+ DATA_TYPE, DATA_TYPE_CODE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION,
+ NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, DATETIME_PRECISION, DATETIME_PRECISION_LEADING
+ FROM INFORMATION_SCHEMA.TABLES T0,
+ INFORMATION_SCHEMA.COLUMNS T1,
+ INFORMATION_SCHEMA.DATA_TYPE_DESCRIPTOR T3
+ WHERE "TABLE_NAME" = '#{table_name}'
+ AND T0.TABLE_PK = T1.TABLE_PK
+ AND T0.TABLE_PK = T3.TABLE_OR_DOMAIN_PK
+ AND T1.COLUMN_PK = T3.COLUMN_NAME_PK
+ ORDER BY T1.ORDINAL_POSITION
+ SQL
+
+ rawresults = query(sql,name)
+ columns = []
+ rawresults.each do |field|
+ args = [base = field[0],
+ name = field[1],
+ typecode = field[6],
+ typestring = field[5],
+ limit = field[7],
+ precision = field[8],
+ scale = field[9],
+ default = field[4],
+ nullable = field[3]]
+ columns << FrontBaseColumn.new(*args)
+ end
+ columns
+ end
+
+ def create_table(name, options = {})
+ table_definition = TableDefinition.new(self)
+ table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
+
+ yield table_definition
+
+ if options[:force]
+ drop_table(name) rescue nil
+ end
+
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
+ create_sql << "#{name} ("
+ create_sql << table_definition.to_sql
+ create_sql << ") #{options[:options]}"
+ begin_db_transaction
+ execute create_sql
+ commit_db_transaction
+ rescue ActiveRecord::StatementInvalid => e
+ raise e unless e.message.match(/Table name - \w* - exists/)
+ end
+
+ def rename_table(name, new_name)
+ columns = columns(name)
+ pkcol = columns.find {|c| c.fb_autogen}
+ execute "ALTER TABLE NAME #{name} TO #{new_name}"
+ if pkcol
+ change_column_default(new_name,pkcol.name,"UNIQUE")
+ begin_db_transaction
+ mpk = select_value("SELECT MAX(#{pkcol.name}) FROM #{new_name}")
+ mpk = 0 if mpk.nil?
+ execute "SET UNIQUE=#{mpk} FOR #{new_name}"
+ commit_db_transaction
+ end
+ end
+
+ # Drops a table from the database.
+ def drop_table(name, options = {})
+ execute "DROP TABLE #{name} RESTRICT"
+ rescue ActiveRecord::StatementInvalid => e
+ raise e unless e.message.match(/Referenced TABLE - \w* - does not exist/)
+ end
+
+ # Adds a new column to the named table.
+ # See TableDefinition#column for details of the options you can use.
+ def add_column(table_name, column_name, type, options = {})
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
+ options[:type] = type
+ add_column_options!(add_column_sql, options)
+ execute(add_column_sql)
+ end
+
+ def add_column_options!(sql, options) #:nodoc:
+ default_value = quote(options[:default], options[:column])
+ if options_include_default?(options)
+ if options[:type] == :boolean
+ default_value = options[:default] == 0 ? quoted_false : quoted_true
+ end
+ sql << " DEFAULT #{default_value}"
+ end
+ sql << " NOT NULL" if options[:null] == false
+ end
+
+ # Removes the column from the table definition.
+ # ===== Examples
+ # remove_column(:suppliers, :qualification)
+ def remove_column(table_name, column_name)
+ execute "ALTER TABLE #{table_name} DROP #{column_name} RESTRICT"
+ end
+
+ def remove_index(table_name, options = {}) #:nodoc:
+ if options[:unique]
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{quote_column_name(index_name(table_name, options))} RESTRICT"
+ else
+ execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
+ end
+ end
+
+ def change_column_default(table_name, column_name, default) #:nodoc:
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{default}" if default != "NULL"
+ end
+
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
+ change_column_sql = %( ALTER COLUMN "#{table_name}"."#{column_name}" TO #{type_to_sql(type, options[:limit])} )
+ execute(change_column_sql)
+ change_column_sql = %( ALTER TABLE "#{table_name}" ALTER COLUMN "#{column_name}" )
+
+ if options_include_default?(options)
+ default_value = quote(options[:default], options[:column])
+ if type == :boolean
+ default_value = options[:default] == 0 ? quoted_false : quoted_true
+ end
+ change_column_sql << " SET DEFAULT #{default_value}"
+ end
+
+ execute(change_column_sql)
+
+# change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
+# add_column_options!(change_column_sql, options)
+# execute(change_column_sql)
+ end
+
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
+ execute %( ALTER COLUMN NAME "#{table_name}"."#{column_name}" TO "#{new_column_name}" )
+ end
+
+ private
+
+ # Clean up sql to make it something FrontBase can digest
+ def cleanup_fb_sql(sql) #:nodoc:
+ # Turn non-standard != into standard <>
+ cleansql = sql.gsub("!=", "<>")
+ # Strip blank lines and comments
+ cleansql.split("\n").reject { |line| line.match(/^(?:\s*|--.*)$/) } * "\n"
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb b/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
new file mode 100644
index 00000000..02cf5650
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
@@ -0,0 +1,106 @@
+module ActiveRecord
+ module Locking
+ # Active Records support optimistic locking if the field lock_version 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.
+ #
+ # You must ensure that your database schema defaults the lock_version column to 0.
+ #
+ # This behavior can be turned off by setting ActiveRecord::Base.lock_optimistically = false.
+ # To override the name of the lock_version column, invoke the set_locking_column method.
+ # This method uses the same syntax as set_table_name
+ module Optimistic
+ def self.included(base) #:nodoc:
+ super
+ base.extend ClassMethods
+
+ base.cattr_accessor :lock_optimistically, :instance_writer => false
+ base.lock_optimistically = true
+
+ base.alias_method_chain :update, :lock
+ base.alias_method_chain :attributes_from_column_definition, :lock
+
+ class << base
+ alias_method :locking_column=, :set_locking_column
+ end
+ end
+
+ def locking_enabled? #:nodoc:
+ lock_optimistically && respond_to?(self.class.locking_column)
+ end
+
+ def attributes_from_column_definition_with_lock
+ result = attributes_from_column_definition_without_lock
+
+ # If the locking column has no default value set,
+ # start the lock version at zero. Note we can't use
+ # locking_enabled? at this point as @attributes may
+ # not have been initialized yet
+
+ if lock_optimistically && result.include?(self.class.locking_column)
+ result[self.class.locking_column] ||= 0
+ end
+
+ return result
+ end
+
+ def update_with_lock #:nodoc:
+ return update_without_lock unless locking_enabled?
+
+ lock_col = self.class.locking_column
+ previous_value = send(lock_col)
+ send(lock_col + '=', previous_value + 1)
+
+ affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking")
+ UPDATE #{self.class.table_name}
+ SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))}
+ WHERE #{self.class.primary_key} = #{quote_value(id)}
+ AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}
+ end_sql
+
+ unless affected_rows == 1
+ raise ActiveRecord::StaleObjectError, "Attempted to update a stale object"
+ end
+
+ return true
+ end
+
+ module ClassMethods
+ DEFAULT_LOCKING_COLUMN = 'lock_version'
+
+ # 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
+ end
+
+ # The version column used for optimistic locking. Defaults to lock_version.
+ def locking_column
+ reset_locking_column
+ end
+
+ # Quote the column name used for optimistic locking.
+ def quoted_locking_column
+ connection.quote_column_name(locking_column)
+ end
+
+ # Reset the column used for optimistic locking back to the lock_version default.
+ def reset_locking_column
+ set_locking_column DEFAULT_LOCKING_COLUMN
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/locking/pessimistic.rb b/vendor/rails/activerecord/lib/active_record/locking/pessimistic.rb
new file mode 100644
index 00000000..caad7607
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/locking/pessimistic.rb
@@ -0,0 +1,77 @@
+# Copyright (c) 2006 Shugo Maeda
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject
+# to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+module ActiveRecord
+ module Locking
+ # Locking::Pessimistic provides support for row-level locking using
+ # SELECT ... FOR UPDATE and other lock types.
+ #
+ # Pass :lock => true to ActiveRecord::Base.find to obtain an exclusive
+ # lock on the selected rows:
+ # # select * from accounts where id=1 for update
+ # Account.find(1, :lock => true)
+ #
+ # Pass :lock => 'some locking clause' to give a database-specific locking clause
+ # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'.
+ #
+ # Example:
+ # Account.transaction do
+ # # select * from accounts where name = 'shugo' limit 1 for update
+ # shugo = Account.find(:first, :conditions => "name = 'shugo'", :lock => true)
+ # yuko = Account.find(:first, :conditions => "name = 'yuko'", :lock => true)
+ # shugo.balance -= 100
+ # shugo.save!
+ # yuko.balance += 100
+ # yuko.save!
+ # end
+ #
+ # You can also use ActiveRecord::Base#lock! method to lock one record by id.
+ # This may be better if you don't need to lock every row. Example:
+ # Account.transaction do
+ # # select * from accounts where ...
+ # accounts = Account.find(:all, :conditions => ...)
+ # account1 = accounts.detect { |account| ... }
+ # account2 = accounts.detect { |account| ... }
+ # # select * from accounts where id=? for update
+ # account1.lock!
+ # account2.lock!
+ # account1.balance -= 100
+ # account1.save!
+ # account2.balance += 100
+ # account2.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/8.1/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
+ module Pessimistic
+ # Obtain a row lock on this record. Reloads the record to obtain the requested
+ # lock. Pass an SQL locking clause to append the end of the SELECT statement
+ # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
+ # the locked record.
+ def lock!(lock = true)
+ reload(:lock => lock) unless new_record?
+ self
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/xml_serialization.rb b/vendor/rails/activerecord/lib/active_record/xml_serialization.rb
new file mode 100644
index 00000000..7a816753
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/xml_serialization.rb
@@ -0,0 +1,308 @@
+module ActiveRecord #:nodoc:
+ module XmlSerialization
+ # Builds an XML document to represent the model. Some configuration is
+ # availble through +options+, however more complicated cases should use
+ # override ActiveRecord's to_xml.
+ #
+ # By default the generated XML document will include the processing
+ # instruction and all object's attributes. For example:
+ #
+ #
+ #
+ # The First Topic
+ # David
+ # 1
+ # false
+ # 0
+ # 2000-01-01T08:28:00+12:00
+ # 2003-07-16T09:28:00+1200
+ # Have a nice day
+ # david@loudthinking.com
+ #
+ # 2004-04-15
+ #
+ #
+ # This behavior can be controlled with :only, :except,
+ # :skip_instruct, :skip_types and :dasherize. The :only and
+ # :except options are the same as for the #attributes method.
+ # The default is to dasherize all column names, to disable this,
+ # set :dasherize to false. To not have the column type included
+ # in the XML output, set :skip_types to false.
+ #
+ # For instance:
+ #
+ # topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ])
+ #
+ #
+ # The First Topic
+ # David
+ # false
+ # Have a nice day
+ # david@loudthinking.com
+ #
+ # 2004-04-15
+ #
+ #
+ # To include first level associations use :include
+ #
+ # firm.to_xml :include => [ :account, :clients ]
+ #
+ #
+ #
+ # 1
+ # 1
+ # 37signals
+ #
+ #
+ # 1
+ # Summit
+ #
+ #
+ # 1
+ # Microsoft
+ #
+ #
+ #
+ # 1
+ # 50
+ #
+ #
+ #
+ # To include any methods on the object(s) being called use :methods
+ #
+ # firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
+ #
+ #
+ # # ... normal attributes as shown above ...
+ # 100000000000000000
+ # 5
+ #
+ #
+ # To call any Proc's on the object(s) use :procs. The Proc's
+ # are passed a modified version of the options hash that was
+ # given to #to_xml.
+ #
+ # proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
+ # firm.to_xml :procs => [ proc ]
+ #
+ #
+ # # ... normal attributes as shown above ...
+ # def
+ #
+ #
+ # You may override the to_xml method in your ActiveRecord::Base
+ # subclasses if you need to. The general form of doing this is
+ #
+ # class IHaveMyOwnXML < ActiveRecord::Base
+ # def to_xml(options = {})
+ # options[:indent] ||= 2
+ # xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ # xml.instruct! unless options[:skip_instruct]
+ # xml.level_one do
+ # xml.tag!(:second_level, 'content')
+ # end
+ # end
+ # end
+ def to_xml(options = {})
+ XmlSerializer.new(self, options).to_s
+ end
+ end
+
+ class XmlSerializer #:nodoc:
+ attr_reader :options
+
+ def initialize(record, options = {})
+ @record, @options = record, options.dup
+ end
+
+ def builder
+ @builder ||= begin
+ options[:indent] ||= 2
+ builder = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+
+ unless options[:skip_instruct]
+ builder.instruct!
+ options[:skip_instruct] = true
+ end
+
+ builder
+ end
+ end
+
+ def root
+ root = (options[:root] || @record.class.to_s.underscore).to_s
+ dasherize? ? root.dasherize : root
+ end
+
+ def dasherize?
+ !options.has_key?(:dasherize) || options[:dasherize]
+ end
+
+
+ # To replicate the behavior in ActiveRecord#attributes,
+ # :except takes precedence over :only. If :only is not set
+ # for a N level model but is set for the N+1 level models,
+ # then because :except is set to a default value, the second
+ # level model can have both :except and :only set. So if
+ # :only is set, always delete :except.
+ def serializable_attributes
+ attribute_names = @record.attribute_names
+
+ if options[:only]
+ options.delete(:except)
+ attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
+ else
+ options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
+ attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
+ end
+
+ attribute_names.collect { |name| Attribute.new(name, @record) }
+ end
+
+ def serializable_method_attributes
+ Array(options[:methods]).collect { |name| MethodAttribute.new(name.to_s, @record) }
+ end
+
+
+ def add_attributes
+ (serializable_attributes + serializable_method_attributes).each do |attribute|
+ add_tag(attribute)
+ end
+ end
+
+ def add_includes
+ if include_associations = options.delete(:include)
+ root_only_or_except = { :except => options[:except],
+ :only => options[:only] }
+
+ include_has_options = include_associations.is_a?(Hash)
+
+ for association in include_has_options ? include_associations.keys : Array(include_associations)
+ association_options = include_has_options ? include_associations[association] : root_only_or_except
+
+ opts = options.merge(association_options)
+
+ case @record.class.reflect_on_association(association).macro
+ when :has_many, :has_and_belongs_to_many
+ records = @record.send(association).to_a
+ unless records.empty?
+ tag = records.first.class.to_s.underscore.pluralize
+ tag = tag.dasherize if dasherize?
+
+ builder.tag!(tag) do
+ records.each { |r| r.to_xml(opts.merge(:root => association.to_s.singularize)) }
+ end
+ end
+ when :has_one, :belongs_to
+ if record = @record.send(association)
+ record.to_xml(opts.merge(:root => association))
+ end
+ end
+ end
+
+ options[:include] = include_associations
+ end
+ end
+
+ def add_procs
+ if procs = options.delete(:procs)
+ [ *procs ].each do |proc|
+ proc.call(options)
+ end
+ end
+ end
+
+
+ def add_tag(attribute)
+ builder.tag!(
+ dasherize? ? attribute.name.dasherize : attribute.name,
+ attribute.value.to_s,
+ attribute.decorations(!options[:skip_types])
+ )
+ end
+
+ def serialize
+ args = [root]
+ if options[:namespace]
+ args << {:xmlns=>options[:namespace]}
+ end
+
+ builder.tag!(*args) do
+ add_attributes
+ add_includes
+ add_procs
+ end
+ end
+
+ alias_method :to_s, :serialize
+
+ class Attribute #:nodoc:
+ attr_reader :name, :value, :type
+
+ def initialize(name, record)
+ @name, @record = name, record
+
+ @type = compute_type
+ @value = compute_value
+ end
+
+ # There is a significant speed improvement if the value
+ # does not need to be escaped, as #tag! escapes all values
+ # to ensure that valid XML is generated. For known binary
+ # values, it is at least an order of magnitude faster to
+ # Base64 encode binary values and directly put them in the
+ # output XML than to pass the original value or the Base64
+ # encoded value to the #tag! method. It definitely makes
+ # no sense to Base64 encode the value and then give it to
+ # #tag!, since that just adds additional overhead.
+ def needs_encoding?
+ ![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
+ end
+
+ def decorations(include_types = true)
+ decorations = {}
+
+ if type == :binary
+ decorations[:encoding] = 'base64'
+ end
+
+ if include_types && type != :string
+ decorations[:type] = type
+ end
+
+ decorations
+ end
+
+ protected
+ def compute_type
+ type = @record.class.columns_hash[name].type
+
+ case type
+ when :text
+ :string
+ when :time
+ :datetime
+ else
+ type
+ end
+ end
+
+ def compute_value
+ value = @record.send(name)
+
+ if formatter = Hash::XML_FORMATTING[type.to_s]
+ value ? formatter.call(value) : nil
+ else
+ value
+ end
+ end
+ end
+
+ class MethodAttribute < Attribute #:nodoc:
+ protected
+ def compute_type
+ Hash::XML_TYPE_NAMES[@record.send(name).class.name] || :string
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/test/active_schema_test_mysql.rb b/vendor/rails/activerecord/test/active_schema_test_mysql.rb
new file mode 100644
index 00000000..b7222352
--- /dev/null
+++ b/vendor/rails/activerecord/test/active_schema_test_mysql.rb
@@ -0,0 +1,31 @@
+require 'abstract_unit'
+
+class ActiveSchemaTest < Test::Unit::TestCase
+ def setup
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
+ alias_method :real_execute, :execute
+ def execute(sql, name = nil) return sql end
+ end
+ end
+
+ def teardown
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:alias_method, :execute, :real_execute)
+ end
+
+ def test_drop_table
+ assert_equal "DROP TABLE people", drop_table(:people)
+ end
+
+ def test_add_column
+ assert_equal "ALTER TABLE people ADD `last_name` varchar(255)", add_column(:people, :last_name, :string)
+ end
+
+ def test_add_column_with_limit
+ assert_equal "ALTER TABLE people ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
+ end
+
+ private
+ def method_missing(method_symbol, *arguments)
+ ActiveRecord::Base.connection.send(method_symbol, *arguments)
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/adapter_test_sqlserver.rb b/vendor/rails/activerecord/test/adapter_test_sqlserver.rb
new file mode 100644
index 00000000..bf746712
--- /dev/null
+++ b/vendor/rails/activerecord/test/adapter_test_sqlserver.rb
@@ -0,0 +1,81 @@
+require 'abstract_unit'
+require 'fixtures/default'
+require 'fixtures/post'
+require 'fixtures/task'
+
+class SqlServerAdapterTest < Test::Unit::TestCase
+ fixtures :posts, :tasks
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.execute("SET LANGUAGE us_english")
+ end
+
+ # SQL Server 2000 has a bug where some unambiguous date formats are not
+ # correctly identified if the session language is set to german
+ def test_date_insertion_when_language_is_german
+ @connection.execute("SET LANGUAGE deutsch")
+
+ assert_nothing_raised do
+ Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
+ end
+ end
+
+ def test_execute_without_block_closes_statement
+ assert_all_statements_used_are_closed do
+ @connection.execute("SELECT 1")
+ end
+ end
+
+ def test_execute_with_block_closes_statement
+ assert_all_statements_used_are_closed do
+ @connection.execute("SELECT 1") do |sth|
+ assert !sth.finished?, "Statement should still be alive within block"
+ end
+ end
+ end
+
+ def test_insert_with_identity_closes_statement
+ assert_all_statements_used_are_closed do
+ @connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
+ end
+ end
+
+ def test_insert_without_identity_closes_statement
+ assert_all_statements_used_are_closed do
+ @connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")
+ end
+ end
+
+ def test_active_closes_statement
+ assert_all_statements_used_are_closed do
+ @connection.active?
+ end
+ end
+
+ def assert_all_statements_used_are_closed(&block)
+ existing_handles = []
+ ObjectSpace.each_object(DBI::StatementHandle) {|handle| existing_handles << handle}
+ GC.disable
+
+ yield
+
+ used_handles = []
+ ObjectSpace.each_object(DBI::StatementHandle) {|handle| used_handles << handle unless existing_handles.include? handle}
+
+ assert_block "No statements were used within given block" do
+ used_handles.size > 0
+ end
+
+ ObjectSpace.each_object(DBI::StatementHandle) do |handle|
+ assert_block "Statement should have been closed within given block" do
+ handle.finished?
+ end
+ end
+ ensure
+ GC.enable
+ end
+end
diff --git a/vendor/rails/activerecord/test/associations/callbacks_test.rb b/vendor/rails/activerecord/test/associations/callbacks_test.rb
new file mode 100644
index 00000000..d0f7fa67
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/callbacks_test.rb
@@ -0,0 +1,126 @@
+require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/author'
+require 'fixtures/category'
+require 'fixtures/project'
+require 'fixtures/developer'
+
+class AssociationCallbacksTest < Test::Unit::TestCase
+ fixtures :posts, :authors, :projects, :developers
+
+ def setup
+ @david = authors(:david)
+ @thinking = posts(:thinking)
+ @authorless = posts(:authorless)
+ assert @david.post_log.empty?
+ end
+
+ def test_adding_macro_callbacks
+ @david.posts_with_callbacks << @thinking
+ assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
+ @david.posts_with_callbacks << @thinking
+ assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
+ "after_adding#{@thinking.id}"], @david.post_log
+ end
+
+ def test_adding_with_proc_callbacks
+ @david.posts_with_proc_callbacks << @thinking
+ assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
+ @david.posts_with_proc_callbacks << @thinking
+ assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
+ "after_adding#{@thinking.id}"], @david.post_log
+ end
+
+ def test_removing_with_macro_callbacks
+ first_post, second_post = @david.posts_with_callbacks[0, 2]
+ @david.posts_with_callbacks.delete(first_post)
+ assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
+ @david.posts_with_callbacks.delete(second_post)
+ assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
+ "after_removing#{second_post.id}"], @david.post_log
+ end
+
+ def test_removing_with_proc_callbacks
+ first_post, second_post = @david.posts_with_callbacks[0, 2]
+ @david.posts_with_proc_callbacks.delete(first_post)
+ assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
+ @david.posts_with_proc_callbacks.delete(second_post)
+ assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
+ "after_removing#{second_post.id}"], @david.post_log
+ end
+
+ def test_multiple_callbacks
+ @david.posts_with_multiple_callbacks << @thinking
+ assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
+ "after_adding_proc#{@thinking.id}"], @david.post_log
+ @david.posts_with_multiple_callbacks << @thinking
+ assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
+ "after_adding_proc#{@thinking.id}", "before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}",
+ "after_adding#{@thinking.id}", "after_adding_proc#{@thinking.id}"], @david.post_log
+ end
+
+ def test_has_and_belongs_to_many_add_callback
+ david = developers(:david)
+ ar = projects(:active_record)
+ assert ar.developers_log.empty?
+ ar.developers_with_callbacks << david
+ assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
+ ar.developers_with_callbacks << david
+ assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
+ "after_adding#{david.id}"], ar.developers_log
+ end
+
+ def test_has_and_belongs_to_many_remove_callback
+ david = developers(:david)
+ jamis = developers(:jamis)
+ activerecord = projects(:active_record)
+ assert activerecord.developers_log.empty?
+ activerecord.developers_with_callbacks.delete(david)
+ assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
+
+ activerecord.developers_with_callbacks.delete(jamis)
+ assert_equal ["before_removing#{david.id}", "after_removing#{david.id}", "before_removing#{jamis.id}",
+ "after_removing#{jamis.id}"], activerecord.developers_log
+ end
+
+ def test_has_and_belongs_to_many_remove_callback_on_clear
+ activerecord = projects(:active_record)
+ assert activerecord.developers_log.empty?
+ if activerecord.developers_with_callbacks.size == 0
+ activerecord.developers << developers(:david)
+ activerecord.developers << developers(:jamis)
+ activerecord.reload
+ assert activerecord.developers_with_callbacks.size == 2
+ end
+ log_array = activerecord.developers_with_callbacks.collect {|d| ["before_removing#{d.id}","after_removing#{d.id}"]}.flatten.sort
+ assert activerecord.developers_with_callbacks.clear
+ assert_equal log_array, activerecord.developers_log.sort
+ end
+
+ def test_dont_add_if_before_callback_raises_exception
+ assert !@david.unchangable_posts.include?(@authorless)
+ begin
+ @david.unchangable_posts << @authorless
+ rescue Exception => e
+ end
+ assert @david.post_log.empty?
+ assert !@david.unchangable_posts.include?(@authorless)
+ @david.reload
+ assert !@david.unchangable_posts.include?(@authorless)
+ end
+
+ def test_push_with_attributes
+ assert_deprecated 'push_with_attributes' do
+ david = developers(:david)
+ activerecord = projects(:active_record)
+ assert activerecord.developers_log.empty?
+ activerecord.developers_with_callbacks.push_with_attributes(david, {})
+ assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], activerecord.developers_log
+ activerecord.developers_with_callbacks.push_with_attributes(david, {})
+ assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
+ "after_adding#{david.id}"], activerecord.developers_log
+ end
+ end
+end
+
diff --git a/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb b/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
new file mode 100644
index 00000000..863af199
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
@@ -0,0 +1,138 @@
+require 'abstract_unit'
+require 'active_record/acts/list'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/author'
+require 'fixtures/category'
+require 'fixtures/categorization'
+require 'fixtures/mixin'
+require 'fixtures/company'
+require 'fixtures/topic'
+require 'fixtures/reply'
+
+class CascadedEagerLoadingTest < Test::Unit::TestCase
+ fixtures :authors, :mixins, :companies, :posts, :topics
+
+ def test_eager_association_loading_with_cascaded_two_levels
+ authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
+ assert_equal 2, authors.size
+ assert_equal 5, authors[0].posts.size
+ assert_equal 1, authors[1].posts.size
+ assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ end
+
+ def test_eager_association_loading_with_cascaded_two_levels_and_one_level
+ authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
+ assert_equal 2, authors.size
+ assert_equal 5, authors[0].posts.size
+ assert_equal 1, authors[1].posts.size
+ assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 1, authors[0].categorizations.size
+ assert_equal 2, authors[1].categorizations.size
+ end
+
+ def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
+ authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
+ assert_equal 2, authors.size
+ assert_equal 5, authors[0].posts.size
+ assert_equal 1, authors[1].posts.size
+ assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ end
+
+ def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
+ authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id")
+ assert_equal 2, authors.size
+ assert_equal 5, authors[0].posts.size
+ assert_equal authors(:david).name, authors[0].name
+ assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
+ end
+
+ def test_eager_association_loading_with_cascaded_two_levels_with_condition
+ authors = Author.find(:all, :include=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id")
+ assert_equal 1, authors.size
+ assert_equal 5, authors[0].posts.size
+ end
+
+ def test_eager_association_loading_with_acts_as_tree
+ roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")
+ assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], roots
+ assert_no_queries do
+ assert_equal 2, roots[0].children.size
+ assert_equal 0, roots[1].children.size
+ assert_equal 0, roots[2].children.size
+ end
+ end
+
+ def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
+ firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
+ assert_equal 2, firms.size
+ assert_equal firms.first.account, firms.first.account.firm.account
+ assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
+ assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
+ end
+
+ def test_eager_association_loading_with_has_many_sti
+ topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
+ assert_equal [topics(:first), topics(:second)], topics
+ assert_no_queries do
+ assert_equal 1, topics[0].replies.size
+ assert_equal 0, topics[1].replies.size
+ end
+ end
+
+ def test_eager_association_loading_with_belongs_to_sti
+ replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
+ assert_equal [topics(:second)], replies
+ assert_equal topics(:first), assert_no_queries { replies.first.topic }
+ end
+
+ def test_eager_association_loading_with_multiple_stis_and_order
+ author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
+ assert_equal authors(:david), author
+ assert_no_queries do
+ author.posts.first.special_comments
+ author.posts.first.very_special_comment
+ end
+ end
+
+ def test_eager_association_loading_of_stis_with_multiple_references
+ authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
+ assert_equal [authors(:david)], authors
+ assert_no_queries do
+ authors.first.posts.first.special_comments.first.post.special_comments
+ authors.first.posts.first.special_comments.first.post.very_special_comment
+ end
+ end
+
+ def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
+ root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')
+ assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }
+ end
+
+ def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
+ root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')
+ assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }
+ end
+
+ def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
+ leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')
+ assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }
+ end
+end
+
+
+require 'fixtures/vertex'
+require 'fixtures/edge'
+class CascadedEagerLoadingTest < Test::Unit::TestCase
+ fixtures :edges, :vertices
+
+ def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
+ source = Vertex.find(:first, :include=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id')
+ assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
+ end
+
+ def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
+ sink = Vertex.find(:first, :include=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC')
+ assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
+ end
+end
diff --git a/vendor/rails/activerecord/test/associations/eager_test.rb b/vendor/rails/activerecord/test/associations/eager_test.rb
new file mode 100644
index 00000000..0d69af1c
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/eager_test.rb
@@ -0,0 +1,393 @@
+require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/author'
+require 'fixtures/category'
+require 'fixtures/company'
+require 'fixtures/person'
+require 'fixtures/reader'
+
+class EagerAssociationTest < Test::Unit::TestCase
+ fixtures :posts, :comments, :authors, :categories, :categories_posts,
+ :companies, :accounts, :tags, :people, :readers
+
+ def test_loading_with_one_association
+ posts = Post.find(:all, :include => :comments)
+ post = posts.find { |p| p.id == 1 }
+ assert_equal 2, post.comments.size
+ assert post.comments.include?(comments(:greetings))
+
+ post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
+ assert_equal 2, post.comments.size
+ assert post.comments.include?(comments(:greetings))
+ 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'")
+ assert_nil posts.detect { |p| p.author_id != authors(:david).id },
+ "expected to find only david's posts"
+ end
+
+ def test_with_ordering
+ list = Post.find(:all, :include => :comments, :order => "posts.id DESC")
+ [:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments,
+ :authorless, :thinking, :welcome
+ ].each_with_index do |post, index|
+ assert_equal posts(post), list[index]
+ end
+ end
+
+ def test_loading_with_multiple_associations
+ posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
+ assert_equal 2, posts.first.comments.size
+ assert_equal 2, posts.first.categories.size
+ assert posts.first.comments.include?(comments(:greetings))
+ end
+
+ def test_loading_from_an_association
+ posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
+ assert_equal 2, posts.first.comments.size
+ end
+
+ def test_loading_with_no_associations
+ assert_nil Post.find(posts(:authorless).id, :include => :author).author
+ end
+
+ def test_eager_association_loading_with_belongs_to
+ comments = Comment.find(:all, :include => :post)
+ assert_equal 10, comments.length
+ titles = comments.map { |c| c.post.title }
+ assert titles.include?(posts(:welcome).title)
+ assert titles.include?(posts(:sti_post_and_comments).title)
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit
+ comments = Comment.find(:all, :include => :post, :limit => 5, :order => 'comments.id')
+ assert_equal 5, comments.length
+ assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
+ comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :order => 'comments.id')
+ assert_equal 3, comments.length
+ assert_equal [5,6,7], comments.collect { |c| c.id }
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit_and_offset
+ comments = Comment.find(:all, :include => :post, :limit => 3, :offset => 2, :order => 'comments.id')
+ assert_equal 3, comments.length
+ assert_equal [3,5,6], comments.collect { |c| c.id }
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
+ comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id')
+ assert_equal 3, comments.length
+ assert_equal [6,7,8], comments.collect { |c| c.id }
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
+ comments = Comment.find(:all, :include => :post, :conditions => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id')
+ assert_equal 3, comments.length
+ assert_equal [6,7,8], comments.collect { |c| c.id }
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
+ posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :order => 'posts.id')
+ assert_equal 1, posts.length
+ assert_equal [1], posts.collect { |p| p.id }
+ end
+
+ def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
+ posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id')
+ assert_equal 1, posts.length
+ assert_equal [2], posts.collect { |p| p.id }
+ end
+
+ def test_eager_with_has_many_through
+ posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
+ posts_with_author = people(:michael).posts.find(:all, :include => :author )
+ posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
+ assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
+ assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
+ assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
+ end
+
+ def test_eager_with_has_many_through_an_sti_join_model
+ author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
+ assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
+ end
+
+ def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
+ author = Author.find(:first, :include => :special_nonexistant_post_comments, :order => 'authors.id')
+ assert_equal [], author.special_nonexistant_post_comments
+ end
+
+ def test_eager_with_has_many_through_join_model_with_conditions
+ assert_equal Author.find(:first, :include => :hello_post_comments,
+ :order => 'authors.id').hello_post_comments.sort_by(&:id),
+ Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id)
+ end
+
+ def test_eager_with_has_many_and_limit
+ posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
+ assert_equal 2, posts.size
+ assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
+ end
+
+ def test_eager_with_has_many_and_limit_and_conditions
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
+ assert_equal 2, posts.size
+ assert_equal [4,5], posts.collect { |p| p.id }
+ end
+
+ def test_eager_with_has_many_and_limit_and_conditions_array
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
+ assert_equal 2, posts.size
+ assert_equal [4,5], posts.collect { |p| p.id }
+ 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' ])
+ assert_equal 2, posts.size
+
+ count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ assert_equal count, posts.size
+ end
+
+ def test_eager_with_has_many_and_limit_ond_high_offset
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
+ assert_equal 0, posts.size
+ end
+
+ def test_count_eager_with_has_many_and_limit_ond_high_offset
+ posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
+ assert_equal 0, posts
+ end
+
+ def test_eager_with_has_many_and_limit_with_no_results
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
+ assert_equal 0, posts.size
+ end
+
+ def test_eager_with_has_and_belongs_to_many_and_limit
+ posts = Post.find(:all, :include => :categories, :order => "posts.id", :limit => 3)
+ assert_equal 3, posts.size
+ assert_equal 2, posts[0].categories.size
+ assert_equal 1, posts[1].categories.size
+ assert_equal 0, posts[2].categories.size
+ assert posts[0].categories.include?(categories(:technology))
+ assert posts[1].categories.include?(categories(:general))
+ end
+
+ def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers
+ posts = authors(:david).posts.find(:all,
+ :include => :comments,
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :limit => 2
+ )
+ assert_equal 2, posts.size
+
+ count = Post.count(
+ :include => [ :comments, :author ],
+ :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :limit => 2
+ )
+ assert_equal count, posts.size
+ end
+
+ def test_eager_with_has_many_and_limit_and_scoped_conditions_on_the_eagers
+ posts = nil
+ Post.with_scope(:find => {
+ :include => :comments,
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
+ }) do
+ posts = authors(:david).posts.find(:all, :limit => 2)
+ assert_equal 2, posts.size
+ end
+
+ Post.with_scope(:find => {
+ :include => [ :comments, :author ],
+ :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
+ }) do
+ count = Post.count(:limit => 2)
+ assert_equal count, posts.size
+ end
+ end
+
+ def test_eager_with_has_many_and_limit_and_scoped_and_explicit_conditions_on_the_eagers
+ Post.with_scope(:find => { :conditions => "1=1" }) do
+ posts = authors(:david).posts.find(:all,
+ :include => :comments,
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :limit => 2
+ )
+ assert_equal 2, posts.size
+
+ count = Post.count(
+ :include => [ :comments, :author ],
+ :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :limit => 2
+ )
+ assert_equal count, posts.size
+ end
+ end
+ def test_eager_association_loading_with_habtm
+ posts = Post.find(:all, :include => :categories, :order => "posts.id")
+ assert_equal 2, posts[0].categories.size
+ assert_equal 1, posts[1].categories.size
+ assert_equal 0, posts[2].categories.size
+ assert posts[0].categories.include?(categories(:technology))
+ assert posts[1].categories.include?(categories(:general))
+ end
+
+ def test_eager_with_inheritance
+ posts = SpecialPost.find(:all, :include => [ :comments ])
+ end
+
+ def test_eager_has_one_with_association_inheritance
+ post = Post.find(4, :include => [ :very_special_comment ])
+ assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
+ end
+
+ def test_eager_has_many_with_association_inheritance
+ post = Post.find(4, :include => [ :special_comments ])
+ post.special_comments.each do |special_comment|
+ assert_equal "SpecialComment", special_comment.class.to_s
+ end
+ end
+
+ def test_eager_habtm_with_association_inheritance
+ post = Post.find(6, :include => [ :special_categories ])
+ assert_equal 1, post.special_categories.size
+ post.special_categories.each do |special_category|
+ assert_equal "SpecialCategory", special_category.class.to_s
+ end
+ end
+
+ def test_eager_with_has_one_dependent_does_not_destroy_dependent
+ assert_not_nil companies(:first_firm).account
+ f = Firm.find(:first, :include => :account,
+ :conditions => ["companies.name = ?", "37signals"])
+ assert_not_nil f.account
+ assert_equal companies(:first_firm, :reload).account, f.account
+ end
+
+ def test_eager_with_invalid_association_reference
+ assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
+ post = Post.find(6, :include=> :monkeys )
+ }
+ assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
+ post = Post.find(6, :include=>[ :monkeys ])
+ }
+ assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
+ post = Post.find(6, :include=>[ 'monkeys' ])
+ }
+ assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
+ post = Post.find(6, :include=>[ :monkeys, :elephants ])
+ }
+ end
+
+ def find_all_ordered(className, include=nil)
+ className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
+ end
+
+ def test_limited_eager_with_order
+ assert_equal [posts(:thinking), posts(: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), posts(: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), posts(: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), posts(: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_eager_with_multiple_associations_with_same_table_has_many_and_habtm
+ # Eager includes of has many and habtm associations aren't necessarily sorted in the same way
+ def assert_equal_after_sort(item1, item2, item3 = nil)
+ assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
+ assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
+ end
+ # Test regular association, association with conditions, association with
+ # STI, and association with conditions assured not to be true
+ post_types = [:posts, :other_posts, :special_posts]
+ # test both has_many and has_and_belongs_to_many
+ [Author, Category].each do |className|
+ d1 = find_all_ordered(className)
+ # test including all post types at once
+ d2 = find_all_ordered(className, post_types)
+ d1.each_index do |i|
+ assert_equal(d1[i], d2[i])
+ assert_equal_after_sort(d1[i].posts, d2[i].posts)
+ post_types[1..-1].each do |post_type|
+ # test including post_types together
+ d3 = find_all_ordered(className, [:posts, post_type])
+ assert_equal(d1[i], d3[i])
+ assert_equal_after_sort(d1[i].posts, d3[i].posts)
+ assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type))
+ end
+ end
+ end
+ end
+
+ def test_eager_with_multiple_associations_with_same_table_has_one
+ d1 = find_all_ordered(Firm)
+ d2 = find_all_ordered(Firm, :account)
+ d1.each_index do |i|
+ assert_equal(d1[i], d2[i])
+ assert_equal(d1[i].account, d2[i].account)
+ end
+ end
+
+ def test_eager_with_multiple_associations_with_same_table_belongs_to
+ firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition]
+ d1 = find_all_ordered(Client)
+ d2 = find_all_ordered(Client, firm_types)
+ d1.each_index do |i|
+ assert_equal(d1[i], d2[i])
+ firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
+ end
+ end
+ def test_eager_with_valid_association_as_string_not_symbol
+ assert_nothing_raised { Post.find(:all, :include => 'comments') }
+ end
+
+ def test_preconfigured_includes_with_belongs_to
+ author = posts(:welcome).author_with_posts
+ assert_equal 5, author.posts.size
+ end
+
+ def test_preconfigured_includes_with_has_one
+ comment = posts(:sti_comments).very_special_comment_with_post
+ assert_equal posts(:sti_comments), comment.post
+ end
+
+ def test_preconfigured_includes_with_has_many
+ posts = authors(:david).posts_with_comments
+ one = posts.detect { |p| p.id == 1 }
+ assert_equal 5, posts.size
+ assert_equal 2, one.comments.size
+ end
+
+ def test_preconfigured_includes_with_habtm
+ posts = authors(:david).posts_with_categories
+ one = posts.detect { |p| p.id == 1 }
+ assert_equal 5, posts.size
+ assert_equal 2, one.categories.size
+ end
+
+ def test_preconfigured_includes_with_has_many_and_habtm
+ posts = authors(:david).posts_with_comments_and_categories
+ one = posts.detect { |p| p.id == 1 }
+ assert_equal 5, posts.size
+ assert_equal 2, one.comments.size
+ assert_equal 2, one.categories.size
+ end
+
+ def test_count_with_include
+ if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
+ else
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/test/associations/extension_test.rb b/vendor/rails/activerecord/test/associations/extension_test.rb
new file mode 100644
index 00000000..e80a2b9f
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/extension_test.rb
@@ -0,0 +1,42 @@
+require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/project'
+require 'fixtures/developer'
+
+class AssociationsExtensionsTest < Test::Unit::TestCase
+ fixtures :projects, :developers, :developers_projects, :comments, :posts
+
+ def test_extension_on_has_many
+ assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
+ end
+
+ def test_extension_on_habtm
+ assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
+ end
+
+ def test_named_extension_on_habtm
+ assert_equal projects(:action_controller), developers(:david).projects_extended_by_name.find_most_recent
+ end
+
+ def test_named_two_extensions_on_habtm
+ assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_twice.find_most_recent
+ assert_equal projects(:active_record), developers(:david).projects_extended_by_name_twice.find_least_recent
+ end
+
+ def test_marshalling_extensions
+ david = developers(:david)
+ assert_equal projects(:action_controller), david.projects.find_most_recent
+
+ david = Marshal.load(Marshal.dump(david))
+ assert_equal projects(:action_controller), david.projects.find_most_recent
+ end
+
+ def test_marshalling_named_extensions
+ david = developers(:david)
+ assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
+
+ david = Marshal.load(Marshal.dump(david))
+ assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/associations/join_model_test.rb b/vendor/rails/activerecord/test/associations/join_model_test.rb
new file mode 100644
index 00000000..1124a0ac
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/join_model_test.rb
@@ -0,0 +1,480 @@
+require 'abstract_unit'
+require 'fixtures/tag'
+require 'fixtures/tagging'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/author'
+require 'fixtures/category'
+require 'fixtures/categorization'
+
+class AssociationsJoinModelTest < Test::Unit::TestCase
+ self.use_transactional_fixtures = false
+ fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites
+
+ def test_has_many
+ assert authors(:david).categories.include?(categories(:general))
+ end
+
+ def test_has_many_inherited
+ assert authors(:mary).categories.include?(categories(:sti_test))
+ end
+
+ def test_inherited_has_many
+ assert categories(:sti_test).authors.include?(authors(:mary))
+ end
+
+ def test_has_many_uniq_through_join_model
+ assert_equal 2, authors(:mary).categorized_posts.size
+ assert_equal 1, authors(:mary).unique_categorized_posts.size
+ end
+
+ def test_polymorphic_has_many
+ assert posts(:welcome).taggings.include?(taggings(:welcome_general))
+ end
+
+ def test_polymorphic_has_one
+ assert_equal taggings(:welcome_general), posts(:welcome).tagging
+ end
+
+ def test_polymorphic_belongs_to
+ assert_equal posts(:welcome), posts(:welcome).taggings.first.taggable
+ end
+
+ def test_polymorphic_has_many_going_through_join_model
+ assert_equal tags(:general), tag = posts(:welcome).tags.first
+ assert_no_queries do
+ tag.tagging
+ end
+ end
+
+ def test_count_polymorphic_has_many
+ assert_equal 1, posts(:welcome).taggings.count
+ assert_equal 1, posts(:welcome).tags.count
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_find
+ assert_equal tags(:general), tag = posts(:welcome).tags.find(:first)
+ assert_no_queries do
+ tag.tagging
+ end
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection
+ assert_equal tags(:general), tag = posts(:welcome).funky_tags.first
+ assert_no_queries do
+ tag.tagging
+ end
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find
+ assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first)
+ assert_no_queries do
+ tag.tagging
+ end
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_disabled_include
+ assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
+ assert_queries 1 do
+ tag.tagging
+ end
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
+ assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
+ tag.author_id
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
+ assert_equal tags(:misc), taggings(:welcome_general).super_tag
+ assert_equal tags(:misc), posts(:welcome).super_tags.first
+ end
+
+ def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
+ post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
+ assert_instance_of SubStiPost, post
+
+ tagging = tags(:misc).taggings.create(:taggable => post)
+ assert_equal "SubStiPost", tagging.taggable_type
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_inheritance
+ assert_equal tags(:general), posts(:thinking).tags.first
+ end
+
+ def test_polymorphic_has_many_going_through_join_model_with_inheritance_with_custom_class_name
+ assert_equal tags(:general), posts(:thinking).funky_tags.first
+ end
+
+ def test_polymorphic_has_many_create_model_with_inheritance
+ post = posts(:thinking)
+ assert_instance_of SpecialPost, post
+
+ tagging = tags(:misc).taggings.create(:taggable => post)
+ assert_equal "Post", tagging.taggable_type
+ end
+
+ def test_polymorphic_has_one_create_model_with_inheritance
+ tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
+ assert_equal "Post", tagging.taggable_type
+ end
+
+ def test_set_polymorphic_has_many
+ tagging = tags(:misc).taggings.create
+ posts(:thinking).taggings << tagging
+ assert_equal "Post", tagging.taggable_type
+ end
+
+ def test_set_polymorphic_has_one
+ tagging = tags(:misc).taggings.create
+ posts(:thinking).tagging = tagging
+ assert_equal "Post", tagging.taggable_type
+ end
+
+ def test_create_polymorphic_has_many_with_scope
+ old_count = posts(:welcome).taggings.count
+ tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
+ assert_equal "Post", tagging.taggable_type
+ assert_equal old_count+1, posts(:welcome).taggings.count
+ end
+
+ def test_create_bang_polymorphic_with_has_many_scope
+ old_count = posts(:welcome).taggings.count
+ tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
+ assert_equal "Post", tagging.taggable_type
+ assert_equal old_count+1, posts(:welcome).taggings.count
+ end
+
+ def test_create_polymorphic_has_one_with_scope
+ old_count = Tagging.count
+ tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
+ assert_equal "Post", tagging.taggable_type
+ assert_equal old_count+1, Tagging.count
+ end
+
+ def test_delete_polymorphic_has_many_with_delete_all
+ assert_equal 1, posts(:welcome).taggings.count
+ posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
+ post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
+
+ old_count = Tagging.count
+ post.destroy
+ assert_equal old_count-1, Tagging.count
+ assert_equal 0, posts(:welcome).taggings.count
+ end
+
+ def test_delete_polymorphic_has_many_with_destroy
+ assert_equal 1, posts(:welcome).taggings.count
+ posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
+ post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
+
+ old_count = Tagging.count
+ post.destroy
+ assert_equal old_count-1, Tagging.count
+ assert_equal 0, posts(:welcome).taggings.count
+ end
+
+ def test_delete_polymorphic_has_many_with_nullify
+ assert_equal 1, posts(:welcome).taggings.count
+ posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
+ post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
+
+ old_count = Tagging.count
+ post.destroy
+ assert_equal old_count, Tagging.count
+ assert_equal 0, posts(:welcome).taggings.count
+ end
+
+ def test_delete_polymorphic_has_one_with_destroy
+ assert posts(:welcome).tagging
+ posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
+ post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
+
+ old_count = Tagging.count
+ post.destroy
+ assert_equal old_count-1, Tagging.count
+ assert_nil posts(:welcome).tagging(true)
+ end
+
+ def test_delete_polymorphic_has_one_with_nullify
+ assert posts(:welcome).tagging
+ posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
+ post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
+
+ old_count = Tagging.count
+ post.destroy
+ assert_equal old_count, Tagging.count
+ assert_nil posts(:welcome).tagging(true)
+ end
+
+ def test_has_many_with_piggyback
+ assert_equal "2", categories(:sti_test).authors.first.post_id.to_s
+ end
+
+ def test_include_has_many_through
+ posts = Post.find(:all, :order => 'posts.id')
+ posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
+ assert_equal posts.length, posts_with_authors.length
+ posts.length.times do |i|
+ assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
+ end
+ end
+
+ def test_include_polymorphic_has_one
+ post = Post.find_by_id(posts(:welcome).id, :include => :tagging)
+ tagging = taggings(:welcome_general)
+ assert_no_queries do
+ assert_equal tagging, post.tagging
+ end
+ end
+
+ def test_include_polymorphic_has_many_through
+ posts = Post.find(:all, :order => 'posts.id')
+ posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
+ assert_equal posts.length, posts_with_tags.length
+ posts.length.times do |i|
+ assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
+ end
+ end
+
+ def test_include_polymorphic_has_many
+ posts = Post.find(:all, :order => 'posts.id')
+ posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
+ assert_equal posts.length, posts_with_taggings.length
+ posts.length.times do |i|
+ assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
+ end
+ end
+
+ def test_has_many_find_all
+ assert_equal [categories(:general)], authors(:david).categories.find(:all)
+ end
+
+ def test_has_many_find_first
+ assert_equal categories(:general), authors(:david).categories.find(:first)
+ end
+
+ def test_has_many_with_hash_conditions
+ assert_equal categories(:general), authors(:david).categories_like_general.find(:first)
+ end
+
+ def test_has_many_find_conditions
+ assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
+ assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
+ end
+
+ def test_has_many_class_methods_called_by_method_missing
+ assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
+# assert_equal nil, authors(:david).categories.find_by_name('Technology')
+ end
+
+ def test_has_many_going_through_join_model_with_custom_foreign_key
+ assert_equal [], posts(:thinking).authors
+ assert_equal [authors(:mary)], posts(:authorless).authors
+ end
+
+ def test_belongs_to_polymorphic_with_counter_cache
+ assert_equal 0, posts(:welcome)[:taggings_count]
+ tagging = posts(:welcome).taggings.create(:tag => tags(:general))
+ assert_equal 1, posts(:welcome, :reload)[:taggings_count]
+ tagging.destroy
+ assert posts(:welcome, :reload)[:taggings_count].zero?
+ end
+
+ def test_unavailable_through_reflection
+ assert_raises (ActiveRecord::HasManyThroughAssociationNotFoundError) { authors(:david).nothings }
+ end
+
+ def test_has_many_through_join_model_with_conditions
+ assert_equal [], posts(:welcome).invalid_taggings
+ assert_equal [], posts(:welcome).invalid_tags
+ end
+
+ def test_has_many_polymorphic
+ assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do
+ assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables
+ end
+ assert_raises ActiveRecord::EagerLoadPolymorphicError do
+ assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable)
+ end
+ end
+
+ def test_has_many_through_has_many_find_all
+ assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first
+ end
+
+ def test_has_many_through_has_many_find_all_with_custom_class
+ assert_equal comments(:greetings), authors(:david).funky_comments.find(:all, :order => 'comments.id').first
+ end
+
+ def test_has_many_through_has_many_find_first
+ assert_equal comments(:greetings), authors(:david).comments.find(:first, :order => 'comments.id')
+ end
+
+ def test_has_many_through_has_many_find_conditions
+ options = { :conditions => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
+ assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, options)
+ end
+
+ def test_has_many_through_has_many_find_by_id
+ assert_equal comments(:more_greetings), authors(:david).comments.find(2)
+ end
+
+ def test_has_many_through_polymorphic_has_one
+ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tagging }
+ end
+
+ def test_has_many_through_polymorphic_has_many
+ assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id }
+ end
+
+ def test_include_has_many_through_polymorphic_has_many
+ author = Author.find_by_id(authors(:david).id, :include => :taggings)
+ expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)]
+ assert_no_queries do
+ assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
+ end
+ end
+
+ def test_has_many_through_has_many_through
+ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags }
+ end
+
+ def test_has_many_through_habtm
+ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories }
+ end
+
+ def test_eager_load_has_many_through_has_many
+ author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
+ SpecialComment.new; VerySpecialComment.new
+ assert_no_queries do
+ assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
+ end
+ end
+
+ def test_eager_load_has_many_through_has_many_with_conditions
+ post = Post.find(:first, :include => :invalid_tags)
+ assert_no_queries do
+ post.invalid_tags
+ end
+ end
+
+ def test_eager_belongs_to_and_has_one_not_singularized
+ assert_nothing_raised do
+ Author.find(:first, :include => :author_address)
+ AuthorAddress.find(:first, :include => :author)
+ end
+ end
+
+ def test_self_referential_has_many_through
+ assert_equal [authors(:mary)], authors(:david).favorite_authors
+ assert_equal [], authors(:mary).favorite_authors
+ end
+
+ def test_add_to_self_referential_has_many_through
+ new_author = Author.create(:name => "Bob")
+ authors(:david).author_favorites.create :favorite_author => new_author
+ assert_equal new_author, authors(:david).reload.favorite_authors.first
+ end
+
+ def test_has_many_through_uses_correct_attributes
+ assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
+ end
+
+ def test_raise_error_when_adding_new_record_to_has_many_through
+ assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags << tags(:general).clone }
+ assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).clone.tags << tags(:general) }
+ assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.build }
+ end
+
+ def test_create_associate_when_adding_to_has_many_through
+ count = posts(:thinking).tags.count
+ push = Tag.create!(:name => 'pushme')
+ post_thinking = posts(:thinking)
+ assert_nothing_raised { post_thinking.tags << push }
+ assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ assert_equal(count + 1, post_thinking.tags.size)
+ assert_equal(count + 1, post_thinking.tags(true).size)
+
+ assert_nothing_raised { post_thinking.tags.create!(:name => 'foo') }
+ assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ assert_equal(count + 2, post_thinking.tags.size)
+ assert_equal(count + 2, post_thinking.tags(true).size)
+
+ assert_nothing_raised { post_thinking.tags.concat(Tag.create!(:name => 'abc'), Tag.create!(:name => 'def')) }
+ assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ assert_equal(count + 4, post_thinking.tags.size)
+ assert_equal(count + 4, post_thinking.tags(true).size)
+ end
+
+ def test_adding_junk_to_has_many_through_should_raise_type_mismatch
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" }
+ end
+
+ def test_adding_to_has_many_through_should_return_self
+ tags = posts(:thinking).tags
+ assert_equal tags, posts(:thinking).tags.push(tags(:general))
+ end
+
+ def test_delete_associate_when_deleting_from_has_many_through
+ count = posts(:thinking).tags.count
+ tags_before = posts(:thinking).tags
+ tag = Tag.create!(:name => 'doomed')
+ post_thinking = posts(:thinking)
+ post_thinking.tags << tag
+ assert_equal(count + 1, post_thinking.tags(true).size)
+
+ assert_nothing_raised { post_thinking.tags.delete(tag) }
+ assert_equal(count, post_thinking.tags.size)
+ assert_equal(count, post_thinking.tags(true).size)
+ assert_equal(tags_before.sort, post_thinking.tags.sort)
+ end
+
+ def test_delete_associate_when_deleting_from_has_many_through_with_multiple_tags
+ count = posts(:thinking).tags.count
+ tags_before = posts(:thinking).tags
+ doomed = Tag.create!(:name => 'doomed')
+ doomed2 = Tag.create!(:name => 'doomed2')
+ quaked = Tag.create!(:name => 'quaked')
+ post_thinking = posts(:thinking)
+ post_thinking.tags << doomed << doomed2
+ assert_equal(count + 2, post_thinking.tags(true).size)
+
+ assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
+ assert_equal(count, post_thinking.tags.size)
+ assert_equal(count, post_thinking.tags(true).size)
+ assert_equal(tags_before.sort, post_thinking.tags.sort)
+ end
+
+ def test_deleting_junk_from_has_many_through_should_raise_type_mismatch
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete("Uhh what now?") }
+ end
+
+ def test_has_many_through_sum_uses_calculations
+ assert_nothing_raised { authors(:david).comments.sum(:post_id) }
+ end
+
+ def test_has_many_through_has_many_with_sti
+ assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
+ end
+
+ private
+ # create dynamic Post models to allow different dependency options
+ def find_post_with_dependency(post_id, association, association_name, dependency)
+ class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
+ Post.find(post_id).update_attribute :type, class_name
+ klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
+ klass.set_table_name 'posts'
+ klass.send(association, association_name, :as => :taggable, :dependent => dependency)
+ klass.find(post_id)
+ end
+end
diff --git a/vendor/rails/activerecord/test/attribute_methods_test.rb b/vendor/rails/activerecord/test/attribute_methods_test.rb
new file mode 100755
index 00000000..0fa13c65
--- /dev/null
+++ b/vendor/rails/activerecord/test/attribute_methods_test.rb
@@ -0,0 +1,49 @@
+require 'abstract_unit'
+
+class AttributeMethodsTest < Test::Unit::TestCase
+ def setup
+ @old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
+ @target = Class.new(ActiveRecord::Base)
+ @target.table_name = 'topics'
+ end
+
+ def teardown
+ ActiveRecord::Base.send(:attribute_method_suffixes).clear
+ ActiveRecord::Base.attribute_method_suffix *@old_suffixes
+ end
+
+
+ def test_match_attribute_method_query_returns_match_data
+ assert_not_nil md = @target.match_attribute_method?('title=')
+ assert_equal 'title', md.pre_match
+ assert_equal ['='], md.captures
+
+ %w(_hello_world ist! _maybe?).each do |suffix|
+ @target.class_eval "def attribute#{suffix}(*args) args end"
+ @target.attribute_method_suffix suffix
+
+ assert_not_nil md = @target.match_attribute_method?("title#{suffix}")
+ assert_equal 'title', md.pre_match
+ assert_equal [suffix], md.captures
+ end
+ end
+
+ def test_declared_attribute_method_affects_respond_to_and_method_missing
+ topic = @target.new(:title => 'Budget')
+ assert topic.respond_to?('title')
+ assert_equal 'Budget', topic.title
+ assert !topic.respond_to?('title_hello_world')
+ assert_raise(NoMethodError) { topic.title_hello_world }
+
+ %w(_hello_world _it! _candidate= able?).each do |suffix|
+ @target.class_eval "def attribute#{suffix}(*args) args end"
+ @target.attribute_method_suffix suffix
+
+ meth = "title#{suffix}"
+ assert topic.respond_to?(meth)
+ assert_equal ['title'], topic.send(meth)
+ assert_equal ['title', 'a'], topic.send(meth, 'a')
+ assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/test/connection_test_firebird.rb b/vendor/rails/activerecord/test/connection_test_firebird.rb
new file mode 100644
index 00000000..4760a46d
--- /dev/null
+++ b/vendor/rails/activerecord/test/connection_test_firebird.rb
@@ -0,0 +1,8 @@
+require 'abstract_unit'
+
+class ConnectionTest < Test::Unit::TestCase
+ def test_charset_properly_set
+ fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection)
+ assert_equal 'UTF8', fb_conn.database.character_set
+ end
+end
diff --git a/vendor/rails/activerecord/test/connections/native_frontbase/connection.rb b/vendor/rails/activerecord/test/connections/native_frontbase/connection.rb
new file mode 100644
index 00000000..11dd8d27
--- /dev/null
+++ b/vendor/rails/activerecord/test/connections/native_frontbase/connection.rb
@@ -0,0 +1,27 @@
+puts 'Using native Frontbase'
+require_dependency 'fixtures/course'
+require 'logger'
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+ 'arunit' => {
+ :adapter => 'frontbase',
+ :host => 'localhost',
+ :username => 'rails',
+ :password => '',
+ :database => 'activerecord_unittest',
+ :session_name => "unittest-#{$$}"
+ },
+ 'arunit2' => {
+ :adapter => 'frontbase',
+ :host => 'localhost',
+ :username => 'rails',
+ :password => '',
+ :database => 'activerecord_unittest2',
+ :session_name => "unittest-#{$$}"
+ }
+}
+
+ActiveRecord::Base.establish_connection 'arunit'
+Course.establish_connection 'arunit2'
diff --git a/vendor/rails/activerecord/test/datatype_test_postgresql.rb b/vendor/rails/activerecord/test/datatype_test_postgresql.rb
new file mode 100644
index 00000000..c4c3318d
--- /dev/null
+++ b/vendor/rails/activerecord/test/datatype_test_postgresql.rb
@@ -0,0 +1,52 @@
+require 'abstract_unit'
+
+class PostgresqlDatatype < ActiveRecord::Base
+end
+
+class PGDataTypeTest < Test::Unit::TestCase
+ self.use_transactional_fixtures = false
+
+ TABLE_NAME = 'postgresql_datatypes'
+ COLUMNS = [
+ 'id SERIAL PRIMARY KEY',
+ 'commission_by_quarter INTEGER[]',
+ 'nicknames TEXT[]'
+ ]
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
+ @connection.execute "INSERT INTO #{TABLE_NAME} (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )"
+ @first = PostgresqlDatatype.find( 1 )
+ end
+
+ def teardown
+ @connection.execute "DROP TABLE #{TABLE_NAME}"
+ end
+
+ def test_data_type_of_array_types
+ assert_equal :string, @first.column_for_attribute("commission_by_quarter").type
+ assert_equal :string, @first.column_for_attribute("nicknames").type
+ end
+
+ def test_array_values
+ assert_equal '{35000,21000,18000,17000}', @first.commission_by_quarter
+ assert_equal '{foo,bar,baz}', @first.nicknames
+ end
+
+ def test_update_integer_array
+ new_value = '{32800,95000,29350,17000}'
+ assert @first.commission_by_quarter = new_value
+ assert @first.save
+ assert @first.reload
+ assert_equal @first.commission_by_quarter, new_value
+ end
+
+ def test_update_text_array
+ new_value = '{robby,robert,rob,robbie}'
+ assert @first.nicknames = new_value
+ assert @first.save
+ assert @first.reload
+ assert_equal @first.nicknames, new_value
+ end
+end
diff --git a/vendor/rails/activerecord/test/empty_date_time_test.rb b/vendor/rails/activerecord/test/empty_date_time_test.rb
new file mode 100644
index 00000000..0b50b094
--- /dev/null
+++ b/vendor/rails/activerecord/test/empty_date_time_test.rb
@@ -0,0 +1,25 @@
+require 'abstract_unit'
+require 'fixtures/topic'
+require 'fixtures/task'
+
+class EmptyDateTimeTest < Test::Unit::TestCase
+ def test_assign_empty_date_time
+ task = Task.new
+ task.starting = ''
+ task.ending = nil
+ assert_nil task.starting
+ assert_nil task.ending
+ end
+
+ def test_assign_empty_date
+ topic = Topic.new
+ topic.last_read = ''
+ assert_nil topic.last_read
+ end
+
+ def test_assign_empty_time
+ topic = Topic.new
+ topic.bonus_time = ''
+ assert_nil topic.bonus_time
+ end
+end
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
new file mode 100644
index 00000000..5fc64f76
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
@@ -0,0 +1,31 @@
+DROP TABLE accounts CASCADE;
+DROP TABLE funny_jokes CASCADE;
+DROP TABLE companies CASCADE;
+DROP TABLE topics CASCADE;
+DROP TABLE developers CASCADE;
+DROP TABLE projects CASCADE;
+DROP TABLE developers_projects CASCADE;
+DROP TABLE orders CASCADE;
+DROP TABLE customers CASCADE;
+DROP TABLE movies CASCADE;
+DROP TABLE subscribers CASCADE;
+DROP TABLE booleantests CASCADE;
+DROP TABLE auto_id_tests CASCADE;
+DROP TABLE entrants CASCADE;
+DROP TABLE colnametests CASCADE;
+DROP TABLE mixins CASCADE;
+DROP TABLE people CASCADE;
+DROP TABLE readers CASCADE;
+DROP TABLE binaries CASCADE;
+DROP TABLE computers CASCADE;
+DROP TABLE posts CASCADE;
+DROP TABLE comments CASCADE;
+DROP TABLE authors CASCADE;
+DROP TABLE tasks CASCADE;
+DROP TABLE categories CASCADE;
+DROP TABLE categories_posts CASCADE;
+DROP TABLE fk_test_has_fk CASCADE;
+DROP TABLE fk_test_has_pk CASCADE;
+DROP TABLE keyboards CASCADE;
+DROP TABLE legacy_things CASCADE;
+DROP TABLE numeric_data CASCADE;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
new file mode 100644
index 00000000..e102f65a
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
@@ -0,0 +1,262 @@
+CREATE TABLE accounts (
+ id integer DEFAULT unique,
+ firm_id integer,
+ credit_limit integer,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR accounts(id);
+
+CREATE TABLE funny_jokes (
+ id integer DEFAULT unique,
+ firm_id integer default NULL,
+ name character varying(50),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR funny_jokes(id);
+
+CREATE TABLE companies (
+ id integer DEFAULT unique,
+ "type" character varying(50),
+ "ruby_type" character varying(50),
+ firm_id integer,
+ name character varying(50),
+ client_of integer,
+ rating integer default 1,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR companies(id);
+
+CREATE TABLE topics (
+ id integer DEFAULT unique,
+ title character varying(255),
+ author_name character varying(255),
+ author_email_address character varying(255),
+ written_on timestamp,
+ bonus_time time,
+ last_read date,
+ content varchar(65536),
+ approved boolean default true,
+ replies_count integer default 0,
+ parent_id integer,
+ "type" character varying(50),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR topics(id);
+
+CREATE TABLE developers (
+ id integer DEFAULT unique,
+ name character varying(100),
+ salary integer DEFAULT 70000,
+ created_at timestamp,
+ updated_at timestamp,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR developers(id);
+
+CREATE TABLE projects (
+ id integer DEFAULT unique,
+ name character varying(100),
+ type varchar(255),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR projects(id);
+
+CREATE TABLE developers_projects (
+ developer_id integer NOT NULL,
+ project_id integer NOT NULL,
+ joined_on date,
+ access_level integer default 1
+);
+
+CREATE TABLE orders (
+ id integer DEFAULT unique,
+ name character varying(100),
+ billing_customer_id integer,
+ shipping_customer_id integer,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR orders(id);
+
+CREATE TABLE customers (
+ id integer DEFAULT unique,
+ name character varying(100),
+ balance integer default 0,
+ address_street character varying(100),
+ address_city character varying(100),
+ address_country character varying(100),
+ gps_location character varying(100),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR customers(id);
+
+CREATE TABLE movies (
+ movieid integer DEFAULT unique,
+ name varchar(65536),
+ PRIMARY KEY (movieid)
+);
+SET UNIQUE FOR movies(movieid);
+
+CREATE TABLE subscribers (
+ nick varchar(65536) NOT NULL,
+ name varchar(65536),
+ PRIMARY KEY (nick)
+);
+
+CREATE TABLE booleantests (
+ id integer DEFAULT unique,
+ value boolean,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR booleantests(id);
+
+CREATE TABLE auto_id_tests (
+ auto_id integer DEFAULT unique,
+ value integer,
+ PRIMARY KEY (auto_id)
+);
+SET UNIQUE FOR auto_id_tests(auto_id);
+
+CREATE TABLE entrants (
+ id integer DEFAULT unique,
+ name varchar(65536),
+ course_id integer,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR entrants(id);
+
+CREATE TABLE colnametests (
+ id integer DEFAULT unique,
+ "references" integer NOT NULL,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR colnametests(id);
+
+CREATE TABLE mixins (
+ id integer DEFAULT unique,
+ parent_id integer,
+ type character varying(100),
+ pos integer,
+ lft integer,
+ rgt integer,
+ root_id integer,
+ created_at timestamp,
+ updated_at timestamp,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR mixins(id);
+
+CREATE TABLE people (
+ id integer DEFAULT unique,
+ first_name varchar(65536),
+ lock_version integer default 0,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR people(id);
+
+CREATE TABLE readers (
+ id integer DEFAULT unique,
+ post_id INTEGER NOT NULL,
+ person_id INTEGER NOT NULL,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR readers(id);
+
+CREATE TABLE binaries (
+ id integer DEFAULT unique,
+ data BLOB,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR binaries(id);
+
+CREATE TABLE computers (
+ id integer DEFAULT unique,
+ developer integer NOT NULL,
+ "extendedWarranty" integer NOT NULL,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR computers(id);
+
+CREATE TABLE posts (
+ id integer DEFAULT unique,
+ author_id integer,
+ title varchar(255),
+ type varchar(255),
+ body varchar(65536),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR posts(id);
+
+CREATE TABLE comments (
+ id integer DEFAULT unique,
+ post_id integer,
+ type varchar(255),
+ body varchar(65536),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR comments(id);
+
+CREATE TABLE authors (
+ id integer DEFAULT unique,
+ name varchar(255) default NULL,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR authors(id);
+
+CREATE TABLE tasks (
+ id integer DEFAULT unique,
+ starting timestamp,
+ ending timestamp,
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR tasks(id);
+
+CREATE TABLE categories (
+ id integer DEFAULT unique,
+ name varchar(255),
+ type varchar(255),
+ PRIMARY KEY (id)
+);
+SET UNIQUE FOR categories(id);
+
+CREATE TABLE categories_posts (
+ category_id integer NOT NULL,
+ post_id integer NOT NULL
+);
+
+CREATE TABLE fk_test_has_pk (
+ id INTEGER NOT NULL PRIMARY KEY
+);
+SET UNIQUE FOR fk_test_has_pk(id);
+
+CREATE TABLE fk_test_has_fk (
+ id INTEGER NOT NULL PRIMARY KEY,
+ fk_id INTEGER NOT NULL REFERENCES fk_test_has_fk(id)
+);
+SET UNIQUE FOR fk_test_has_fk(id);
+
+CREATE TABLE keyboards (
+ key_number integer DEFAULT unique,
+ "name" character varying(50),
+ PRIMARY KEY (key_number)
+);
+SET UNIQUE FOR keyboards(key_number);
+
+create table "legacy_things"
+(
+ "id" int,
+ "tps_report_number" int default NULL,
+ "version" int default 0 not null,
+ primary key ("id")
+);
+SET UNIQUE FOR legacy_things(id);
+
+CREATE TABLE "numeric_data" (
+ "id" integer NOT NULL
+ "bank_balance" DECIMAL(10,2),
+ "big_bank_balance" DECIMAL(15,2),
+ "world_population" DECIMAL(10),
+ "my_house_population" DECIMAL(2),
+ "decimal_number_with_default" DECIMAL(3,2) DEFAULT 2.78,
+ primary key ("id")
+);
+SET UNIQUE FOR numeric_data(id);
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.drop.sql
new file mode 100644
index 00000000..17b9ad46
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.drop.sql
@@ -0,0 +1 @@
+DROP TABLE courses CASCADE;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.sql
new file mode 100644
index 00000000..42f4bb78
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.sql
@@ -0,0 +1,4 @@
+CREATE TABLE courses (
+ id integer DEFAULT unique,
+ name varchar(100)
+);
diff --git a/vendor/rails/activerecord/test/fixtures/edge.rb b/vendor/rails/activerecord/test/fixtures/edge.rb
new file mode 100644
index 00000000..55e0c31f
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/edge.rb
@@ -0,0 +1,5 @@
+# This class models an edge in a directed graph.
+class Edge < ActiveRecord::Base
+ belongs_to :source, :class_name => 'Vertex', :foreign_key => 'source_id'
+ belongs_to :sink, :class_name => 'Vertex', :foreign_key => 'sink_id'
+end
diff --git a/vendor/rails/activerecord/test/fixtures/edges.yml b/vendor/rails/activerecord/test/fixtures/edges.yml
new file mode 100644
index 00000000..c16c70dd
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/edges.yml
@@ -0,0 +1,6 @@
+<% (1..4).each do |id| %>
+edge_<%= id %>:
+ id: <%= id %>
+ source_id: <%= id %>
+ sink_id: <%= id + 1 %>
+<% end %>
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb b/vendor/rails/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb
new file mode 100644
index 00000000..0aed7cbd
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb
@@ -0,0 +1,15 @@
+class GiveMeBigNumbers < ActiveRecord::Migration
+ def self.up
+ create_table :big_numbers do |table|
+ table.column :bank_balance, :decimal, :precision => 10, :scale => 2
+ table.column :big_bank_balance, :decimal, :precision => 15, :scale => 2
+ table.column :world_population, :decimal, :precision => 10
+ table.column :my_house_population, :decimal, :precision => 2
+ table.column :value_of_e, :decimal
+ end
+ end
+
+ def self.down
+ drop_table :big_numbers
+ end
+end
diff --git a/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb
new file mode 100644
index 00000000..753ee341
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb
@@ -0,0 +1,9 @@
+class PeopleHaveMiddleNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "middle_name", :string
+ end
+
+ def self.down
+ remove_column "people", "middle_name"
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb
new file mode 100644
index 00000000..009729b3
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "last_name", :string
+ end
+
+ def self.down
+ remove_column "people", "last_name"
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb
new file mode 100644
index 00000000..ac5918f0
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb
@@ -0,0 +1,12 @@
+class WeNeedReminders < ActiveRecord::Migration
+ def self.up
+ create_table("reminders") do |t|
+ t.column :content, :text
+ t.column :remind_at, :datetime
+ end
+ end
+
+ def self.down
+ drop_table "reminders"
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb
new file mode 100644
index 00000000..21c9ca53
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb
@@ -0,0 +1,12 @@
+class InnocentJointable < 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
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/vertex.rb b/vendor/rails/activerecord/test/fixtures/vertex.rb
new file mode 100644
index 00000000..f4c11144
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/vertex.rb
@@ -0,0 +1,9 @@
+# This class models a vertex in a directed graph.
+class Vertex < ActiveRecord::Base
+ has_many :sink_edges, :class_name => 'Edge', :foreign_key => 'source_id'
+ has_many :sinks, :through => :sink_edges, :source => :sink
+
+ has_and_belongs_to_many :sources,
+ :class_name => 'Vertex', :join_table => 'edges',
+ :foreign_key => 'sink_id', :association_foreign_key => 'source_id'
+end
diff --git a/vendor/rails/activerecord/test/fixtures/vertices.yml b/vendor/rails/activerecord/test/fixtures/vertices.yml
new file mode 100644
index 00000000..8af0593f
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/vertices.yml
@@ -0,0 +1,4 @@
+<% (1..5).each do |id| %>
+vertex_<%= id %>:
+ id: <%= id %>
+<% end %>
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/migration_test_firebird.rb b/vendor/rails/activerecord/test/migration_test_firebird.rb
new file mode 100644
index 00000000..92d531b7
--- /dev/null
+++ b/vendor/rails/activerecord/test/migration_test_firebird.rb
@@ -0,0 +1,124 @@
+require 'abstract_unit'
+require 'fixtures/course'
+
+class FirebirdMigrationTest < Test::Unit::TestCase
+ self.use_transactional_fixtures = false
+
+ def setup
+ # using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain
+ @connection = Course.connection
+ @fireruby_connection = @connection.instance_variable_get(:@connection)
+ end
+
+ def teardown
+ @connection.drop_table :foo rescue nil
+ @connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil
+ end
+
+ def test_create_table_with_custom_sequence_name
+ assert_nothing_raised do
+ @connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f|
+ f.column :bar, :string
+ end
+ end
+ assert !sequence_exists?('foo_seq')
+ assert sequence_exists?('foo_custom_seq')
+
+ assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') }
+ assert !sequence_exists?('foo_custom_seq')
+ ensure
+ FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil
+ end
+
+ def test_create_table_without_sequence
+ assert_nothing_raised do
+ @connection.create_table(:foo, :sequence => false) do |f|
+ f.column :bar, :string
+ end
+ end
+ assert !sequence_exists?('foo_seq')
+ assert_nothing_raised { @connection.drop_table :foo }
+
+ assert_nothing_raised do
+ @connection.create_table(:foo, :id => false) do |f|
+ f.column :bar, :string
+ end
+ end
+ assert !sequence_exists?('foo_seq')
+ assert_nothing_raised { @connection.drop_table :foo }
+ end
+
+ def test_create_table_with_boolean_column
+ assert !boolean_domain_exists?
+ assert_nothing_raised do
+ @connection.create_table :foo do |f|
+ f.column :bar, :string
+ f.column :baz, :boolean
+ end
+ end
+ assert boolean_domain_exists?
+ end
+
+ def test_add_boolean_column
+ assert !boolean_domain_exists?
+ @connection.create_table :foo do |f|
+ f.column :bar, :string
+ end
+
+ assert_nothing_raised { @connection.add_column :foo, :baz, :boolean }
+ assert boolean_domain_exists?
+ assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type
+ end
+
+ def test_change_column_to_boolean
+ assert !boolean_domain_exists?
+ # Manually create table with a SMALLINT column, which can be changed to a BOOLEAN
+ @connection.execute "CREATE TABLE foo (bar SMALLINT)"
+ assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type
+
+ assert_nothing_raised { @connection.change_column :foo, :bar, :boolean }
+ assert boolean_domain_exists?
+ assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type
+ end
+
+ def test_rename_table_with_data_and_index
+ @connection.create_table :foo do |f|
+ f.column :baz, :string, :limit => 50
+ end
+ 100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" }
+ @connection.add_index :foo, :baz
+
+ assert_nothing_raised { @connection.rename_table :foo, :bar }
+ assert !@connection.tables.include?("foo")
+ assert @connection.tables.include?("bar")
+ assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name
+ assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last
+ assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"]
+ ensure
+ @connection.drop_table :bar rescue nil
+ end
+
+ def test_renaming_table_with_fk_constraint_raises_error
+ @connection.create_table :parent do |p|
+ p.column :name, :string
+ end
+ @connection.create_table :child do |c|
+ c.column :parent_id, :integer
+ end
+ @connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)"
+ assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant }
+ ensure
+ @connection.drop_table :child rescue nil
+ @connection.drop_table :descendant rescue nil
+ @connection.drop_table :parent rescue nil
+ end
+
+ private
+ def boolean_domain_exists?
+ !@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil?
+ end
+
+ def sequence_exists?(sequence_name)
+ FireRuby::Generator.exists?(sequence_name, @fireruby_connection)
+ end
+end
diff --git a/vendor/rails/activerecord/test/schema_authorization_test_postgresql.rb b/vendor/rails/activerecord/test/schema_authorization_test_postgresql.rb
new file mode 100644
index 00000000..f1328765
--- /dev/null
+++ b/vendor/rails/activerecord/test/schema_authorization_test_postgresql.rb
@@ -0,0 +1,75 @@
+require 'abstract_unit'
+
+class SchemaThing < ActiveRecord::Base
+end
+
+class SchemaAuthorizationTest < Test::Unit::TestCase
+ self.use_transactional_fixtures = false
+
+ TABLE_NAME = 'schema_things'
+ COLUMNS = [
+ 'id serial primary key',
+ 'name character varying(50)'
+ ]
+ USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2']
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.execute "SET search_path TO '$user',public"
+ set_session_auth
+ USERS.each do |u|
+ @connection.execute "CREATE ROLE #{u}"
+ @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}"
+ set_session_auth u
+ @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
+ @connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')"
+ set_session_auth
+ end
+ end
+
+ def teardown
+ set_session_auth
+ @connection.execute "RESET search_path"
+ USERS.each do |u|
+ @connection.execute "DROP SCHEMA #{u} CASCADE"
+ @connection.execute "DROP ROLE #{u}"
+ end
+ end
+
+ def test_schema_invisible
+ assert_raise(ActiveRecord::StatementInvalid) do
+ set_session_auth
+ @connection.execute "SELECT * FROM #{TABLE_NAME}"
+ end
+ end
+
+ def test_schema_uniqueness
+ assert_nothing_raised do
+ set_session_auth
+ USERS.each do |u|
+ set_session_auth u
+ assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1")
+ set_session_auth
+ end
+ end
+ end
+
+ def test_sequence_schema_caching
+ assert_nothing_raised do
+ USERS.each do |u|
+ set_session_auth u
+ st = SchemaThing.new :name => 'TEST1'
+ st.save!
+ st = SchemaThing.new :id => 5, :name => 'TEST2'
+ st.save!
+ set_session_auth
+ end
+ end
+ end
+
+ private
+ def set_session_auth auth = nil
+ @connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}"
+ end
+
+end
diff --git a/vendor/rails/activerecord/test/table_name_test_sqlserver.rb b/vendor/rails/activerecord/test/table_name_test_sqlserver.rb
new file mode 100644
index 00000000..c2d17115
--- /dev/null
+++ b/vendor/rails/activerecord/test/table_name_test_sqlserver.rb
@@ -0,0 +1,23 @@
+require 'abstract_unit'
+require "#{File.dirname(__FILE__)}/../lib/active_record/schema"
+
+if ActiveRecord::Base.connection.supports_migrations?
+ class Order < ActiveRecord::Base
+ self.table_name = '[order]'
+ end
+
+ class TableNameTest < Test::Unit::TestCase
+ self.use_transactional_fixtures = false
+
+ # Ensures Model.columns works when using SQLServer escape characters.
+ # Enables legacy schemas using SQL reserved words as table names.
+ # Should work with table names with spaces as well ('table name').
+ def test_escaped_table_name
+ assert_nothing_raised do
+ ActiveRecord::Base.connection.select_all 'SELECT * FROM [order]'
+ end
+ assert_equal '[order]', Order.table_name
+ assert_equal 5, Order.columns.length
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/test/xml_serialization_test.rb b/vendor/rails/activerecord/test/xml_serialization_test.rb
new file mode 100644
index 00000000..73ff354c
--- /dev/null
+++ b/vendor/rails/activerecord/test/xml_serialization_test.rb
@@ -0,0 +1,125 @@
+require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/author'
+
+class Contact < ActiveRecord::Base
+ # mock out self.columns so no pesky db is needed for these tests
+ def self.columns() @columns ||= []; end
+ def self.column(name, sql_type = nil, default = nil, null = true)
+ columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
+ end
+
+ column :name, :string
+ column :age, :integer
+ column :avatar, :binary
+ column :created_at, :datetime
+ column :awesome, :boolean
+end
+
+class XmlSerializationTest < Test::Unit::TestCase
+ def test_should_serialize_default_root
+ @xml = Contact.new.to_xml
+ assert_match %r{^}, @xml
+ assert_match %r{$}, @xml
+ end
+
+ def test_should_serialize_default_root_with_namespace
+ @xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
+ assert_match %r{^}, @xml
+ assert_match %r{$}, @xml
+ end
+
+ def test_should_serialize_custom_root
+ @xml = Contact.new.to_xml :root => 'xml_contact'
+ assert_match %r{^}, @xml
+ assert_match %r{$}, @xml
+ end
+
+ def test_should_allow_undasherized_tags
+ @xml = Contact.new.to_xml :root => 'xml_contact', :dasherize => false
+ assert_match %r{^}, @xml
+ assert_match %r{$}, @xml
+ assert_match %r{ [:age, :name]
+ assert_match %r{ [:age, :name]
+ assert_no_match %r{ 'aaron stack', :age => 25, :avatar => 'binarydata', :created_at => Time.utc(2006, 8, 1), :awesome => false).to_xml
+ end
+
+ def test_should_serialize_string
+ assert_match %r{aaron stack}, @xml
+ end
+
+ def test_should_serialize_integer
+ assert_match %r{25}, @xml
+ end
+
+ def test_should_serialize_binary
+ assert_match %r{YmluYXJ5ZGF0YQ==\n}, @xml
+ assert_match %r{2006-08-01T00:00:00Z}, @xml
+ end
+
+ def test_should_serialize_boolean
+ assert_match %r{false}, @xml
+ end
+end
+
+class NilXmlSerializationTest < Test::Unit::TestCase
+ def setup
+ @xml = Contact.new.to_xml(:root => 'xml_contact')
+ end
+
+ def test_should_serialize_string
+ assert_match %r{}, @xml
+ end
+
+ def test_should_serialize_integer
+ assert_match %r{}, @xml
+ end
+
+ def test_should_serialize_binary
+ assert_match %r{>}, @xml
+ assert_match %r{}, @xml
+ end
+
+ def test_should_serialize_boolean
+ assert_match %r{}, @xml
+ end
+end
+
+class DatabaseConnectedXmlSerializationTest < Test::Unit::TestCase
+ fixtures :authors, :posts
+ # to_xml used to mess with the hash the user provided which
+ # caused the builder to be reused
+ def test_passing_hash_shouldnt_reuse_builder
+ options = {:include=>:posts}
+ david = authors(:david)
+ first_xml_size = david.to_xml(options).size
+ second_xml_size = david.to_xml(options).size
+ assert_equal first_xml_size, second_xml_size
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/MIT-LICENSE b/vendor/rails/activesupport/MIT-LICENSE
new file mode 100644
index 00000000..c1caf0bf
--- /dev/null
+++ b/vendor/rails/activesupport/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2005-2006 David Heinemeier Hansson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
new file mode 100644
index 00000000..fae23d4a
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -0,0 +1,55 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Array #:nodoc:
+ module Grouping
+ # Iterate over an array in groups of a certain size, padding any remaining
+ # slots with specified value (nil by default) unless it is
+ # false.
+ #
+ # E.g.
+ #
+ # %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
+ # ["1", "2", "3"]
+ # ["4", "5", "6"]
+ # ["7", nil, nil]
+ #
+ # %w(1 2 3).in_groups_of(2, ' ') {|g| p g}
+ # ["1", "2"]
+ # ["3", " "]
+ #
+ # %w(1 2 3).in_groups_of(2, false) {|g| p g}
+ # ["1", "2"]
+ # ["3"]
+ def in_groups_of(number, fill_with = nil, &block)
+ require 'enumerator'
+ collection = dup
+ collection << fill_with until collection.size.modulo(number).zero? unless fill_with == false
+ grouped_collection = [] unless block_given?
+ collection.each_slice(number) do |group|
+ block_given? ? yield(group) : grouped_collection << group
+ end
+ grouped_collection unless block_given?
+ end
+
+ # Divide the array into one or more subarrays based on a delimiting +value+
+ # or the result of an optional block.
+ #
+ # ex.
+ #
+ # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
+ # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
+ def split(value = nil, &block)
+ block ||= Proc.new { |e| e == value }
+ inject([[]]) do |results, element|
+ if block.call(element)
+ results << []
+ else
+ results.last << element
+ end
+ results
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
new file mode 100644
index 00000000..436b3e00
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
@@ -0,0 +1,3 @@
+require 'bigdecimal'
+
+require File.dirname(__FILE__) + '/bigdecimal/formatting.rb'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
new file mode 100644
index 00000000..d86adbea
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
@@ -0,0 +1,7 @@
+class BigDecimal #:nodoc:
+
+ alias :_original_to_s :to_s
+ def to_s(format="F")
+ _original_to_s(format)
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
new file mode 100644
index 00000000..81e81696
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -0,0 +1,58 @@
+class Module
+ # Encapsulates the common pattern of:
+ #
+ # alias_method :foo_without_feature, :foo
+ # alias_method :foo, :foo_with_feature
+ #
+ # With this, you simply do:
+ #
+ # alias_method_chain :foo, :feature
+ #
+ # And both aliases are set up for you.
+ #
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
+ #
+ # alias_method_chain :foo?, :feature
+ #
+ # is equivalent to
+ #
+ # alias_method :foo_without_feature?, :foo?
+ # alias_method :foo?, :foo_with_feature?
+ #
+ # so you can safely chain foo, foo?, and foo! with the same feature.
+ def alias_method_chain(target, feature)
+ # Strip out punctuation on predicates or bang methods since
+ # e.g. target?_without_feature is not a valid method name.
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
+ yield(aliased_target, punctuation) if block_given?
+ alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
+ alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
+ end
+
+ # Allows you to make aliases for attributes, which includes
+ # getter, setter, and query methods.
+ #
+ # 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"
+ def alias_attribute(new_name, old_name)
+ module_eval <<-STR, __FILE__, __LINE__+1
+ def #{new_name}; #{old_name}; end
+ def #{new_name}?; #{old_name}?; end
+ def #{new_name}=(v); self.#{old_name} = v; end
+ STR
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module/attr_internal.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module/attr_internal.rb
new file mode 100644
index 00000000..7f2fb964
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module/attr_internal.rb
@@ -0,0 +1,31 @@
+class Module
+ # Declare an attribute reader backed by an internally-named instance variable.
+ def attr_internal_reader(*attrs)
+ attrs.each do |attr|
+ module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end"
+ end
+ end
+
+ # Declare an attribute writer backed by an internally-named instance variable.
+ def attr_internal_writer(*attrs)
+ attrs.each do |attr|
+ module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end"
+ end
+ end
+
+ # Declare attributes backed by 'internal' instance variables names.
+ def attr_internal_accessor(*attrs)
+ attr_internal_reader(*attrs)
+ attr_internal_writer(*attrs)
+ end
+
+ alias_method :attr_internal, :attr_internal_accessor
+
+ private
+ mattr_accessor :attr_internal_naming_format
+ self.attr_internal_naming_format = '@_%s'
+
+ def attr_internal_ivar_name(attr)
+ attr_internal_naming_format % attr
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb b/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
new file mode 100644
index 00000000..4af8f228
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
@@ -0,0 +1,17 @@
+# Add a +missing_name+ method to NameError instances.
+class NameError < StandardError #:nodoc:
+ # Add a method to obtain the missing name from a NameError.
+ def missing_name
+ $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
+ end
+
+ # Was this exception raised because the given name was missing?
+ def missing_name?(name)
+ if name.is_a? Symbol
+ last_name = (missing_name || '').split('::').last
+ last_name == name.to_s
+ else
+ missing_name == name.to_s
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb b/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
new file mode 100644
index 00000000..0e4c76fb
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
@@ -0,0 +1,42 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module String #:nodoc:
+ # Define methods for handeling unicode data.
+ module Unicode
+ # +chars+ is a Unicode safe proxy for string methods. 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. Undefined methods are forwarded to String, so all of the
+ # string overrides can also be called through the +chars+ proxy.
+ #
+ # name = 'Claus Müller'
+ # name.reverse #=> "rell??M sualC"
+ # name.length #=> 13
+ #
+ # name.chars.reverse.to_s #=> "rellüM sualC"
+ # name.chars.length #=> 12
+ #
+ #
+ # 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.chars.reverse.length #=> 12
+ #
+ # The Char 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 and
+ # ActiveSupport::Multibyte::Handlers::UTF8Handler
+ def chars
+ ActiveSupport::Multibyte::Chars.new(self)
+ 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::Handlers::UTF8Handler.consumes?(self)
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/deprecation.rb b/vendor/rails/activesupport/lib/active_support/deprecation.rb
new file mode 100644
index 00000000..d2c61274
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/deprecation.rb
@@ -0,0 +1,201 @@
+require 'yaml'
+
+module ActiveSupport
+ module Deprecation #:nodoc:
+ mattr_accessor :debug
+ self.debug = false
+
+ # Choose the default warn behavior according to RAILS_ENV.
+ # Ignore deprecation warnings in production.
+ DEFAULT_BEHAVIORS = {
+ 'test' => Proc.new { |message, callstack|
+ $stderr.puts(message)
+ $stderr.puts callstack.join("\n ") if debug
+ },
+ 'development' => Proc.new { |message, callstack|
+ RAILS_DEFAULT_LOGGER.warn message
+ RAILS_DEFAULT_LOGGER.debug callstack.join("\n ") if debug
+ }
+ }
+
+ class << self
+ def warn(message = nil, callstack = caller)
+ behavior.call(deprecation_message(callstack, message), callstack) if behavior && !silenced?
+ end
+
+ def default_behavior
+ if defined?(RAILS_ENV)
+ DEFAULT_BEHAVIORS[RAILS_ENV.to_s]
+ else
+ DEFAULT_BEHAVIORS['test']
+ end
+ end
+
+ # Have deprecations been silenced?
+ def silenced?
+ @silenced = false unless defined?(@silenced)
+ @silenced
+ end
+
+ # Silence deprecation warnings within the block.
+ def silence
+ old_silenced, @silenced = @silenced, true
+ yield
+ ensure
+ @silenced = old_silenced
+ end
+
+ attr_writer :silenced
+
+
+ private
+ def deprecation_message(callstack, message = nil)
+ message ||= "You are using deprecated behavior which will be removed from Rails 2.0."
+ "DEPRECATION WARNING: #{message} See http://www.rubyonrails.org/deprecation for details. #{deprecation_caller_message(callstack)}"
+ end
+
+ def deprecation_caller_message(callstack)
+ file, line, method = extract_callstack(callstack)
+ if file
+ if line && method
+ "(called from #{method} at #{file}:#{line})"
+ else
+ "(called from #{file}:#{line})"
+ end
+ end
+ end
+
+ def extract_callstack(callstack)
+ if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
+ md.captures
+ else
+ callstack.first
+ end
+ end
+ end
+
+ # Behavior is a block that takes a message argument.
+ mattr_accessor :behavior
+ self.behavior = default_behavior
+
+ # Warnings are not silenced by default.
+ self.silenced = false
+
+ module ClassMethods #:nodoc:
+ # Declare that a method has been deprecated.
+ def deprecate(*method_names)
+ options = method_names.last.is_a?(Hash) ? method_names.pop : {}
+ method_names = method_names + options.keys
+ method_names.each do |method_name|
+ alias_method_chain(method_name, :deprecation) do |target, punctuation|
+ class_eval(<<-EOS, __FILE__, __LINE__)
+ def #{target}_with_deprecation#{punctuation}(*args, &block)
+ ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:#{method_name}, #{options[method_name].inspect}), caller)
+ #{target}_without_deprecation#{punctuation}(*args, &block)
+ end
+ EOS
+ end
+ end
+ end
+
+ def deprecated_method_warning(method_name, message=nil)
+ warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}"
+ case message
+ when Symbol then "#{warning} (use #{message} instead)"
+ when String then "#{warning} (#{message})"
+ else warning
+ end
+ end
+
+ def deprecation_horizon
+ '2.0'
+ end
+ end
+
+ module Assertions #:nodoc:
+ def assert_deprecated(match = nil, &block)
+ result, warnings = collect_deprecations(&block)
+ assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
+ if match
+ match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
+ assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
+ end
+ result
+ end
+
+ def assert_not_deprecated(&block)
+ result, deprecations = collect_deprecations(&block)
+ assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
+ result
+ end
+
+ private
+ def collect_deprecations
+ old_behavior = ActiveSupport::Deprecation.behavior
+ deprecations = []
+ ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
+ deprecations << message
+ end
+ result = yield
+ [result, deprecations]
+ ensure
+ ActiveSupport::Deprecation.behavior = old_behavior
+ end
+ end
+
+ # Stand-in for @request, @attributes, @params, etc which emits deprecation
+ # warnings on any method call (except #inspect).
+ class DeprecatedInstanceVariableProxy #:nodoc:
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
+
+ def initialize(instance, method, var = "@#{method}")
+ @instance, @method, @var = instance, method, var
+ end
+
+ # Don't give a deprecation warning on inspect since test/unit and error
+ # logs rely on it for diagnostics.
+ def inspect
+ target.inspect
+ end
+
+ private
+ def method_missing(called, *args, &block)
+ warn caller, called, args
+ target.__send__(called, *args, &block)
+ end
+
+ def target
+ @instance.__send__(@method)
+ end
+
+ def warn(callstack, called, args)
+ ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
+ end
+ end
+ end
+end
+
+class Module
+ include ActiveSupport::Deprecation::ClassMethods
+end
+
+require 'test/unit/error'
+
+module Test
+ module Unit
+ class TestCase
+ include ActiveSupport::Deprecation::Assertions
+ end
+
+ class Error # :nodoc:
+ # Silence warnings when reporting test errors.
+ def message_with_silenced_deprecation
+ ActiveSupport::Deprecation.silence do
+ message_without_silenced_deprecation
+ end
+ end
+
+ alias_method_chain :message, :silenced_deprecation
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte.rb b/vendor/rails/activesupport/lib/active_support/multibyte.rb
new file mode 100644
index 00000000..9e17d064
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/multibyte.rb
@@ -0,0 +1,7 @@
+module ActiveSupport::Multibyte #:nodoc:
+ DEFAULT_NORMALIZATION_FORM = :kc
+ NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
+ UNICODE_VERSION = '5.0.0'
+end
+
+require 'active_support/multibyte/chars'
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb b/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
new file mode 100644
index 00000000..374adc78
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
@@ -0,0 +1,129 @@
+require 'active_support/multibyte/handlers/utf8_handler'
+require 'active_support/multibyte/handlers/passthru_handler'
+
+# Encapsulates all the functionality related to the Chars proxy.
+module ActiveSupport::Multibyte #:nodoc:
+ # Chars enables you to work transparently with multibyte encodings in the Ruby String class without having extensive
+ # knowledge about the encoding. A Chars object accepts a string upon initialization and proxies String methods in an
+ # encoding safe manner. All the normal String methods are also implemented on the proxy.
+ #
+ # String methods are proxied through the Chars object, and can be accessed through the +chars+ method. Methods
+ # which would normally return a String object now return a Chars object so methods can be chained.
+ #
+ # "The Perfect String ".chars.downcase.strip.normalize #=> "the perfect string"
+ #
+ # Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made.
+ # If certain methods do explicitly check the class, call +to_s+ before you pass chars objects to them.
+ #
+ # bad.explicit_checking_method "T".chars.downcase.to_s
+ #
+ # The actual operations on the string are delegated to handlers. Theoretically handlers can be implemented for
+ # any encoding, but the default handler handles UTF-8. This handler is set during initialization, if you want to
+ # use you own handler, you can set it on the Chars class. Look at the UTF8Handler source for an example how to
+ # implement your own handler. If you your own handler to work on anything but UTF-8 you probably also
+ # want to override Chars#handler.
+ #
+ # ActiveSupport::Multibyte::Chars.handler = MyHandler
+ #
+ # Note that a few methods are defined on Chars instead of the handler because they are defined on Object or Kernel
+ # and method_missing can't catch them.
+ class Chars
+
+ attr_reader :string # The contained string
+ alias_method :to_s, :string
+
+ include Comparable
+
+ # The magic method to make String and Chars comparable
+ def to_str
+ # Using any other ways of overriding the String itself will lead you all the way from infinite loops to
+ # core dumps. Don't go there.
+ @string
+ end
+
+ # Create a new Chars instance.
+ def initialize(str)
+ @string = (str.string rescue str)
+ end
+
+ # 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+. See String.<=>
+ # for more details.
+ def <=>(other); @string <=> other.to_s; end
+
+ # Works just like String#split, with the exception that the items in the resulting list are Chars
+ # instances instead of String. This makes chaining methods easier.
+ def split(*args)
+ @string.split(*args).map { |i| i.chars }
+ end
+
+ # Gsub works exactly the same as gsub on a normal string.
+ def gsub(*a, &b); @string.gsub(*a, &b).chars; end
+
+ # Like String.=~ only it returns the character offset (in codepoints) instead of the byte offset.
+ def =~(other)
+ handler.translate_offset(@string, @string =~ other)
+ end
+
+ # Try to forward all undefined methods to the handler, when a method is not defined on the handler, send it to
+ # the contained string. Method_missing is also responsible for making the bang! methods destructive.
+ def method_missing(m, *a, &b)
+ begin
+ # Simulate methods with a ! at the end because we can't touch the enclosed string from the handlers.
+ if m.to_s =~ /^(.*)\!$/
+ result = handler.send($1, @string, *a, &b)
+ if result == @string
+ result = nil
+ else
+ @string.replace result
+ end
+ else
+ result = handler.send(m, @string, *a, &b)
+ end
+ rescue NoMethodError
+ result = @string.send(m, *a, &b)
+ rescue Handlers::EncodingError
+ @string.replace handler.tidy_bytes(@string)
+ retry
+ end
+
+ if result.kind_of?(String)
+ result.chars
+ else
+ result
+ end
+ end
+
+ # Set the handler class for the Char objects.
+ def self.handler=(klass)
+ @@handler = klass
+ end
+
+ # Returns the proper handler for the contained string depending on $KCODE and the encoding of the string. This
+ # method is used internally to always redirect messages to the proper classes depending on the context.
+ def handler
+ if utf8_pragma?
+ @@handler
+ else
+ ActiveSupport::Multibyte::Handlers::PassthruHandler
+ end
+ end
+
+ private
+
+ # +utf8_pragma+ checks if it can send this string to the handlers. It makes sure @string isn't nil and $KCODE is
+ # set to 'UTF8'.
+ def utf8_pragma?
+ !@string.nil? && ($KCODE == 'UTF8')
+ end
+ end
+end
+
+# When we can load the utf8proc library, override normalization with the faster methods
+begin
+ require 'utf8proc_native'
+ require 'active_support/multibyte/handlers/utf8_handler_proc'
+ ActiveSupport::Multibyte::Chars.handler = ActiveSupport::Multibyte::Handlers::UTF8HandlerProc
+rescue LoadError
+ ActiveSupport::Multibyte::Chars.handler = ActiveSupport::Multibyte::Handlers::UTF8Handler
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/generators/generate_tables.rb b/vendor/rails/activesupport/lib/active_support/multibyte/generators/generate_tables.rb
new file mode 100644
index 00000000..7f807585
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/generators/generate_tables.rb
@@ -0,0 +1,149 @@
+#!/usr/bin/env ruby
+begin
+ require File.dirname(__FILE__) + '/../../../active_support'
+rescue IOError
+end
+require 'open-uri'
+require 'tmpdir'
+
+module ActiveSupport::Multibyte::Handlers #:nodoc:
+ class UnicodeDatabase #:nodoc:
+ def self.load
+ [Hash.new(Codepoint.new),[],{},{}]
+ end
+ end
+
+ class UnicodeTableGenerator #:nodoc:
+ BASE_URI = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::UNICODE_VERSION}/ucd/"
+ SOURCES = {
+ :codepoints => BASE_URI + 'UnicodeData.txt',
+ :composition_exclusion => BASE_URI + 'CompositionExclusions.txt',
+ :grapheme_break_property => BASE_URI + 'auxiliary/GraphemeBreakProperty.txt',
+ :cp1252 => 'http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT'
+ }
+
+ def initialize
+ @ucd = UnicodeDatabase.new
+
+ default = Codepoint.new
+ default.combining_class = 0
+ default.uppercase_mapping = 0
+ default.lowercase_mapping = 0
+ @ucd.codepoints = Hash.new(default)
+
+ @ucd.composition_exclusion = []
+ @ucd.composition_map = {}
+ @ucd.boundary = {}
+ @ucd.cp1252 = {}
+ end
+
+ def parse_codepoints(line)
+ codepoint = Codepoint.new
+ raise "Could not parse input." unless line =~ /^
+ ([0-9A-F]+); # code
+ ([^;]+); # name
+ ([A-Z]+); # general category
+ ([0-9]+); # canonical combining class
+ ([A-Z]+); # bidi class
+ (<([A-Z]*)>)? # decomposition type
+ ((\ ?[0-9A-F]+)*); # decompomposition mapping
+ ([0-9]*); # decimal digit
+ ([0-9]*); # digit
+ ([^;]*); # numeric
+ ([YN]*); # bidi mirrored
+ ([^;]*); # unicode 1.0 name
+ ([^;]*); # iso comment
+ ([0-9A-F]*); # simple uppercase mapping
+ ([0-9A-F]*); # simple lowercase mapping
+ ([0-9A-F]*)$/ix # simple titlecase mapping
+ codepoint.code = $1.hex
+ #codepoint.name = $2
+ #codepoint.category = $3
+ codepoint.combining_class = Integer($4)
+ #codepoint.bidi_class = $5
+ codepoint.decomp_type = $7
+ codepoint.decomp_mapping = ($8=='') ? nil : $8.split.collect { |element| element.hex }
+ #codepoint.bidi_mirrored = ($13=='Y') ? true : false
+ codepoint.uppercase_mapping = ($16=='') ? 0 : $16.hex
+ codepoint.lowercase_mapping = ($17=='') ? 0 : $17.hex
+ #codepoint.titlecase_mapping = ($18=='') ? nil : $18.hex
+ @ucd.codepoints[codepoint.code] = codepoint
+ end
+
+ def parse_grapheme_break_property(line)
+ if line =~ /^([0-9A-F\.]+)\s*;\s*([\w]+)\s*#/
+ type = $2.downcase.intern
+ @ucd.boundary[type] ||= []
+ if $1.include? '..'
+ parts = $1.split '..'
+ @ucd.boundary[type] << (parts[0].hex..parts[1].hex)
+ else
+ @ucd.boundary[type] << $1.hex
+ end
+ end
+ end
+
+ def parse_composition_exclusion(line)
+ if line =~ /^([0-9A-F]+)/i
+ @ucd.composition_exclusion << $1.hex
+ end
+ end
+
+ def parse_cp1252(line)
+ if line =~ /^([0-9A-Fx]+)\s([0-9A-Fx]+)/i
+ @ucd.cp1252[$1.hex] = $2.hex
+ end
+ end
+
+ def create_composition_map
+ @ucd.codepoints.each do |_, cp|
+ if !cp.nil? and cp.combining_class == 0 and cp.decomp_type.nil? and !cp.decomp_mapping.nil? and cp.decomp_mapping.length == 2 and @ucd[cp.decomp_mapping[0]].combining_class == 0 and !@ucd.composition_exclusion.include?(cp.code)
+ @ucd.composition_map[cp.decomp_mapping[0]] ||= {}
+ @ucd.composition_map[cp.decomp_mapping[0]][cp.decomp_mapping[1]] = cp.code
+ end
+ end
+ end
+
+ def normalize_boundary_map
+ @ucd.boundary.each do |k,v|
+ if [:lf, :cr].include? k
+ @ucd.boundary[k] = v[0]
+ end
+ end
+ end
+
+ def parse
+ SOURCES.each do |type, url|
+ filename = File.join(Dir.tmpdir, "#{url.split('/').last}")
+ unless File.exist?(filename)
+ $stderr.puts "Downloading #{url.split('/').last}"
+ File.open(filename, 'wb') do |target|
+ open(url) do |source|
+ source.each_line { |line| target.write line }
+ end
+ end
+ end
+ File.open(filename) do |file|
+ file.each_line { |line| send "parse_#{type}".intern, line }
+ end
+ end
+ create_composition_map
+ normalize_boundary_map
+ end
+
+ def dump_to(filename)
+ File.open(filename, 'wb') do |f|
+ f.write Marshal.dump([@ucd.codepoints, @ucd.composition_exclusion, @ucd.composition_map, @ucd.boundary, @ucd.cp1252])
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ filename = ActiveSupport::Multibyte::Handlers::UnicodeDatabase.filename
+ generator = ActiveSupport::Multibyte::Handlers::UnicodeTableGenerator.new
+ generator.parse
+ print "Writing to: #{filename}"
+ generator.dump_to filename
+ puts " (#{File.size(filename)} bytes)"
+end
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/handlers/passthru_handler.rb b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/passthru_handler.rb
new file mode 100644
index 00000000..916215c2
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/passthru_handler.rb
@@ -0,0 +1,9 @@
+# Chars uses this handler when $KCODE is not set to 'UTF8'. Because this handler doesn't define any methods all call
+# will be forwarded to String.
+class ActiveSupport::Multibyte::Handlers::PassthruHandler #:nodoc:
+
+ # Return the original byteoffset
+ def self.translate_offset(string, byte_offset) #:nodoc:
+ byte_offset
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
new file mode 100644
index 00000000..d79e69f0
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
@@ -0,0 +1,454 @@
+# Contains all the handlers and helper classes
+module ActiveSupport::Multibyte::Handlers #:nodoc:
+ class EncodingError < ArgumentError #:nodoc:
+ end
+
+ class Codepoint #:nodoc:
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
+ end
+
+ class UnicodeDatabase #:nodoc:
+ attr_writer :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
+
+ # self-expiring methods that lazily load the Unicode database and then return the value.
+ [:codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252].each do |attr_name|
+ class_eval(<<-EOS, __FILE__, __LINE__)
+ def #{attr_name}
+ load
+ @#{attr_name}
+ end
+ EOS
+ end
+
+ # Shortcut to ucd.codepoints[]
+ def [](index); codepoints[index]; end
+
+ # Returns the directory in which the data files are stored
+ def self.dirname
+ File.dirname(__FILE__) + '/../../values/'
+ end
+
+ # Returns the filename for the data file for this version
+ def self.filename
+ File.expand_path File.join(dirname, "unicode_tables.dat")
+ end
+
+ # Loads the unicode database and returns all the internal objects of UnicodeDatabase
+ # Once the values have been loaded, define attr_reader methods for the instance variables.
+ def load
+ begin
+ @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
+ rescue Exception => e
+ raise IOError.new("Couldn't load the unicode tables for UTF8Handler (#{e.message}), handler is unusable")
+ end
+ @codepoints ||= Hash.new(Codepoint.new)
+ @composition_exclusion ||= []
+ @composition_map ||= {}
+ @boundary ||= {}
+ @cp1252 ||= {}
+
+ # Redefine the === method so we can write shorter rules for grapheme cluster breaks
+ @boundary.each do |k,_|
+ @boundary[k].instance_eval do
+ def ===(other)
+ detect { |i| i === other } ? true : false
+ end
+ end if @boundary[k].kind_of?(Array)
+ end
+
+ # define attr_reader methods for the instance variables
+ class << self
+ attr_reader :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
+ end
+ end
+ end
+
+ # UTF8Handler implements Unicode aware operations for strings, these operations will be used by the Chars
+ # proxy when $KCODE is set to 'UTF8'.
+ class UTF8Handler
+ # Hangul character boundaries and properties
+ HANGUL_SBASE = 0xAC00
+ HANGUL_LBASE = 0x1100
+ HANGUL_VBASE = 0x1161
+ HANGUL_TBASE = 0x11A7
+ HANGUL_LCOUNT = 19
+ HANGUL_VCOUNT = 21
+ HANGUL_TCOUNT = 28
+ HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
+ HANGUL_SCOUNT = 11172
+ HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
+ HANGUL_JAMO_FIRST = 0x1100
+ HANGUL_JAMO_LAST = 0x11FF
+
+ # All the unicode whitespace
+ UNICODE_WHITESPACE = [
+ (0x0009..0x000D).to_a, # White_Space # Cc [5] ..
+ 0x0020, # White_Space # Zs SPACE
+ 0x0085, # White_Space # Cc
+ 0x00A0, # White_Space # Zs NO-BREAK SPACE
+ 0x1680, # White_Space # Zs OGHAM SPACE MARK
+ 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
+ (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
+ 0x2028, # White_Space # Zl LINE SEPARATOR
+ 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
+ 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
+ 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
+ 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
+ ].flatten.freeze
+
+ # BOM (byte order mark) can also be seen as whitespace, it's a non-rendering character used to distinguish
+ # between little and big endian. This is not an issue in utf-8, so it must be ignored.
+ UNICODE_LEADERS_AND_TRAILERS = UNICODE_WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
+
+ # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
+ UTF8_PAT = /\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
+
+ # Returns a regular expression pattern that matches the passed Unicode codepoints
+ def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
+ array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
+ end
+ UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
+ UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
+
+ class << self
+
+ # ///
+ # /// BEGIN String method overrides
+ # ///
+
+ # Inserts the passed string at specified codepoint offsets
+ def insert(str, offset, fragment)
+ str.replace(
+ u_unpack(str).insert(
+ offset,
+ u_unpack(fragment)
+ ).flatten.pack('U*')
+ )
+ end
+
+ # Returns the position of the passed argument in the string, counting in codepoints
+ def index(str, *args)
+ bidx = str.index(*args)
+ bidx ? (u_unpack(str.slice(0...bidx)).size) : nil
+ end
+
+ # Does Unicode-aware rstrip
+ def rstrip(str)
+ str.gsub(UNICODE_TRAILERS_PAT, '')
+ end
+
+ # Does Unicode-aware lstrip
+ def lstrip(str)
+ str.gsub(UNICODE_LEADERS_PAT, '')
+ end
+
+ # Removed leading and trailing whitespace
+ def strip(str)
+ str.gsub(UNICODE_LEADERS_PAT, '').gsub(UNICODE_TRAILERS_PAT, '')
+ end
+
+ # Returns the number of codepoints in the string
+ def size(str)
+ u_unpack(str).size
+ end
+ alias_method :length, :size
+
+ # Reverses codepoints in the string.
+ def reverse(str)
+ u_unpack(str).reverse.pack('U*')
+ end
+
+ # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
+ # character.
+ def slice(str, *args)
+ if (args.size == 2 && args.first.is_a?(Range))
+ raise TypeError, 'cannot convert Range into Integer' # Do as if we were native
+ elsif args[0].kind_of? Range
+ cps = u_unpack(str).slice(*args)
+ cps.nil? ? nil : cps.pack('U*')
+ elsif args.size == 1 && args[0].kind_of?(Numeric)
+ u_unpack(str)[args[0]]
+ else
+ u_unpack(str).slice(*args).pack('U*')
+ end
+ end
+ alias_method :[], :slice
+
+ # Convert characters in the string to uppercase
+ def upcase(str); to_case :uppercase_mapping, str; end
+
+ # Convert characters in the string to lowercase
+ def downcase(str); to_case :lowercase_mapping, str; end
+
+ # Returns a copy of +str+ with the first character converted to uppercase and the remainder to lowercase
+ def capitalize(str)
+ upcase(slice(str, 0..0)) + downcase(slice(str, 1..-1) || '')
+ end
+
+ # ///
+ # /// Extra String methods for unicode operations
+ # ///
+
+ # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
+ # passing strings to databases and validations.
+ #
+ # * str: The string to perform normalization on.
+ # * form: The form you want to normalize in. Should be one of the following: :c, :kc, :d or :kd.
+ def normalize(str, form=ActiveSupport::Multibyte::DEFAULT_NORMALIZATION_FORM)
+ # See http://www.unicode.org/reports/tr15, Table 1
+ codepoints = u_unpack(str)
+ case form
+ when :d
+ reorder_characters(decompose_codepoints(:canonical, codepoints))
+ when :c
+ compose_codepoints reorder_characters(decompose_codepoints(:canonical, codepoints))
+ when :kd
+ reorder_characters(decompose_codepoints(:compatability, codepoints))
+ when :kc
+ compose_codepoints reorder_characters(decompose_codepoints(:compatability, codepoints))
+ else
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
+ end.pack('U*')
+ end
+
+ # Perform decomposition on the characters in the string
+ def decompose(str)
+ decompose_codepoints(:canonical, u_unpack(str)).pack('U*')
+ end
+
+ # Perform composition on the characters in the string
+ def compose(str)
+ compose_codepoints u_unpack(str).pack('U*')
+ end
+
+ # ///
+ # /// BEGIN Helper methods for unicode operation
+ # ///
+
+ # Used to translate an offset from bytes to characters, for instance one received from a regular expression match
+ def translate_offset(str, byte_offset)
+ return 0 if str == ''
+ return nil if byte_offset.nil?
+ chunk = str[0..byte_offset]
+ begin
+ begin
+ chunk.unpack('U*').length - 1
+ rescue ArgumentError => e
+ chunk = str[0..(byte_offset+=1)]
+ # Stop retrying at the end of the string
+ raise e unless byte_offset < chunk.length
+ # We damaged a character, retry
+ retry
+ end
+ # Catch the ArgumentError so we can throw our own
+ rescue ArgumentError
+ raise EncodingError.new('malformed UTF-8 character')
+ end
+ end
+
+ # Checks if the string is valid UTF8.
+ def consumes?(str)
+ # Unpack is a little bit faster than regular expressions
+ begin
+ str.unpack('U*')
+ true
+ rescue ArgumentError
+ false
+ end
+ end
+
+ # Returns the number of grapheme clusters in the string. This method is very likely to be moved or renamed
+ # in future versions.
+ def g_length(str)
+ g_unpack(str).length
+ end
+
+ # Replaces all the non-utf-8 bytes by their iso-8859-1 or cp1252 equivalent resulting in a valid utf-8 string
+ def tidy_bytes(str)
+ str.split(//u).map do |c|
+ if !UTF8_PAT.match(c)
+ n = c.unpack('C')[0]
+ n < 128 ? n.chr :
+ n < 160 ? [UCD.cp1252[n] || n].pack('U') :
+ n < 192 ? "\xC2" + n.chr : "\xC3" + (n-64).chr
+ else
+ c
+ end
+ end.join
+ end
+
+ protected
+
+ # Detect whether the codepoint is in a certain character class. Primarily used by the
+ # grapheme cluster support.
+ def in_char_class?(codepoint, classes)
+ classes.detect { |c| UCD.boundary[c] === codepoint } ? true : false
+ end
+
+ # Unpack the string at codepoints boundaries
+ def u_unpack(str)
+ begin
+ str.unpack 'U*'
+ rescue ArgumentError
+ raise EncodingError.new('malformed UTF-8 character')
+ end
+ end
+
+ # Unpack the string at grapheme boundaries instead of codepoint boundaries
+ def g_unpack(str)
+ codepoints = u_unpack(str)
+ unpacked = []
+ pos = 0
+ marker = 0
+ eoc = codepoints.length
+ while(pos < eoc)
+ pos += 1
+ previous = codepoints[pos-1]
+ current = codepoints[pos]
+ if (
+ # CR X LF
+ one = ( previous == UCD.boundary[:cr] and current == UCD.boundary[:lf] ) or
+ # L X (L|V|LV|LVT)
+ two = ( UCD.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
+ # (LV|V) X (V|T)
+ three = ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
+ # (LVT|T) X (T)
+ four = ( in_char_class?(previous, [:lvt,:t]) and UCD.boundary[:t] === current ) or
+ # X Extend
+ five = (UCD.boundary[:extend] === current)
+ )
+ else
+ unpacked << codepoints[marker..pos-1]
+ marker = pos
+ end
+ end
+ unpacked
+ end
+
+ # Reverse operation of g_unpack
+ def g_pack(unpacked)
+ unpacked.flatten
+ end
+
+ # Convert characters to a different case
+ def to_case(way, str)
+ u_unpack(str).map do |codepoint|
+ cp = UCD[codepoint]
+ unless cp.nil?
+ ncp = cp.send(way)
+ ncp > 0 ? ncp : codepoint
+ else
+ codepoint
+ end
+ end.pack('U*')
+ end
+
+ # Re-order codepoints so the string becomes canonical
+ def reorder_characters(codepoints)
+ length = codepoints.length- 1
+ pos = 0
+ while pos < length do
+ cp1, cp2 = UCD[codepoints[pos]], UCD[codepoints[pos+1]]
+ if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
+ codepoints[pos..pos+1] = cp2.code, cp1.code
+ pos += (pos > 0 ? -1 : 1)
+ else
+ pos += 1
+ end
+ end
+ codepoints
+ end
+
+ # Decompose composed characters to the decomposed form
+ def decompose_codepoints(type, codepoints)
+ codepoints.inject([]) do |decomposed, cp|
+ # if it's a hangul syllable starter character
+ if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
+ sindex = cp - HANGUL_SBASE
+ ncp = [] # new codepoints
+ ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
+ ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
+ tindex = sindex % HANGUL_TCOUNT
+ ncp << (HANGUL_TBASE + tindex) unless tindex == 0
+ decomposed.concat ncp
+ # if the codepoint is decomposable in with the current decomposition type
+ elsif (ncp = UCD[cp].decomp_mapping) and (!UCD[cp].decomp_type || type == :compatability)
+ decomposed.concat decompose_codepoints(type, ncp.dup)
+ else
+ decomposed << cp
+ end
+ end
+ end
+
+ # Compose decomposed characters to the composed form
+ def compose_codepoints(codepoints)
+ pos = 0
+ eoa = codepoints.length - 1
+ starter_pos = 0
+ starter_char = codepoints[0]
+ previous_combining_class = -1
+ while pos < eoa
+ pos += 1
+ lindex = starter_char - HANGUL_LBASE
+ # -- Hangul
+ if 0 <= lindex and lindex < HANGUL_LCOUNT
+ vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
+ if 0 <= vindex and vindex < HANGUL_VCOUNT
+ tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
+ if 0 <= tindex and tindex < HANGUL_TCOUNT
+ j = starter_pos + 2
+ eoa -= 2
+ else
+ tindex = 0
+ j = starter_pos + 1
+ eoa -= 1
+ end
+ codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
+ end
+ starter_pos += 1
+ starter_char = codepoints[starter_pos]
+ # -- Other characters
+ else
+ current_char = codepoints[pos]
+ current = UCD[current_char]
+ if current.combining_class > previous_combining_class
+ if ref = UCD.composition_map[starter_char]
+ composition = ref[current_char]
+ else
+ composition = nil
+ end
+ unless composition.nil?
+ codepoints[starter_pos] = composition
+ starter_char = composition
+ codepoints.delete_at pos
+ eoa -= 1
+ pos -= 1
+ previous_combining_class = -1
+ else
+ previous_combining_class = current.combining_class
+ end
+ else
+ previous_combining_class = current.combining_class
+ end
+ if current.combining_class == 0
+ starter_pos = pos
+ starter_char = codepoints[pos]
+ end
+ end
+ end
+ codepoints
+ end
+
+ # UniCode Database
+ UCD = UnicodeDatabase.new
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler_proc.rb b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler_proc.rb
new file mode 100644
index 00000000..f10eecc6
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler_proc.rb
@@ -0,0 +1,43 @@
+# Methods in this handler call functions in the utf8proc ruby extension. These are significantly faster than the
+# pure ruby versions. Chars automatically uses this handler when it can load the utf8proc extension. For
+# documentation on handler methods see UTF8Handler.
+class ActiveSupport::Multibyte::Handlers::UTF8HandlerProc < ActiveSupport::Multibyte::Handlers::UTF8Handler #:nodoc:
+ class << self
+ def normalize(str, form=ActiveSupport::Multibyte::DEFAULT_NORMALIZATION_FORM) #:nodoc:
+ codepoints = str.unpack('U*')
+ case form
+ when :d
+ utf8map(str, :stable)
+ when :c
+ utf8map(str, :stable, :compose)
+ when :kd
+ utf8map(str, :stable, :compat)
+ when :kc
+ utf8map(str, :stable, :compose, :compat)
+ else
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
+ end
+ end
+
+ def decompose(str) #:nodoc:
+ utf8map(str, :stable)
+ end
+
+ def downcase(str) #:nodoc:c
+ utf8map(str, :casefold)
+ end
+
+ protected
+
+ def utf8map(str, *option_array) #:nodoc:
+ options = 0
+ option_array.each do |option|
+ flag = Utf8Proc::Options[option]
+ raise ArgumentError, "Unknown argument given to utf8map." unless
+ flag
+ options |= flag
+ end
+ return Utf8Proc::utf8map(str, options)
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/values/unicode_tables.dat b/vendor/rails/activesupport/lib/active_support/values/unicode_tables.dat
new file mode 100644
index 0000000000000000000000000000000000000000..35edb148c3de0f3815f5b847e014ba91a30449e6
GIT binary patch
literal 656156
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